279e69f7829bfdf05c19d19e77dfe45a9b3b6575
[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 {Date} date (optional) Defaults to now
1209  @param {String} interval   A valid date interval enum value (eg. Date.DAY)
1210  @return {Number} The diff in milliseconds
1211  @member Date getElapsed
1212  */
1213 Date.prototype.getElapsed = function(date, interval)
1214 {
1215     date = date ||  new Date();
1216     var ret = Math.abs(date.getTime()-this.getTime());
1217     switch (interval) {
1218        
1219         case  Date.SECOND:
1220             return Math.floor(ret / (1000));
1221         case  Date.MINUTE:
1222             return Math.floor(ret / (100*60));
1223         case  Date.HOUR:
1224             return Math.floor(ret / (100*60*60));
1225         case  Date.DAY:
1226             return Math.floor(ret / (100*60*60*24));
1227         case  Date.MONTH: // this does not give exact number...??
1228             return ((date.format("Y") - this.format("Y")) * 12) + (date.format("m") - this.format("m"));
1229         case  Date.YEAR: // this does not give exact number...??
1230             return (date.format("Y") - this.format("Y"));
1231        
1232         case  Date.MILLI:
1233         default:
1234             return ret;
1235     }
1236 };
1237  
1238 // was in date file..
1239
1240
1241 // private
1242 Date.parseFunctions = {count:0};
1243 // private
1244 Date.parseRegexes = [];
1245 // private
1246 Date.formatFunctions = {count:0};
1247
1248 // private
1249 Date.prototype.dateFormat = function(format) {
1250     if (Date.formatFunctions[format] == null) {
1251         Date.createNewFormat(format);
1252     }
1253     var func = Date.formatFunctions[format];
1254     return this[func]();
1255 };
1256
1257
1258 /**
1259  * Formats a date given the supplied format string
1260  * @param {String} format The format string
1261  * @return {String} The formatted date
1262  * @method
1263  */
1264 Date.prototype.format = Date.prototype.dateFormat;
1265
1266 // private
1267 Date.createNewFormat = function(format) {
1268     var funcName = "format" + Date.formatFunctions.count++;
1269     Date.formatFunctions[format] = funcName;
1270     var code = "Date.prototype." + funcName + " = function(){return ";
1271     var special = false;
1272     var ch = '';
1273     for (var i = 0; i < format.length; ++i) {
1274         ch = format.charAt(i);
1275         if (!special && ch == "\\") {
1276             special = true;
1277         }
1278         else if (special) {
1279             special = false;
1280             code += "'" + String.escape(ch) + "' + ";
1281         }
1282         else {
1283             code += Date.getFormatCode(ch);
1284         }
1285     }
1286     /** eval:var:zzzzzzzzzzzzz */
1287     eval(code.substring(0, code.length - 3) + ";}");
1288 };
1289
1290 // private
1291 Date.getFormatCode = function(character) {
1292     switch (character) {
1293     case "d":
1294         return "String.leftPad(this.getDate(), 2, '0') + ";
1295     case "D":
1296         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1297     case "j":
1298         return "this.getDate() + ";
1299     case "l":
1300         return "Date.dayNames[this.getDay()] + ";
1301     case "S":
1302         return "this.getSuffix() + ";
1303     case "w":
1304         return "this.getDay() + ";
1305     case "z":
1306         return "this.getDayOfYear() + ";
1307     case "W":
1308         return "this.getWeekOfYear() + ";
1309     case "F":
1310         return "Date.monthNames[this.getMonth()] + ";
1311     case "m":
1312         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1313     case "M":
1314         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1315     case "n":
1316         return "(this.getMonth() + 1) + ";
1317     case "t":
1318         return "this.getDaysInMonth() + ";
1319     case "L":
1320         return "(this.isLeapYear() ? 1 : 0) + ";
1321     case "Y":
1322         return "this.getFullYear() + ";
1323     case "y":
1324         return "('' + this.getFullYear()).substring(2, 4) + ";
1325     case "a":
1326         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1327     case "A":
1328         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1329     case "g":
1330         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1331     case "G":
1332         return "this.getHours() + ";
1333     case "h":
1334         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1335     case "H":
1336         return "String.leftPad(this.getHours(), 2, '0') + ";
1337     case "i":
1338         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1339     case "s":
1340         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1341     case "O":
1342         return "this.getGMTOffset() + ";
1343     case "P":
1344         return "this.getGMTColonOffset() + ";
1345     case "T":
1346         return "this.getTimezone() + ";
1347     case "Z":
1348         return "(this.getTimezoneOffset() * -60) + ";
1349     default:
1350         return "'" + String.escape(character) + "' + ";
1351     }
1352 };
1353
1354 /**
1355  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1356  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1357  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1358  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1359  * string or the parse operation will fail.
1360  * Example Usage:
1361 <pre><code>
1362 //dt = Fri May 25 2007 (current date)
1363 var dt = new Date();
1364
1365 //dt = Thu May 25 2006 (today's month/day in 2006)
1366 dt = Date.parseDate("2006", "Y");
1367
1368 //dt = Sun Jan 15 2006 (all date parts specified)
1369 dt = Date.parseDate("2006-1-15", "Y-m-d");
1370
1371 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1372 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1373 </code></pre>
1374  * @param {String} input The unparsed date as a string
1375  * @param {String} format The format the date is in
1376  * @return {Date} The parsed date
1377  * @static
1378  */
1379 Date.parseDate = function(input, format) {
1380     if (Date.parseFunctions[format] == null) {
1381         Date.createParser(format);
1382     }
1383     var func = Date.parseFunctions[format];
1384     return Date[func](input);
1385 };
1386 /**
1387  * @private
1388  */
1389
1390 Date.createParser = function(format) {
1391     var funcName = "parse" + Date.parseFunctions.count++;
1392     var regexNum = Date.parseRegexes.length;
1393     var currentGroup = 1;
1394     Date.parseFunctions[format] = funcName;
1395
1396     var code = "Date." + funcName + " = function(input){\n"
1397         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1398         + "var d = new Date();\n"
1399         + "y = d.getFullYear();\n"
1400         + "m = d.getMonth();\n"
1401         + "d = d.getDate();\n"
1402         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1403         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1404         + "if (results && results.length > 0) {";
1405     var regex = "";
1406
1407     var special = false;
1408     var ch = '';
1409     for (var i = 0; i < format.length; ++i) {
1410         ch = format.charAt(i);
1411         if (!special && ch == "\\") {
1412             special = true;
1413         }
1414         else if (special) {
1415             special = false;
1416             regex += String.escape(ch);
1417         }
1418         else {
1419             var obj = Date.formatCodeToRegex(ch, currentGroup);
1420             currentGroup += obj.g;
1421             regex += obj.s;
1422             if (obj.g && obj.c) {
1423                 code += obj.c;
1424             }
1425         }
1426     }
1427
1428     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1429         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1430         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1431         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1432         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1433         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1434         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1435         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1436         + "else if (y >= 0 && m >= 0)\n"
1437         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1438         + "else if (y >= 0)\n"
1439         + "{v = new Date(y); v.setFullYear(y);}\n"
1440         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1441         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1442         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1443         + ";}";
1444
1445     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1446     /** eval:var:zzzzzzzzzzzzz */
1447     eval(code);
1448 };
1449
1450 // private
1451 Date.formatCodeToRegex = function(character, currentGroup) {
1452     switch (character) {
1453     case "D":
1454         return {g:0,
1455         c:null,
1456         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1457     case "j":
1458         return {g:1,
1459             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1460             s:"(\\d{1,2})"}; // day of month without leading zeroes
1461     case "d":
1462         return {g:1,
1463             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1464             s:"(\\d{2})"}; // day of month with leading zeroes
1465     case "l":
1466         return {g:0,
1467             c:null,
1468             s:"(?:" + Date.dayNames.join("|") + ")"};
1469     case "S":
1470         return {g:0,
1471             c:null,
1472             s:"(?:st|nd|rd|th)"};
1473     case "w":
1474         return {g:0,
1475             c:null,
1476             s:"\\d"};
1477     case "z":
1478         return {g:0,
1479             c:null,
1480             s:"(?:\\d{1,3})"};
1481     case "W":
1482         return {g:0,
1483             c:null,
1484             s:"(?:\\d{2})"};
1485     case "F":
1486         return {g:1,
1487             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1488             s:"(" + Date.monthNames.join("|") + ")"};
1489     case "M":
1490         return {g:1,
1491             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1492             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1493     case "n":
1494         return {g:1,
1495             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1496             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1497     case "m":
1498         return {g:1,
1499             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1500             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1501     case "t":
1502         return {g:0,
1503             c:null,
1504             s:"\\d{1,2}"};
1505     case "L":
1506         return {g:0,
1507             c:null,
1508             s:"(?:1|0)"};
1509     case "Y":
1510         return {g:1,
1511             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1512             s:"(\\d{4})"};
1513     case "y":
1514         return {g:1,
1515             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1516                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1517             s:"(\\d{1,2})"};
1518     case "a":
1519         return {g:1,
1520             c:"if (results[" + currentGroup + "] == 'am') {\n"
1521                 + "if (h == 12) { h = 0; }\n"
1522                 + "} else { if (h < 12) { h += 12; }}",
1523             s:"(am|pm)"};
1524     case "A":
1525         return {g:1,
1526             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1527                 + "if (h == 12) { h = 0; }\n"
1528                 + "} else { if (h < 12) { h += 12; }}",
1529             s:"(AM|PM)"};
1530     case "g":
1531     case "G":
1532         return {g:1,
1533             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1534             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1535     case "h":
1536     case "H":
1537         return {g:1,
1538             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1539             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1540     case "i":
1541         return {g:1,
1542             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1543             s:"(\\d{2})"};
1544     case "s":
1545         return {g:1,
1546             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1547             s:"(\\d{2})"};
1548     case "O":
1549         return {g:1,
1550             c:[
1551                 "o = results[", currentGroup, "];\n",
1552                 "var sn = o.substring(0,1);\n", // get + / - sign
1553                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1554                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1555                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1556                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1557             ].join(""),
1558             s:"([+\-]\\d{2,4})"};
1559     
1560     
1561     case "P":
1562         return {g:1,
1563                 c:[
1564                    "o = results[", currentGroup, "];\n",
1565                    "var sn = o.substring(0,1);\n",
1566                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1567                    "var mn = o.substring(4,6) % 60;\n",
1568                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1569                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1570             ].join(""),
1571             s:"([+\-]\\d{4})"};
1572     case "T":
1573         return {g:0,
1574             c:null,
1575             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1576     case "Z":
1577         return {g:1,
1578             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1579                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1580             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1581     default:
1582         return {g:0,
1583             c:null,
1584             s:String.escape(character)};
1585     }
1586 };
1587
1588 /**
1589  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1590  * @return {String} The abbreviated timezone name (e.g. 'CST')
1591  */
1592 Date.prototype.getTimezone = function() {
1593     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1594 };
1595
1596 /**
1597  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1598  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1599  */
1600 Date.prototype.getGMTOffset = function() {
1601     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1602         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1603         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1604 };
1605
1606 /**
1607  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1608  * @return {String} 2-characters representing hours and 2-characters representing minutes
1609  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1610  */
1611 Date.prototype.getGMTColonOffset = function() {
1612         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1613                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1614                 + ":"
1615                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1616 }
1617
1618 /**
1619  * Get the numeric day number of the year, adjusted for leap year.
1620  * @return {Number} 0 through 364 (365 in leap years)
1621  */
1622 Date.prototype.getDayOfYear = function() {
1623     var num = 0;
1624     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1625     for (var i = 0; i < this.getMonth(); ++i) {
1626         num += Date.daysInMonth[i];
1627     }
1628     return num + this.getDate() - 1;
1629 };
1630
1631 /**
1632  * Get the string representation of the numeric week number of the year
1633  * (equivalent to the format specifier 'W').
1634  * @return {String} '00' through '52'
1635  */
1636 Date.prototype.getWeekOfYear = function() {
1637     // Skip to Thursday of this week
1638     var now = this.getDayOfYear() + (4 - this.getDay());
1639     // Find the first Thursday of the year
1640     var jan1 = new Date(this.getFullYear(), 0, 1);
1641     var then = (7 - jan1.getDay() + 4);
1642     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1643 };
1644
1645 /**
1646  * Whether or not the current date is in a leap year.
1647  * @return {Boolean} True if the current date is in a leap year, else false
1648  */
1649 Date.prototype.isLeapYear = function() {
1650     var year = this.getFullYear();
1651     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1652 };
1653
1654 /**
1655  * Get the first day of the current month, adjusted for leap year.  The returned value
1656  * is the numeric day index within the week (0-6) which can be used in conjunction with
1657  * the {@link #monthNames} array to retrieve the textual day name.
1658  * Example:
1659  *<pre><code>
1660 var dt = new Date('1/10/2007');
1661 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1662 </code></pre>
1663  * @return {Number} The day number (0-6)
1664  */
1665 Date.prototype.getFirstDayOfMonth = function() {
1666     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1667     return (day < 0) ? (day + 7) : day;
1668 };
1669
1670 /**
1671  * Get the last day of the current month, adjusted for leap year.  The returned value
1672  * is the numeric day index within the week (0-6) which can be used in conjunction with
1673  * the {@link #monthNames} array to retrieve the textual day name.
1674  * Example:
1675  *<pre><code>
1676 var dt = new Date('1/10/2007');
1677 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1678 </code></pre>
1679  * @return {Number} The day number (0-6)
1680  */
1681 Date.prototype.getLastDayOfMonth = function() {
1682     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1683     return (day < 0) ? (day + 7) : day;
1684 };
1685
1686
1687 /**
1688  * Get the first date of this date's month
1689  * @return {Date}
1690  */
1691 Date.prototype.getFirstDateOfMonth = function() {
1692     return new Date(this.getFullYear(), this.getMonth(), 1);
1693 };
1694
1695 /**
1696  * Get the last date of this date's month
1697  * @return {Date}
1698  */
1699 Date.prototype.getLastDateOfMonth = function() {
1700     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1701 };
1702 /**
1703  * Get the number of days in the current month, adjusted for leap year.
1704  * @return {Number} The number of days in the month
1705  */
1706 Date.prototype.getDaysInMonth = function() {
1707     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1708     return Date.daysInMonth[this.getMonth()];
1709 };
1710
1711 /**
1712  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1713  * @return {String} 'st, 'nd', 'rd' or 'th'
1714  */
1715 Date.prototype.getSuffix = function() {
1716     switch (this.getDate()) {
1717         case 1:
1718         case 21:
1719         case 31:
1720             return "st";
1721         case 2:
1722         case 22:
1723             return "nd";
1724         case 3:
1725         case 23:
1726             return "rd";
1727         default:
1728             return "th";
1729     }
1730 };
1731
1732 // private
1733 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1734
1735 /**
1736  * An array of textual month names.
1737  * Override these values for international dates, for example...
1738  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1739  * @type Array
1740  * @static
1741  */
1742 Date.monthNames =
1743    ["January",
1744     "February",
1745     "March",
1746     "April",
1747     "May",
1748     "June",
1749     "July",
1750     "August",
1751     "September",
1752     "October",
1753     "November",
1754     "December"];
1755
1756 /**
1757  * An array of textual day names.
1758  * Override these values for international dates, for example...
1759  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1760  * @type Array
1761  * @static
1762  */
1763 Date.dayNames =
1764    ["Sunday",
1765     "Monday",
1766     "Tuesday",
1767     "Wednesday",
1768     "Thursday",
1769     "Friday",
1770     "Saturday"];
1771
1772 // private
1773 Date.y2kYear = 50;
1774 // private
1775 Date.monthNumbers = {
1776     Jan:0,
1777     Feb:1,
1778     Mar:2,
1779     Apr:3,
1780     May:4,
1781     Jun:5,
1782     Jul:6,
1783     Aug:7,
1784     Sep:8,
1785     Oct:9,
1786     Nov:10,
1787     Dec:11};
1788
1789 /**
1790  * Creates and returns a new Date instance with the exact same date value as the called instance.
1791  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1792  * variable will also be changed.  When the intention is to create a new variable that will not
1793  * modify the original instance, you should create a clone.
1794  *
1795  * Example of correctly cloning a date:
1796  * <pre><code>
1797 //wrong way:
1798 var orig = new Date('10/1/2006');
1799 var copy = orig;
1800 copy.setDate(5);
1801 document.write(orig);  //returns 'Thu Oct 05 2006'!
1802
1803 //correct way:
1804 var orig = new Date('10/1/2006');
1805 var copy = orig.clone();
1806 copy.setDate(5);
1807 document.write(orig);  //returns 'Thu Oct 01 2006'
1808 </code></pre>
1809  * @return {Date} The new Date instance
1810  */
1811 Date.prototype.clone = function() {
1812         return new Date(this.getTime());
1813 };
1814
1815 /**
1816  * Clears any time information from this date
1817  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1818  @return {Date} this or the clone
1819  */
1820 Date.prototype.clearTime = function(clone){
1821     if(clone){
1822         return this.clone().clearTime();
1823     }
1824     this.setHours(0);
1825     this.setMinutes(0);
1826     this.setSeconds(0);
1827     this.setMilliseconds(0);
1828     return this;
1829 };
1830
1831 // private
1832 // safari setMonth is broken -- check that this is only donw once...
1833 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1834     Date.brokenSetMonth = Date.prototype.setMonth;
1835         Date.prototype.setMonth = function(num){
1836                 if(num <= -1){
1837                         var n = Math.ceil(-num);
1838                         var back_year = Math.ceil(n/12);
1839                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1840                         this.setFullYear(this.getFullYear() - back_year);
1841                         return Date.brokenSetMonth.call(this, month);
1842                 } else {
1843                         return Date.brokenSetMonth.apply(this, arguments);
1844                 }
1845         };
1846 }
1847
1848 /** Date interval constant 
1849 * @static 
1850 * @type String */
1851 Date.MILLI = "ms";
1852 /** Date interval constant 
1853 * @static 
1854 * @type String */
1855 Date.SECOND = "s";
1856 /** Date interval constant 
1857 * @static 
1858 * @type String */
1859 Date.MINUTE = "mi";
1860 /** Date interval constant 
1861 * @static 
1862 * @type String */
1863 Date.HOUR = "h";
1864 /** Date interval constant 
1865 * @static 
1866 * @type String */
1867 Date.DAY = "d";
1868 /** Date interval constant 
1869 * @static 
1870 * @type String */
1871 Date.MONTH = "mo";
1872 /** Date interval constant 
1873 * @static 
1874 * @type String */
1875 Date.YEAR = "y";
1876
1877 /**
1878  * Provides a convenient method of performing basic date arithmetic.  This method
1879  * does not modify the Date instance being called - it creates and returns
1880  * a new Date instance containing the resulting date value.
1881  *
1882  * Examples:
1883  * <pre><code>
1884 //Basic usage:
1885 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1886 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1887
1888 //Negative values will subtract correctly:
1889 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1890 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1891
1892 //You can even chain several calls together in one line!
1893 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1894 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1895  </code></pre>
1896  *
1897  * @param {String} interval   A valid date interval enum value
1898  * @param {Number} value      The amount to add to the current date
1899  * @return {Date} The new Date instance
1900  */
1901 Date.prototype.add = function(interval, value){
1902   var d = this.clone();
1903   if (!interval || value === 0) { return d; }
1904   switch(interval.toLowerCase()){
1905     case Date.MILLI:
1906       d.setMilliseconds(this.getMilliseconds() + value);
1907       break;
1908     case Date.SECOND:
1909       d.setSeconds(this.getSeconds() + value);
1910       break;
1911     case Date.MINUTE:
1912       d.setMinutes(this.getMinutes() + value);
1913       break;
1914     case Date.HOUR:
1915       d.setHours(this.getHours() + value);
1916       break;
1917     case Date.DAY:
1918       d.setDate(this.getDate() + value);
1919       break;
1920     case Date.MONTH:
1921       var day = this.getDate();
1922       if(day > 28){
1923           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1924       }
1925       d.setDate(day);
1926       d.setMonth(this.getMonth() + value);
1927       break;
1928     case Date.YEAR:
1929       d.setFullYear(this.getFullYear() + value);
1930       break;
1931   }
1932   return d;
1933 };
1934 /**
1935  * @class Roo.lib.Dom
1936  * @licence LGPL
1937  * @static
1938  * 
1939  * Dom utils (from YIU afaik)
1940  *
1941  * 
1942  **/
1943 Roo.lib.Dom = {
1944     /**
1945      * Get the view width
1946      * @param {Boolean} full True will get the full document, otherwise it's the view width
1947      * @return {Number} The width
1948      */
1949      
1950     getViewWidth : function(full) {
1951         return full ? this.getDocumentWidth() : this.getViewportWidth();
1952     },
1953     /**
1954      * Get the view height
1955      * @param {Boolean} full True will get the full document, otherwise it's the view height
1956      * @return {Number} The height
1957      */
1958     getViewHeight : function(full) {
1959         return full ? this.getDocumentHeight() : this.getViewportHeight();
1960     },
1961     /**
1962      * Get the Full Document height 
1963      * @return {Number} The height
1964      */
1965     getDocumentHeight: function() {
1966         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1967         return Math.max(scrollHeight, this.getViewportHeight());
1968     },
1969     /**
1970      * Get the Full Document width
1971      * @return {Number} The width
1972      */
1973     getDocumentWidth: function() {
1974         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1975         return Math.max(scrollWidth, this.getViewportWidth());
1976     },
1977     /**
1978      * Get the Window Viewport height
1979      * @return {Number} The height
1980      */
1981     getViewportHeight: function() {
1982         var height = self.innerHeight;
1983         var mode = document.compatMode;
1984
1985         if ((mode || Roo.isIE) && !Roo.isOpera) {
1986             height = (mode == "CSS1Compat") ?
1987                      document.documentElement.clientHeight :
1988                      document.body.clientHeight;
1989         }
1990
1991         return height;
1992     },
1993     /**
1994      * Get the Window Viewport width
1995      * @return {Number} The width
1996      */
1997     getViewportWidth: function() {
1998         var width = self.innerWidth;
1999         var mode = document.compatMode;
2000
2001         if (mode || Roo.isIE) {
2002             width = (mode == "CSS1Compat") ?
2003                     document.documentElement.clientWidth :
2004                     document.body.clientWidth;
2005         }
2006         return width;
2007     },
2008
2009     isAncestor : function(p, c) {
2010         p = Roo.getDom(p);
2011         c = Roo.getDom(c);
2012         if (!p || !c) {
2013             return false;
2014         }
2015
2016         if (p.contains && !Roo.isSafari) {
2017             return p.contains(c);
2018         } else if (p.compareDocumentPosition) {
2019             return !!(p.compareDocumentPosition(c) & 16);
2020         } else {
2021             var parent = c.parentNode;
2022             while (parent) {
2023                 if (parent == p) {
2024                     return true;
2025                 }
2026                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2027                     return false;
2028                 }
2029                 parent = parent.parentNode;
2030             }
2031             return false;
2032         }
2033     },
2034
2035     getRegion : function(el) {
2036         return Roo.lib.Region.getRegion(el);
2037     },
2038
2039     getY : function(el) {
2040         return this.getXY(el)[1];
2041     },
2042
2043     getX : function(el) {
2044         return this.getXY(el)[0];
2045     },
2046
2047     getXY : function(el) {
2048         var p, pe, b, scroll, bd = document.body;
2049         el = Roo.getDom(el);
2050         var fly = Roo.lib.AnimBase.fly;
2051         if (el.getBoundingClientRect) {
2052             b = el.getBoundingClientRect();
2053             scroll = fly(document).getScroll();
2054             return [b.left + scroll.left, b.top + scroll.top];
2055         }
2056         var x = 0, y = 0;
2057
2058         p = el;
2059
2060         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2061
2062         while (p) {
2063
2064             x += p.offsetLeft;
2065             y += p.offsetTop;
2066
2067             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2068                 hasAbsolute = true;
2069             }
2070
2071             if (Roo.isGecko) {
2072                 pe = fly(p);
2073
2074                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2075                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2076
2077
2078                 x += bl;
2079                 y += bt;
2080
2081
2082                 if (p != el && pe.getStyle('overflow') != 'visible') {
2083                     x += bl;
2084                     y += bt;
2085                 }
2086             }
2087             p = p.offsetParent;
2088         }
2089
2090         if (Roo.isSafari && hasAbsolute) {
2091             x -= bd.offsetLeft;
2092             y -= bd.offsetTop;
2093         }
2094
2095         if (Roo.isGecko && !hasAbsolute) {
2096             var dbd = fly(bd);
2097             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2098             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2099         }
2100
2101         p = el.parentNode;
2102         while (p && p != bd) {
2103             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2104                 x -= p.scrollLeft;
2105                 y -= p.scrollTop;
2106             }
2107             p = p.parentNode;
2108         }
2109         return [x, y];
2110     },
2111  
2112   
2113
2114
2115     setXY : function(el, xy) {
2116         el = Roo.fly(el, '_setXY');
2117         el.position();
2118         var pts = el.translatePoints(xy);
2119         if (xy[0] !== false) {
2120             el.dom.style.left = pts.left + "px";
2121         }
2122         if (xy[1] !== false) {
2123             el.dom.style.top = pts.top + "px";
2124         }
2125     },
2126
2127     setX : function(el, x) {
2128         this.setXY(el, [x, false]);
2129     },
2130
2131     setY : function(el, y) {
2132         this.setXY(el, [false, y]);
2133     }
2134 };
2135 /*
2136  * Portions of this file are based on pieces of Yahoo User Interface Library
2137  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2138  * YUI licensed under the BSD License:
2139  * http://developer.yahoo.net/yui/license.txt
2140  * <script type="text/javascript">
2141  *
2142  */
2143
2144 Roo.lib.Event = function() {
2145     var loadComplete = false;
2146     var listeners = [];
2147     var unloadListeners = [];
2148     var retryCount = 0;
2149     var onAvailStack = [];
2150     var counter = 0;
2151     var lastError = null;
2152
2153     return {
2154         POLL_RETRYS: 200,
2155         POLL_INTERVAL: 20,
2156         EL: 0,
2157         TYPE: 1,
2158         FN: 2,
2159         WFN: 3,
2160         OBJ: 3,
2161         ADJ_SCOPE: 4,
2162         _interval: null,
2163
2164         startInterval: function() {
2165             if (!this._interval) {
2166                 var self = this;
2167                 var callback = function() {
2168                     self._tryPreloadAttach();
2169                 };
2170                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2171
2172             }
2173         },
2174
2175         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2176             onAvailStack.push({ id:         p_id,
2177                 fn:         p_fn,
2178                 obj:        p_obj,
2179                 override:   p_override,
2180                 checkReady: false    });
2181
2182             retryCount = this.POLL_RETRYS;
2183             this.startInterval();
2184         },
2185
2186
2187         addListener: function(el, eventName, fn) {
2188             el = Roo.getDom(el);
2189             if (!el || !fn) {
2190                 return false;
2191             }
2192
2193             if ("unload" == eventName) {
2194                 unloadListeners[unloadListeners.length] =
2195                 [el, eventName, fn];
2196                 return true;
2197             }
2198
2199             var wrappedFn = function(e) {
2200                 return fn(Roo.lib.Event.getEvent(e));
2201             };
2202
2203             var li = [el, eventName, fn, wrappedFn];
2204
2205             var index = listeners.length;
2206             listeners[index] = li;
2207
2208             this.doAdd(el, eventName, wrappedFn, false);
2209             return true;
2210
2211         },
2212
2213
2214         removeListener: function(el, eventName, fn) {
2215             var i, len;
2216
2217             el = Roo.getDom(el);
2218
2219             if(!fn) {
2220                 return this.purgeElement(el, false, eventName);
2221             }
2222
2223
2224             if ("unload" == eventName) {
2225
2226                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2227                     var li = unloadListeners[i];
2228                     if (li &&
2229                         li[0] == el &&
2230                         li[1] == eventName &&
2231                         li[2] == fn) {
2232                         unloadListeners.splice(i, 1);
2233                         return true;
2234                     }
2235                 }
2236
2237                 return false;
2238             }
2239
2240             var cacheItem = null;
2241
2242
2243             var index = arguments[3];
2244
2245             if ("undefined" == typeof index) {
2246                 index = this._getCacheIndex(el, eventName, fn);
2247             }
2248
2249             if (index >= 0) {
2250                 cacheItem = listeners[index];
2251             }
2252
2253             if (!el || !cacheItem) {
2254                 return false;
2255             }
2256
2257             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2258
2259             delete listeners[index][this.WFN];
2260             delete listeners[index][this.FN];
2261             listeners.splice(index, 1);
2262
2263             return true;
2264
2265         },
2266
2267
2268         getTarget: function(ev, resolveTextNode) {
2269             ev = ev.browserEvent || ev;
2270             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2271             var t = ev.target || ev.srcElement;
2272             return this.resolveTextNode(t);
2273         },
2274
2275
2276         resolveTextNode: function(node) {
2277             if (Roo.isSafari && node && 3 == node.nodeType) {
2278                 return node.parentNode;
2279             } else {
2280                 return node;
2281             }
2282         },
2283
2284
2285         getPageX: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2288             var x = ev.pageX;
2289             if (!x && 0 !== x) {
2290                 x = ev.clientX || 0;
2291
2292                 if (Roo.isIE) {
2293                     x += this.getScroll()[1];
2294                 }
2295             }
2296
2297             return x;
2298         },
2299
2300
2301         getPageY: function(ev) {
2302             ev = ev.browserEvent || ev;
2303             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2304             var y = ev.pageY;
2305             if (!y && 0 !== y) {
2306                 y = ev.clientY || 0;
2307
2308                 if (Roo.isIE) {
2309                     y += this.getScroll()[0];
2310                 }
2311             }
2312
2313
2314             return y;
2315         },
2316
2317
2318         getXY: function(ev) {
2319             ev = ev.browserEvent || ev;
2320             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2321             return [this.getPageX(ev), this.getPageY(ev)];
2322         },
2323
2324
2325         getRelatedTarget: function(ev) {
2326             ev = ev.browserEvent || ev;
2327             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2328             var t = ev.relatedTarget;
2329             if (!t) {
2330                 if (ev.type == "mouseout") {
2331                     t = ev.toElement;
2332                 } else if (ev.type == "mouseover") {
2333                     t = ev.fromElement;
2334                 }
2335             }
2336
2337             return this.resolveTextNode(t);
2338         },
2339
2340
2341         getTime: function(ev) {
2342             ev = ev.browserEvent || ev;
2343             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2344             if (!ev.time) {
2345                 var t = new Date().getTime();
2346                 try {
2347                     ev.time = t;
2348                 } catch(ex) {
2349                     this.lastError = ex;
2350                     return t;
2351                 }
2352             }
2353
2354             return ev.time;
2355         },
2356
2357
2358         stopEvent: function(ev) {
2359             this.stopPropagation(ev);
2360             this.preventDefault(ev);
2361         },
2362
2363
2364         stopPropagation: function(ev) {
2365             ev = ev.browserEvent || ev;
2366             if (ev.stopPropagation) {
2367                 ev.stopPropagation();
2368             } else {
2369                 ev.cancelBubble = true;
2370             }
2371         },
2372
2373
2374         preventDefault: function(ev) {
2375             ev = ev.browserEvent || ev;
2376             if(ev.preventDefault) {
2377                 ev.preventDefault();
2378             } else {
2379                 ev.returnValue = false;
2380             }
2381         },
2382
2383
2384         getEvent: function(e) {
2385             var ev = e || window.event;
2386             if (!ev) {
2387                 var c = this.getEvent.caller;
2388                 while (c) {
2389                     ev = c.arguments[0];
2390                     if (ev && Event == ev.constructor) {
2391                         break;
2392                     }
2393                     c = c.caller;
2394                 }
2395             }
2396             return ev;
2397         },
2398
2399
2400         getCharCode: function(ev) {
2401             ev = ev.browserEvent || ev;
2402             return ev.charCode || ev.keyCode || 0;
2403         },
2404
2405
2406         _getCacheIndex: function(el, eventName, fn) {
2407             for (var i = 0,len = listeners.length; i < len; ++i) {
2408                 var li = listeners[i];
2409                 if (li &&
2410                     li[this.FN] == fn &&
2411                     li[this.EL] == el &&
2412                     li[this.TYPE] == eventName) {
2413                     return i;
2414                 }
2415             }
2416
2417             return -1;
2418         },
2419
2420
2421         elCache: {},
2422
2423
2424         getEl: function(id) {
2425             return document.getElementById(id);
2426         },
2427
2428
2429         clearCache: function() {
2430         },
2431
2432
2433         _load: function(e) {
2434             loadComplete = true;
2435             var EU = Roo.lib.Event;
2436
2437
2438             if (Roo.isIE) {
2439                 EU.doRemove(window, "load", EU._load);
2440             }
2441         },
2442
2443
2444         _tryPreloadAttach: function() {
2445
2446             if (this.locked) {
2447                 return false;
2448             }
2449
2450             this.locked = true;
2451
2452
2453             var tryAgain = !loadComplete;
2454             if (!tryAgain) {
2455                 tryAgain = (retryCount > 0);
2456             }
2457
2458
2459             var notAvail = [];
2460             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2461                 var item = onAvailStack[i];
2462                 if (item) {
2463                     var el = this.getEl(item.id);
2464
2465                     if (el) {
2466                         if (!item.checkReady ||
2467                             loadComplete ||
2468                             el.nextSibling ||
2469                             (document && document.body)) {
2470
2471                             var scope = el;
2472                             if (item.override) {
2473                                 if (item.override === true) {
2474                                     scope = item.obj;
2475                                 } else {
2476                                     scope = item.override;
2477                                 }
2478                             }
2479                             item.fn.call(scope, item.obj);
2480                             onAvailStack[i] = null;
2481                         }
2482                     } else {
2483                         notAvail.push(item);
2484                     }
2485                 }
2486             }
2487
2488             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2489
2490             if (tryAgain) {
2491
2492                 this.startInterval();
2493             } else {
2494                 clearInterval(this._interval);
2495                 this._interval = null;
2496             }
2497
2498             this.locked = false;
2499
2500             return true;
2501
2502         },
2503
2504
2505         purgeElement: function(el, recurse, eventName) {
2506             var elListeners = this.getListeners(el, eventName);
2507             if (elListeners) {
2508                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2509                     var l = elListeners[i];
2510                     this.removeListener(el, l.type, l.fn);
2511                 }
2512             }
2513
2514             if (recurse && el && el.childNodes) {
2515                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2516                     this.purgeElement(el.childNodes[i], recurse, eventName);
2517                 }
2518             }
2519         },
2520
2521
2522         getListeners: function(el, eventName) {
2523             var results = [], searchLists;
2524             if (!eventName) {
2525                 searchLists = [listeners, unloadListeners];
2526             } else if (eventName == "unload") {
2527                 searchLists = [unloadListeners];
2528             } else {
2529                 searchLists = [listeners];
2530             }
2531
2532             for (var j = 0; j < searchLists.length; ++j) {
2533                 var searchList = searchLists[j];
2534                 if (searchList && searchList.length > 0) {
2535                     for (var i = 0,len = searchList.length; i < len; ++i) {
2536                         var l = searchList[i];
2537                         if (l && l[this.EL] === el &&
2538                             (!eventName || eventName === l[this.TYPE])) {
2539                             results.push({
2540                                 type:   l[this.TYPE],
2541                                 fn:     l[this.FN],
2542                                 obj:    l[this.OBJ],
2543                                 adjust: l[this.ADJ_SCOPE],
2544                                 index:  i
2545                             });
2546                         }
2547                     }
2548                 }
2549             }
2550
2551             return (results.length) ? results : null;
2552         },
2553
2554
2555         _unload: function(e) {
2556
2557             var EU = Roo.lib.Event, i, j, l, len, index;
2558
2559             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2560                 l = unloadListeners[i];
2561                 if (l) {
2562                     var scope = window;
2563                     if (l[EU.ADJ_SCOPE]) {
2564                         if (l[EU.ADJ_SCOPE] === true) {
2565                             scope = l[EU.OBJ];
2566                         } else {
2567                             scope = l[EU.ADJ_SCOPE];
2568                         }
2569                     }
2570                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2571                     unloadListeners[i] = null;
2572                     l = null;
2573                     scope = null;
2574                 }
2575             }
2576
2577             unloadListeners = null;
2578
2579             if (listeners && listeners.length > 0) {
2580                 j = listeners.length;
2581                 while (j) {
2582                     index = j - 1;
2583                     l = listeners[index];
2584                     if (l) {
2585                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2586                                 l[EU.FN], index);
2587                     }
2588                     j = j - 1;
2589                 }
2590                 l = null;
2591
2592                 EU.clearCache();
2593             }
2594
2595             EU.doRemove(window, "unload", EU._unload);
2596
2597         },
2598
2599
2600         getScroll: function() {
2601             var dd = document.documentElement, db = document.body;
2602             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2603                 return [dd.scrollTop, dd.scrollLeft];
2604             } else if (db) {
2605                 return [db.scrollTop, db.scrollLeft];
2606             } else {
2607                 return [0, 0];
2608             }
2609         },
2610
2611
2612         doAdd: function () {
2613             if (window.addEventListener) {
2614                 return function(el, eventName, fn, capture) {
2615                     el.addEventListener(eventName, fn, (capture));
2616                 };
2617             } else if (window.attachEvent) {
2618                 return function(el, eventName, fn, capture) {
2619                     el.attachEvent("on" + eventName, fn);
2620                 };
2621             } else {
2622                 return function() {
2623                 };
2624             }
2625         }(),
2626
2627
2628         doRemove: function() {
2629             if (window.removeEventListener) {
2630                 return function (el, eventName, fn, capture) {
2631                     el.removeEventListener(eventName, fn, (capture));
2632                 };
2633             } else if (window.detachEvent) {
2634                 return function (el, eventName, fn) {
2635                     el.detachEvent("on" + eventName, fn);
2636                 };
2637             } else {
2638                 return function() {
2639                 };
2640             }
2641         }()
2642     };
2643     
2644 }();
2645 (function() {     
2646    
2647     var E = Roo.lib.Event;
2648     E.on = E.addListener;
2649     E.un = E.removeListener;
2650
2651     if (document && document.body) {
2652         E._load();
2653     } else {
2654         E.doAdd(window, "load", E._load);
2655     }
2656     E.doAdd(window, "unload", E._unload);
2657     E._tryPreloadAttach();
2658 })();
2659
2660  
2661
2662 (function() {
2663     /**
2664      * @class Roo.lib.Ajax
2665      *
2666      * provide a simple Ajax request utility functions
2667      * 
2668      * Portions of this file are based on pieces of Yahoo User Interface Library
2669     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2670     * YUI licensed under the BSD License:
2671     * http://developer.yahoo.net/yui/license.txt
2672     * <script type="text/javascript">
2673     *
2674      *
2675      */
2676     Roo.lib.Ajax = {
2677         /**
2678          * @static 
2679          */
2680         request : function(method, uri, cb, data, options) {
2681             if(options){
2682                 var hs = options.headers;
2683                 if(hs){
2684                     for(var h in hs){
2685                         if(hs.hasOwnProperty(h)){
2686                             this.initHeader(h, hs[h], false);
2687                         }
2688                     }
2689                 }
2690                 if(options.xmlData){
2691                     this.initHeader('Content-Type', 'text/xml', false);
2692                     method = 'POST';
2693                     data = options.xmlData;
2694                 }
2695             }
2696
2697             return this.asyncRequest(method, uri, cb, data);
2698         },
2699         /**
2700          * serialize a form
2701          *
2702          * @static
2703          * @param {DomForm} form element
2704          * @return {String} urlencode form output.
2705          */
2706         serializeForm : function(form) {
2707             if(typeof form == 'string') {
2708                 form = (document.getElementById(form) || document.forms[form]);
2709             }
2710
2711             var el, name, val, disabled, data = '', hasSubmit = false;
2712             for (var i = 0; i < form.elements.length; i++) {
2713                 el = form.elements[i];
2714                 disabled = form.elements[i].disabled;
2715                 name = form.elements[i].name;
2716                 val = form.elements[i].value;
2717
2718                 if (!disabled && name){
2719                     switch (el.type)
2720                             {
2721                         case 'select-one':
2722                         case 'select-multiple':
2723                             for (var j = 0; j < el.options.length; j++) {
2724                                 if (el.options[j].selected) {
2725                                     if (Roo.isIE) {
2726                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2727                                     }
2728                                     else {
2729                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2730                                     }
2731                                 }
2732                             }
2733                             break;
2734                         case 'radio':
2735                         case 'checkbox':
2736                             if (el.checked) {
2737                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2738                             }
2739                             break;
2740                         case 'file':
2741
2742                         case undefined:
2743
2744                         case 'reset':
2745
2746                         case 'button':
2747
2748                             break;
2749                         case 'submit':
2750                             if(hasSubmit == false) {
2751                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2752                                 hasSubmit = true;
2753                             }
2754                             break;
2755                         default:
2756                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2757                             break;
2758                     }
2759                 }
2760             }
2761             data = data.substr(0, data.length - 1);
2762             return data;
2763         },
2764
2765         headers:{},
2766
2767         hasHeaders:false,
2768
2769         useDefaultHeader:true,
2770
2771         defaultPostHeader:'application/x-www-form-urlencoded',
2772
2773         useDefaultXhrHeader:true,
2774
2775         defaultXhrHeader:'XMLHttpRequest',
2776
2777         hasDefaultHeaders:true,
2778
2779         defaultHeaders:{},
2780
2781         poll:{},
2782
2783         timeout:{},
2784
2785         pollInterval:50,
2786
2787         transactionId:0,
2788
2789         setProgId:function(id)
2790         {
2791             this.activeX.unshift(id);
2792         },
2793
2794         setDefaultPostHeader:function(b)
2795         {
2796             this.useDefaultHeader = b;
2797         },
2798
2799         setDefaultXhrHeader:function(b)
2800         {
2801             this.useDefaultXhrHeader = b;
2802         },
2803
2804         setPollingInterval:function(i)
2805         {
2806             if (typeof i == 'number' && isFinite(i)) {
2807                 this.pollInterval = i;
2808             }
2809         },
2810
2811         createXhrObject:function(transactionId)
2812         {
2813             var obj,http;
2814             try
2815             {
2816
2817                 http = new XMLHttpRequest();
2818
2819                 obj = { conn:http, tId:transactionId };
2820             }
2821             catch(e)
2822             {
2823                 for (var i = 0; i < this.activeX.length; ++i) {
2824                     try
2825                     {
2826
2827                         http = new ActiveXObject(this.activeX[i]);
2828
2829                         obj = { conn:http, tId:transactionId };
2830                         break;
2831                     }
2832                     catch(e) {
2833                     }
2834                 }
2835             }
2836             finally
2837             {
2838                 return obj;
2839             }
2840         },
2841
2842         getConnectionObject:function()
2843         {
2844             var o;
2845             var tId = this.transactionId;
2846
2847             try
2848             {
2849                 o = this.createXhrObject(tId);
2850                 if (o) {
2851                     this.transactionId++;
2852                 }
2853             }
2854             catch(e) {
2855             }
2856             finally
2857             {
2858                 return o;
2859             }
2860         },
2861
2862         asyncRequest:function(method, uri, callback, postData)
2863         {
2864             var o = this.getConnectionObject();
2865
2866             if (!o) {
2867                 return null;
2868             }
2869             else {
2870                 o.conn.open(method, uri, true);
2871
2872                 if (this.useDefaultXhrHeader) {
2873                     if (!this.defaultHeaders['X-Requested-With']) {
2874                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2875                     }
2876                 }
2877
2878                 if(postData && this.useDefaultHeader){
2879                     this.initHeader('Content-Type', this.defaultPostHeader);
2880                 }
2881
2882                  if (this.hasDefaultHeaders || this.hasHeaders) {
2883                     this.setHeader(o);
2884                 }
2885
2886                 this.handleReadyState(o, callback);
2887                 o.conn.send(postData || null);
2888
2889                 return o;
2890             }
2891         },
2892
2893         handleReadyState:function(o, callback)
2894         {
2895             var oConn = this;
2896
2897             if (callback && callback.timeout) {
2898                 
2899                 this.timeout[o.tId] = window.setTimeout(function() {
2900                     oConn.abort(o, callback, true);
2901                 }, callback.timeout);
2902             }
2903
2904             this.poll[o.tId] = window.setInterval(
2905                     function() {
2906                         if (o.conn && o.conn.readyState == 4) {
2907                             window.clearInterval(oConn.poll[o.tId]);
2908                             delete oConn.poll[o.tId];
2909
2910                             if(callback && callback.timeout) {
2911                                 window.clearTimeout(oConn.timeout[o.tId]);
2912                                 delete oConn.timeout[o.tId];
2913                             }
2914
2915                             oConn.handleTransactionResponse(o, callback);
2916                         }
2917                     }
2918                     , this.pollInterval);
2919         },
2920
2921         handleTransactionResponse:function(o, callback, isAbort)
2922         {
2923
2924             if (!callback) {
2925                 this.releaseObject(o);
2926                 return;
2927             }
2928
2929             var httpStatus, responseObject;
2930
2931             try
2932             {
2933                 if (o.conn.status !== undefined && o.conn.status != 0) {
2934                     httpStatus = o.conn.status;
2935                 }
2936                 else {
2937                     httpStatus = 13030;
2938                 }
2939             }
2940             catch(e) {
2941
2942
2943                 httpStatus = 13030;
2944             }
2945
2946             if (httpStatus >= 200 && httpStatus < 300) {
2947                 responseObject = this.createResponseObject(o, callback.argument);
2948                 if (callback.success) {
2949                     if (!callback.scope) {
2950                         callback.success(responseObject);
2951                     }
2952                     else {
2953
2954
2955                         callback.success.apply(callback.scope, [responseObject]);
2956                     }
2957                 }
2958             }
2959             else {
2960                 switch (httpStatus) {
2961
2962                     case 12002:
2963                     case 12029:
2964                     case 12030:
2965                     case 12031:
2966                     case 12152:
2967                     case 13030:
2968                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2969                         if (callback.failure) {
2970                             if (!callback.scope) {
2971                                 callback.failure(responseObject);
2972                             }
2973                             else {
2974                                 callback.failure.apply(callback.scope, [responseObject]);
2975                             }
2976                         }
2977                         break;
2978                     default:
2979                         responseObject = this.createResponseObject(o, callback.argument);
2980                         if (callback.failure) {
2981                             if (!callback.scope) {
2982                                 callback.failure(responseObject);
2983                             }
2984                             else {
2985                                 callback.failure.apply(callback.scope, [responseObject]);
2986                             }
2987                         }
2988                 }
2989             }
2990
2991             this.releaseObject(o);
2992             responseObject = null;
2993         },
2994
2995         createResponseObject:function(o, callbackArg)
2996         {
2997             var obj = {};
2998             var headerObj = {};
2999
3000             try
3001             {
3002                 var headerStr = o.conn.getAllResponseHeaders();
3003                 var header = headerStr.split('\n');
3004                 for (var i = 0; i < header.length; i++) {
3005                     var delimitPos = header[i].indexOf(':');
3006                     if (delimitPos != -1) {
3007                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
3008                     }
3009                 }
3010             }
3011             catch(e) {
3012             }
3013
3014             obj.tId = o.tId;
3015             obj.status = o.conn.status;
3016             obj.statusText = o.conn.statusText;
3017             obj.getResponseHeader = headerObj;
3018             obj.getAllResponseHeaders = headerStr;
3019             obj.responseText = o.conn.responseText;
3020             obj.responseXML = o.conn.responseXML;
3021
3022             if (typeof callbackArg !== undefined) {
3023                 obj.argument = callbackArg;
3024             }
3025
3026             return obj;
3027         },
3028
3029         createExceptionObject:function(tId, callbackArg, isAbort)
3030         {
3031             var COMM_CODE = 0;
3032             var COMM_ERROR = 'communication failure';
3033             var ABORT_CODE = -1;
3034             var ABORT_ERROR = 'transaction aborted';
3035
3036             var obj = {};
3037
3038             obj.tId = tId;
3039             if (isAbort) {
3040                 obj.status = ABORT_CODE;
3041                 obj.statusText = ABORT_ERROR;
3042             }
3043             else {
3044                 obj.status = COMM_CODE;
3045                 obj.statusText = COMM_ERROR;
3046             }
3047
3048             if (callbackArg) {
3049                 obj.argument = callbackArg;
3050             }
3051
3052             return obj;
3053         },
3054
3055         initHeader:function(label, value, isDefault)
3056         {
3057             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3058
3059             if (headerObj[label] === undefined) {
3060                 headerObj[label] = value;
3061             }
3062             else {
3063
3064
3065                 headerObj[label] = value + "," + headerObj[label];
3066             }
3067
3068             if (isDefault) {
3069                 this.hasDefaultHeaders = true;
3070             }
3071             else {
3072                 this.hasHeaders = true;
3073             }
3074         },
3075
3076
3077         setHeader:function(o)
3078         {
3079             if (this.hasDefaultHeaders) {
3080                 for (var prop in this.defaultHeaders) {
3081                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3082                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3083                     }
3084                 }
3085             }
3086
3087             if (this.hasHeaders) {
3088                 for (var prop in this.headers) {
3089                     if (this.headers.hasOwnProperty(prop)) {
3090                         o.conn.setRequestHeader(prop, this.headers[prop]);
3091                     }
3092                 }
3093                 this.headers = {};
3094                 this.hasHeaders = false;
3095             }
3096         },
3097
3098         resetDefaultHeaders:function() {
3099             delete this.defaultHeaders;
3100             this.defaultHeaders = {};
3101             this.hasDefaultHeaders = false;
3102         },
3103
3104         abort:function(o, callback, isTimeout)
3105         {
3106             if(this.isCallInProgress(o)) {
3107                 o.conn.abort();
3108                 window.clearInterval(this.poll[o.tId]);
3109                 delete this.poll[o.tId];
3110                 if (isTimeout) {
3111                     delete this.timeout[o.tId];
3112                 }
3113
3114                 this.handleTransactionResponse(o, callback, true);
3115
3116                 return true;
3117             }
3118             else {
3119                 return false;
3120             }
3121         },
3122
3123
3124         isCallInProgress:function(o)
3125         {
3126             if (o && o.conn) {
3127                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3128             }
3129             else {
3130
3131                 return false;
3132             }
3133         },
3134
3135
3136         releaseObject:function(o)
3137         {
3138
3139             o.conn = null;
3140
3141             o = null;
3142         },
3143
3144         activeX:[
3145         'MSXML2.XMLHTTP.3.0',
3146         'MSXML2.XMLHTTP',
3147         'Microsoft.XMLHTTP'
3148         ]
3149
3150
3151     };
3152 })();/*
3153  * Portions of this file are based on pieces of Yahoo User Interface Library
3154  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3155  * YUI licensed under the BSD License:
3156  * http://developer.yahoo.net/yui/license.txt
3157  * <script type="text/javascript">
3158  *
3159  */
3160
3161 Roo.lib.Region = function(t, r, b, l) {
3162     this.top = t;
3163     this[1] = t;
3164     this.right = r;
3165     this.bottom = b;
3166     this.left = l;
3167     this[0] = l;
3168 };
3169
3170
3171 Roo.lib.Region.prototype = {
3172     contains : function(region) {
3173         return ( region.left >= this.left &&
3174                  region.right <= this.right &&
3175                  region.top >= this.top &&
3176                  region.bottom <= this.bottom    );
3177
3178     },
3179
3180     getArea : function() {
3181         return ( (this.bottom - this.top) * (this.right - this.left) );
3182     },
3183
3184     intersect : function(region) {
3185         var t = Math.max(this.top, region.top);
3186         var r = Math.min(this.right, region.right);
3187         var b = Math.min(this.bottom, region.bottom);
3188         var l = Math.max(this.left, region.left);
3189
3190         if (b >= t && r >= l) {
3191             return new Roo.lib.Region(t, r, b, l);
3192         } else {
3193             return null;
3194         }
3195     },
3196     union : function(region) {
3197         var t = Math.min(this.top, region.top);
3198         var r = Math.max(this.right, region.right);
3199         var b = Math.max(this.bottom, region.bottom);
3200         var l = Math.min(this.left, region.left);
3201
3202         return new Roo.lib.Region(t, r, b, l);
3203     },
3204
3205     adjust : function(t, l, b, r) {
3206         this.top += t;
3207         this.left += l;
3208         this.right += r;
3209         this.bottom += b;
3210         return this;
3211     }
3212 };
3213
3214 Roo.lib.Region.getRegion = function(el) {
3215     var p = Roo.lib.Dom.getXY(el);
3216
3217     var t = p[1];
3218     var r = p[0] + el.offsetWidth;
3219     var b = p[1] + el.offsetHeight;
3220     var l = p[0];
3221
3222     return new Roo.lib.Region(t, r, b, l);
3223 };
3224 /*
3225  * Portions of this file are based on pieces of Yahoo User Interface Library
3226  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3227  * YUI licensed under the BSD License:
3228  * http://developer.yahoo.net/yui/license.txt
3229  * <script type="text/javascript">
3230  *
3231  */
3232 //@@dep Roo.lib.Region
3233
3234
3235 Roo.lib.Point = function(x, y) {
3236     if (x instanceof Array) {
3237         y = x[1];
3238         x = x[0];
3239     }
3240     this.x = this.right = this.left = this[0] = x;
3241     this.y = this.top = this.bottom = this[1] = y;
3242 };
3243
3244 Roo.lib.Point.prototype = new Roo.lib.Region();
3245 /*
3246  * Portions of this file are based on pieces of Yahoo User Interface Library
3247  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3248  * YUI licensed under the BSD License:
3249  * http://developer.yahoo.net/yui/license.txt
3250  * <script type="text/javascript">
3251  *
3252  */
3253  
3254 (function() {   
3255
3256     Roo.lib.Anim = {
3257         scroll : function(el, args, duration, easing, cb, scope) {
3258             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3259         },
3260
3261         motion : function(el, args, duration, easing, cb, scope) {
3262             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3263         },
3264
3265         color : function(el, args, duration, easing, cb, scope) {
3266             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3267         },
3268
3269         run : function(el, args, duration, easing, cb, scope, type) {
3270             type = type || Roo.lib.AnimBase;
3271             if (typeof easing == "string") {
3272                 easing = Roo.lib.Easing[easing];
3273             }
3274             var anim = new type(el, args, duration, easing);
3275             anim.animateX(function() {
3276                 Roo.callback(cb, scope);
3277             });
3278             return anim;
3279         }
3280     };
3281 })();/*
3282  * Portions of this file are based on pieces of Yahoo User Interface Library
3283  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3284  * YUI licensed under the BSD License:
3285  * http://developer.yahoo.net/yui/license.txt
3286  * <script type="text/javascript">
3287  *
3288  */
3289
3290 (function() {    
3291     var libFlyweight;
3292     
3293     function fly(el) {
3294         if (!libFlyweight) {
3295             libFlyweight = new Roo.Element.Flyweight();
3296         }
3297         libFlyweight.dom = el;
3298         return libFlyweight;
3299     }
3300
3301     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3302     
3303    
3304     
3305     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3306         if (el) {
3307             this.init(el, attributes, duration, method);
3308         }
3309     };
3310
3311     Roo.lib.AnimBase.fly = fly;
3312     
3313     
3314     
3315     Roo.lib.AnimBase.prototype = {
3316
3317         toString: function() {
3318             var el = this.getEl();
3319             var id = el.id || el.tagName;
3320             return ("Anim " + id);
3321         },
3322
3323         patterns: {
3324             noNegatives:        /width|height|opacity|padding/i,
3325             offsetAttribute:  /^((width|height)|(top|left))$/,
3326             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3327             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3328         },
3329
3330
3331         doMethod: function(attr, start, end) {
3332             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3333         },
3334
3335
3336         setAttribute: function(attr, val, unit) {
3337             if (this.patterns.noNegatives.test(attr)) {
3338                 val = (val > 0) ? val : 0;
3339             }
3340
3341             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3342         },
3343
3344
3345         getAttribute: function(attr) {
3346             var el = this.getEl();
3347             var val = fly(el).getStyle(attr);
3348
3349             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3350                 return parseFloat(val);
3351             }
3352
3353             var a = this.patterns.offsetAttribute.exec(attr) || [];
3354             var pos = !!( a[3] );
3355             var box = !!( a[2] );
3356
3357
3358             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3359                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3360             } else {
3361                 val = 0;
3362             }
3363
3364             return val;
3365         },
3366
3367
3368         getDefaultUnit: function(attr) {
3369             if (this.patterns.defaultUnit.test(attr)) {
3370                 return 'px';
3371             }
3372
3373             return '';
3374         },
3375
3376         animateX : function(callback, scope) {
3377             var f = function() {
3378                 this.onComplete.removeListener(f);
3379                 if (typeof callback == "function") {
3380                     callback.call(scope || this, this);
3381                 }
3382             };
3383             this.onComplete.addListener(f, this);
3384             this.animate();
3385         },
3386
3387
3388         setRuntimeAttribute: function(attr) {
3389             var start;
3390             var end;
3391             var attributes = this.attributes;
3392
3393             this.runtimeAttributes[attr] = {};
3394
3395             var isset = function(prop) {
3396                 return (typeof prop !== 'undefined');
3397             };
3398
3399             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3400                 return false;
3401             }
3402
3403             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3404
3405
3406             if (isset(attributes[attr]['to'])) {
3407                 end = attributes[attr]['to'];
3408             } else if (isset(attributes[attr]['by'])) {
3409                 if (start.constructor == Array) {
3410                     end = [];
3411                     for (var i = 0, len = start.length; i < len; ++i) {
3412                         end[i] = start[i] + attributes[attr]['by'][i];
3413                     }
3414                 } else {
3415                     end = start + attributes[attr]['by'];
3416                 }
3417             }
3418
3419             this.runtimeAttributes[attr].start = start;
3420             this.runtimeAttributes[attr].end = end;
3421
3422
3423             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3424         },
3425
3426
3427         init: function(el, attributes, duration, method) {
3428
3429             var isAnimated = false;
3430
3431
3432             var startTime = null;
3433
3434
3435             var actualFrames = 0;
3436
3437
3438             el = Roo.getDom(el);
3439
3440
3441             this.attributes = attributes || {};
3442
3443
3444             this.duration = duration || 1;
3445
3446
3447             this.method = method || Roo.lib.Easing.easeNone;
3448
3449
3450             this.useSeconds = true;
3451
3452
3453             this.currentFrame = 0;
3454
3455
3456             this.totalFrames = Roo.lib.AnimMgr.fps;
3457
3458
3459             this.getEl = function() {
3460                 return el;
3461             };
3462
3463
3464             this.isAnimated = function() {
3465                 return isAnimated;
3466             };
3467
3468
3469             this.getStartTime = function() {
3470                 return startTime;
3471             };
3472
3473             this.runtimeAttributes = {};
3474
3475
3476             this.animate = function() {
3477                 if (this.isAnimated()) {
3478                     return false;
3479                 }
3480
3481                 this.currentFrame = 0;
3482
3483                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3484
3485                 Roo.lib.AnimMgr.registerElement(this);
3486             };
3487
3488
3489             this.stop = function(finish) {
3490                 if (finish) {
3491                     this.currentFrame = this.totalFrames;
3492                     this._onTween.fire();
3493                 }
3494                 Roo.lib.AnimMgr.stop(this);
3495             };
3496
3497             var onStart = function() {
3498                 this.onStart.fire();
3499
3500                 this.runtimeAttributes = {};
3501                 for (var attr in this.attributes) {
3502                     this.setRuntimeAttribute(attr);
3503                 }
3504
3505                 isAnimated = true;
3506                 actualFrames = 0;
3507                 startTime = new Date();
3508             };
3509
3510
3511             var onTween = function() {
3512                 var data = {
3513                     duration: new Date() - this.getStartTime(),
3514                     currentFrame: this.currentFrame
3515                 };
3516
3517                 data.toString = function() {
3518                     return (
3519                             'duration: ' + data.duration +
3520                             ', currentFrame: ' + data.currentFrame
3521                             );
3522                 };
3523
3524                 this.onTween.fire(data);
3525
3526                 var runtimeAttributes = this.runtimeAttributes;
3527
3528                 for (var attr in runtimeAttributes) {
3529                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3530                 }
3531
3532                 actualFrames += 1;
3533             };
3534
3535             var onComplete = function() {
3536                 var actual_duration = (new Date() - startTime) / 1000 ;
3537
3538                 var data = {
3539                     duration: actual_duration,
3540                     frames: actualFrames,
3541                     fps: actualFrames / actual_duration
3542                 };
3543
3544                 data.toString = function() {
3545                     return (
3546                             'duration: ' + data.duration +
3547                             ', frames: ' + data.frames +
3548                             ', fps: ' + data.fps
3549                             );
3550                 };
3551
3552                 isAnimated = false;
3553                 actualFrames = 0;
3554                 this.onComplete.fire(data);
3555             };
3556
3557
3558             this._onStart = new Roo.util.Event(this);
3559             this.onStart = new Roo.util.Event(this);
3560             this.onTween = new Roo.util.Event(this);
3561             this._onTween = new Roo.util.Event(this);
3562             this.onComplete = new Roo.util.Event(this);
3563             this._onComplete = new Roo.util.Event(this);
3564             this._onStart.addListener(onStart);
3565             this._onTween.addListener(onTween);
3566             this._onComplete.addListener(onComplete);
3567         }
3568     };
3569 })();
3570 /*
3571  * Portions of this file are based on pieces of Yahoo User Interface Library
3572  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3573  * YUI licensed under the BSD License:
3574  * http://developer.yahoo.net/yui/license.txt
3575  * <script type="text/javascript">
3576  *
3577  */
3578
3579 Roo.lib.AnimMgr = new function() {
3580
3581     var thread = null;
3582
3583
3584     var queue = [];
3585
3586
3587     var tweenCount = 0;
3588
3589
3590     this.fps = 1000;
3591
3592
3593     this.delay = 1;
3594
3595
3596     this.registerElement = function(tween) {
3597         queue[queue.length] = tween;
3598         tweenCount += 1;
3599         tween._onStart.fire();
3600         this.start();
3601     };
3602
3603
3604     this.unRegister = function(tween, index) {
3605         tween._onComplete.fire();
3606         index = index || getIndex(tween);
3607         if (index != -1) {
3608             queue.splice(index, 1);
3609         }
3610
3611         tweenCount -= 1;
3612         if (tweenCount <= 0) {
3613             this.stop();
3614         }
3615     };
3616
3617
3618     this.start = function() {
3619         if (thread === null) {
3620             thread = setInterval(this.run, this.delay);
3621         }
3622     };
3623
3624
3625     this.stop = function(tween) {
3626         if (!tween) {
3627             clearInterval(thread);
3628
3629             for (var i = 0, len = queue.length; i < len; ++i) {
3630                 if (queue[0].isAnimated()) {
3631                     this.unRegister(queue[0], 0);
3632                 }
3633             }
3634
3635             queue = [];
3636             thread = null;
3637             tweenCount = 0;
3638         }
3639         else {
3640             this.unRegister(tween);
3641         }
3642     };
3643
3644
3645     this.run = function() {
3646         for (var i = 0, len = queue.length; i < len; ++i) {
3647             var tween = queue[i];
3648             if (!tween || !tween.isAnimated()) {
3649                 continue;
3650             }
3651
3652             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3653             {
3654                 tween.currentFrame += 1;
3655
3656                 if (tween.useSeconds) {
3657                     correctFrame(tween);
3658                 }
3659                 tween._onTween.fire();
3660             }
3661             else {
3662                 Roo.lib.AnimMgr.stop(tween, i);
3663             }
3664         }
3665     };
3666
3667     var getIndex = function(anim) {
3668         for (var i = 0, len = queue.length; i < len; ++i) {
3669             if (queue[i] == anim) {
3670                 return i;
3671             }
3672         }
3673         return -1;
3674     };
3675
3676
3677     var correctFrame = function(tween) {
3678         var frames = tween.totalFrames;
3679         var frame = tween.currentFrame;
3680         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3681         var elapsed = (new Date() - tween.getStartTime());
3682         var tweak = 0;
3683
3684         if (elapsed < tween.duration * 1000) {
3685             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3686         } else {
3687             tweak = frames - (frame + 1);
3688         }
3689         if (tweak > 0 && isFinite(tweak)) {
3690             if (tween.currentFrame + tweak >= frames) {
3691                 tweak = frames - (frame + 1);
3692             }
3693
3694             tween.currentFrame += tweak;
3695         }
3696     };
3697 };
3698
3699     /*
3700  * Portions of this file are based on pieces of Yahoo User Interface Library
3701  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3702  * YUI licensed under the BSD License:
3703  * http://developer.yahoo.net/yui/license.txt
3704  * <script type="text/javascript">
3705  *
3706  */
3707 Roo.lib.Bezier = new function() {
3708
3709         this.getPosition = function(points, t) {
3710             var n = points.length;
3711             var tmp = [];
3712
3713             for (var i = 0; i < n; ++i) {
3714                 tmp[i] = [points[i][0], points[i][1]];
3715             }
3716
3717             for (var j = 1; j < n; ++j) {
3718                 for (i = 0; i < n - j; ++i) {
3719                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3720                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3721                 }
3722             }
3723
3724             return [ tmp[0][0], tmp[0][1] ];
3725
3726         };
3727     }; 
3728
3729 /**
3730  * @class Roo.lib.Color
3731  * @constructor
3732  * An abstract Color implementation. Concrete Color implementations should use
3733  * an instance of this function as their prototype, and implement the getRGB and
3734  * getHSL functions. getRGB should return an object representing the RGB
3735  * components of this Color, with the red, green, and blue components in the
3736  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3737  * return an object representing the HSL components of this Color, with the hue
3738  * component in the range [0,360), the saturation and lightness components in
3739  * the range [0,100], and the alpha component in the range [0,1].
3740  *
3741  *
3742  * Color.js
3743  *
3744  * Functions for Color handling and processing.
3745  *
3746  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3747  *
3748  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3749  * rights to this program, with the intention of it becoming part of the public
3750  * domain. Because this program is released into the public domain, it comes with
3751  * no warranty either expressed or implied, to the extent permitted by law.
3752  * 
3753  * For more free and public domain JavaScript code by the same author, visit:
3754  * http://www.safalra.com/web-design/javascript/
3755  * 
3756  */
3757 Roo.lib.Color = function() { }
3758
3759
3760 Roo.apply(Roo.lib.Color.prototype, {
3761   
3762   rgb : null,
3763   hsv : null,
3764   hsl : null,
3765   
3766   /**
3767    * getIntegerRGB
3768    * @return {Object} an object representing the RGBA components of this Color. The red,
3769    * green, and blue components are converted to integers in the range [0,255].
3770    * The alpha is a value in the range [0,1].
3771    */
3772   getIntegerRGB : function(){
3773
3774     // get the RGB components of this Color
3775     var rgb = this.getRGB();
3776
3777     // return the integer components
3778     return {
3779       'r' : Math.round(rgb.r),
3780       'g' : Math.round(rgb.g),
3781       'b' : Math.round(rgb.b),
3782       'a' : rgb.a
3783     };
3784
3785   },
3786
3787   /**
3788    * getPercentageRGB
3789    * @return {Object} an object representing the RGBA components of this Color. The red,
3790    * green, and blue components are converted to numbers in the range [0,100].
3791    * The alpha is a value in the range [0,1].
3792    */
3793   getPercentageRGB : function(){
3794
3795     // get the RGB components of this Color
3796     var rgb = this.getRGB();
3797
3798     // return the percentage components
3799     return {
3800       'r' : 100 * rgb.r / 255,
3801       'g' : 100 * rgb.g / 255,
3802       'b' : 100 * rgb.b / 255,
3803       'a' : rgb.a
3804     };
3805
3806   },
3807
3808   /**
3809    * getCSSHexadecimalRGB
3810    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3811    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3812    * are two-digit hexadecimal numbers.
3813    */
3814   getCSSHexadecimalRGB : function()
3815   {
3816
3817     // get the integer RGB components
3818     var rgb = this.getIntegerRGB();
3819
3820     // determine the hexadecimal equivalents
3821     var r16 = rgb.r.toString(16);
3822     var g16 = rgb.g.toString(16);
3823     var b16 = rgb.b.toString(16);
3824
3825     // return the CSS RGB Color value
3826     return '#'
3827         + (r16.length == 2 ? r16 : '0' + r16)
3828         + (g16.length == 2 ? g16 : '0' + g16)
3829         + (b16.length == 2 ? b16 : '0' + b16);
3830
3831   },
3832
3833   /**
3834    * getCSSIntegerRGB
3835    * @return {String} a string representing this Color as a CSS integer RGB Color
3836    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3837    * are integers in the range [0,255].
3838    */
3839   getCSSIntegerRGB : function(){
3840
3841     // get the integer RGB components
3842     var rgb = this.getIntegerRGB();
3843
3844     // return the CSS RGB Color value
3845     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3846
3847   },
3848
3849   /**
3850    * getCSSIntegerRGBA
3851    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3852    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3853    * b are integers in the range [0,255] and a is in the range [0,1].
3854    */
3855   getCSSIntegerRGBA : function(){
3856
3857     // get the integer RGB components
3858     var rgb = this.getIntegerRGB();
3859
3860     // return the CSS integer RGBA Color value
3861     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3862
3863   },
3864
3865   /**
3866    * getCSSPercentageRGB
3867    * @return {String} a string representing this Color as a CSS percentage RGB Color
3868    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3869    * b are in the range [0,100].
3870    */
3871   getCSSPercentageRGB : function(){
3872
3873     // get the percentage RGB components
3874     var rgb = this.getPercentageRGB();
3875
3876     // return the CSS RGB Color value
3877     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3878
3879   },
3880
3881   /**
3882    * getCSSPercentageRGBA
3883    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3884    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3885    * and b are in the range [0,100] and a is in the range [0,1].
3886    */
3887   getCSSPercentageRGBA : function(){
3888
3889     // get the percentage RGB components
3890     var rgb = this.getPercentageRGB();
3891
3892     // return the CSS percentage RGBA Color value
3893     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3894
3895   },
3896
3897   /**
3898    * getCSSHSL
3899    * @return {String} a string representing this Color as a CSS HSL Color value - that
3900    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3901    * s and l are in the range [0,100].
3902    */
3903   getCSSHSL : function(){
3904
3905     // get the HSL components
3906     var hsl = this.getHSL();
3907
3908     // return the CSS HSL Color value
3909     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3910
3911   },
3912
3913   /**
3914    * getCSSHSLA
3915    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3916    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3917    * s and l are in the range [0,100], and a is in the range [0,1].
3918    */
3919   getCSSHSLA : function(){
3920
3921     // get the HSL components
3922     var hsl = this.getHSL();
3923
3924     // return the CSS HSL Color value
3925     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3926
3927   },
3928
3929   /**
3930    * Sets the Color of the specified node to this Color. This functions sets
3931    * the CSS 'color' property for the node. The parameter is:
3932    * 
3933    * @param {DomElement} node - the node whose Color should be set
3934    */
3935   setNodeColor : function(node){
3936
3937     // set the Color of the node
3938     node.style.color = this.getCSSHexadecimalRGB();
3939
3940   },
3941
3942   /**
3943    * Sets the background Color of the specified node to this Color. This
3944    * functions sets the CSS 'background-color' property for the node. The
3945    * parameter is:
3946    *
3947    * @param {DomElement} node - the node whose background Color should be set
3948    */
3949   setNodeBackgroundColor : function(node){
3950
3951     // set the background Color of the node
3952     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3953
3954   },
3955   // convert between formats..
3956   toRGB: function()
3957   {
3958     var r = this.getIntegerRGB();
3959     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3960     
3961   },
3962   toHSL : function()
3963   {
3964      var hsl = this.getHSL();
3965   // return the CSS HSL Color value
3966     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3967     
3968   },
3969   
3970   toHSV : function()
3971   {
3972     var rgb = this.toRGB();
3973     var hsv = rgb.getHSV();
3974    // return the CSS HSL Color value
3975     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3976     
3977   },
3978   
3979   // modify  v = 0 ... 1 (eg. 0.5)
3980   saturate : function(v)
3981   {
3982       var rgb = this.toRGB();
3983       var hsv = rgb.getHSV();
3984       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3985       
3986   
3987   },
3988   
3989    
3990   /**
3991    * getRGB
3992    * @return {Object} the RGB and alpha components of this Color as an object with r,
3993    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3994    * the range [0,1].
3995    */
3996   getRGB: function(){
3997    
3998     // return the RGB components
3999     return {
4000       'r' : this.rgb.r,
4001       'g' : this.rgb.g,
4002       'b' : this.rgb.b,
4003       'a' : this.alpha
4004     };
4005
4006   },
4007
4008   /**
4009    * getHSV
4010    * @return {Object} the HSV and alpha components of this Color as an object with h,
4011    * s, v, and a properties. h is in the range [0,360), s and v are in the range
4012    * [0,100], and a is in the range [0,1].
4013    */
4014   getHSV : function()
4015   {
4016     
4017     // calculate the HSV components if necessary
4018     if (this.hsv == null) {
4019       this.calculateHSV();
4020     }
4021
4022     // return the HSV components
4023     return {
4024       'h' : this.hsv.h,
4025       's' : this.hsv.s,
4026       'v' : this.hsv.v,
4027       'a' : this.alpha
4028     };
4029
4030   },
4031
4032   /**
4033    * getHSL
4034    * @return {Object} the HSL and alpha components of this Color as an object with h,
4035    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4036    * [0,100], and a is in the range [0,1].
4037    */
4038   getHSL : function(){
4039     
4040      
4041     // calculate the HSV components if necessary
4042     if (this.hsl == null) { this.calculateHSL(); }
4043
4044     // return the HSL components
4045     return {
4046       'h' : this.hsl.h,
4047       's' : this.hsl.s,
4048       'l' : this.hsl.l,
4049       'a' : this.alpha
4050     };
4051
4052   }
4053   
4054
4055 });
4056
4057
4058 /**
4059  * @class Roo.lib.RGBColor
4060  * @extends Roo.lib.Color
4061  * Creates a Color specified in the RGB Color space, with an optional alpha
4062  * component. The parameters are:
4063  * @constructor
4064  * 
4065
4066  * @param {Number} r - the red component, clipped to the range [0,255]
4067  * @param {Number} g - the green component, clipped to the range [0,255]
4068  * @param {Number} b - the blue component, clipped to the range [0,255]
4069  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4070  *     optional and defaults to 1
4071  */
4072 Roo.lib.RGBColor = function (r, g, b, a){
4073
4074   // store the alpha component after clipping it if necessary
4075   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4076
4077   // store the RGB components after clipping them if necessary
4078   this.rgb =
4079       {
4080         'r' : Math.max(0, Math.min(255, r)),
4081         'g' : Math.max(0, Math.min(255, g)),
4082         'b' : Math.max(0, Math.min(255, b))
4083       };
4084
4085   // initialise the HSV and HSL components to null
4086   
4087
4088   /* 
4089    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4090    * range [0,360). The parameters are:
4091    *
4092    * maximum - the maximum of the RGB component values
4093    * range   - the range of the RGB component values
4094    */
4095    
4096
4097 }
4098 // this does an 'exteds'
4099 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4100
4101   
4102     getHue  : function(maximum, range)
4103     {
4104       var rgb = this.rgb;
4105        
4106       // check whether the range is zero
4107       if (range == 0){
4108   
4109         // set the hue to zero (any hue is acceptable as the Color is grey)
4110         var hue = 0;
4111   
4112       }else{
4113   
4114         // determine which of the components has the highest value and set the hue
4115         switch (maximum){
4116   
4117           // red has the highest value
4118           case rgb.r:
4119             var hue = (rgb.g - rgb.b) / range * 60;
4120             if (hue < 0) { hue += 360; }
4121             break;
4122   
4123           // green has the highest value
4124           case rgb.g:
4125             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4126             break;
4127   
4128           // blue has the highest value
4129           case rgb.b:
4130             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4131             break;
4132   
4133         }
4134   
4135       }
4136   
4137       // return the hue
4138       return hue;
4139   
4140     },
4141
4142   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4143    * be returned be the getHSV function.
4144    */
4145    calculateHSV : function(){
4146     var rgb = this.rgb;
4147     // get the maximum and range of the RGB component values
4148     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4149     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4150
4151     // store the HSV components
4152     this.hsv =
4153         {
4154           'h' : this.getHue(maximum, range),
4155           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4156           'v' : maximum / 2.55
4157         };
4158
4159   },
4160
4161   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4162    * be returned be the getHSL function.
4163    */
4164    calculateHSL : function(){
4165     var rgb = this.rgb;
4166     // get the maximum and range of the RGB component values
4167     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4168     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4169
4170     // determine the lightness in the range [0,1]
4171     var l = maximum / 255 - range / 510;
4172
4173     // store the HSL components
4174     this.hsl =
4175         {
4176           'h' : this.getHue(maximum, range),
4177           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4178           'l' : 100 * l
4179         };
4180
4181   }
4182
4183 });
4184
4185 /**
4186  * @class Roo.lib.HSVColor
4187  * @extends Roo.lib.Color
4188  * Creates a Color specified in the HSV Color space, with an optional alpha
4189  * component. The parameters are:
4190  * @constructor
4191  *
4192  * @param {Number} h - the hue component, wrapped to the range [0,360)
4193  * @param {Number} s - the saturation component, clipped to the range [0,100]
4194  * @param {Number} v - the value component, clipped to the range [0,100]
4195  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4196  *     optional and defaults to 1
4197  */
4198 Roo.lib.HSVColor = function (h, s, v, a){
4199
4200   // store the alpha component after clipping it if necessary
4201   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4202
4203   // store the HSV components after clipping or wrapping them if necessary
4204   this.hsv =
4205       {
4206         'h' : (h % 360 + 360) % 360,
4207         's' : Math.max(0, Math.min(100, s)),
4208         'v' : Math.max(0, Math.min(100, v))
4209       };
4210
4211   // initialise the RGB and HSL components to null
4212   this.rgb = null;
4213   this.hsl = null;
4214 }
4215
4216 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4217   /* Calculates and stores the RGB components of this HSVColor so that they can
4218    * be returned be the getRGB function.
4219    */
4220   calculateRGB: function ()
4221   {
4222     var hsv = this.hsv;
4223     // check whether the saturation is zero
4224     if (hsv.s == 0){
4225
4226       // set the Color to the appropriate shade of grey
4227       var r = hsv.v;
4228       var g = hsv.v;
4229       var b = hsv.v;
4230
4231     }else{
4232
4233       // set some temporary values
4234       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4235       var p  = hsv.v * (1 - hsv.s / 100);
4236       var q  = hsv.v * (1 - hsv.s / 100 * f);
4237       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4238
4239       // set the RGB Color components to their temporary values
4240       switch (Math.floor(hsv.h / 60)){
4241         case 0: var r = hsv.v; var g = t; var b = p; break;
4242         case 1: var r = q; var g = hsv.v; var b = p; break;
4243         case 2: var r = p; var g = hsv.v; var b = t; break;
4244         case 3: var r = p; var g = q; var b = hsv.v; break;
4245         case 4: var r = t; var g = p; var b = hsv.v; break;
4246         case 5: var r = hsv.v; var g = p; var b = q; break;
4247       }
4248
4249     }
4250
4251     // store the RGB components
4252     this.rgb =
4253         {
4254           'r' : r * 2.55,
4255           'g' : g * 2.55,
4256           'b' : b * 2.55
4257         };
4258
4259   },
4260
4261   /* Calculates and stores the HSL components of this HSVColor so that they can
4262    * be returned be the getHSL function.
4263    */
4264   calculateHSL : function (){
4265
4266     var hsv = this.hsv;
4267     // determine the lightness in the range [0,100]
4268     var l = (2 - hsv.s / 100) * hsv.v / 2;
4269
4270     // store the HSL components
4271     this.hsl =
4272         {
4273           'h' : hsv.h,
4274           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4275           'l' : l
4276         };
4277
4278     // correct a division-by-zero error
4279     if (isNaN(hsl.s)) { hsl.s = 0; }
4280
4281   } 
4282  
4283
4284 });
4285  
4286
4287 /**
4288  * @class Roo.lib.HSLColor
4289  * @extends Roo.lib.Color
4290  *
4291  * @constructor
4292  * Creates a Color specified in the HSL Color space, with an optional alpha
4293  * component. The parameters are:
4294  *
4295  * @param {Number} h - the hue component, wrapped to the range [0,360)
4296  * @param {Number} s - the saturation component, clipped to the range [0,100]
4297  * @param {Number} l - the lightness component, clipped to the range [0,100]
4298  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4299  *     optional and defaults to 1
4300  */
4301
4302 Roo.lib.HSLColor = function(h, s, l, a){
4303
4304   // store the alpha component after clipping it if necessary
4305   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4306
4307   // store the HSL components after clipping or wrapping them if necessary
4308   this.hsl =
4309       {
4310         'h' : (h % 360 + 360) % 360,
4311         's' : Math.max(0, Math.min(100, s)),
4312         'l' : Math.max(0, Math.min(100, l))
4313       };
4314
4315   // initialise the RGB and HSV components to null
4316 }
4317
4318 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4319
4320   /* Calculates and stores the RGB components of this HSLColor so that they can
4321    * be returned be the getRGB function.
4322    */
4323   calculateRGB: function (){
4324
4325     // check whether the saturation is zero
4326     if (this.hsl.s == 0){
4327
4328       // store the RGB components representing the appropriate shade of grey
4329       this.rgb =
4330           {
4331             'r' : this.hsl.l * 2.55,
4332             'g' : this.hsl.l * 2.55,
4333             'b' : this.hsl.l * 2.55
4334           };
4335
4336     }else{
4337
4338       // set some temporary values
4339       var p = this.hsl.l < 50
4340             ? this.hsl.l * (1 + hsl.s / 100)
4341             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4342       var q = 2 * hsl.l - p;
4343
4344       // initialise the RGB components
4345       this.rgb =
4346           {
4347             'r' : (h + 120) / 60 % 6,
4348             'g' : h / 60,
4349             'b' : (h + 240) / 60 % 6
4350           };
4351
4352       // loop over the RGB components
4353       for (var key in this.rgb){
4354
4355         // ensure that the property is not inherited from the root object
4356         if (this.rgb.hasOwnProperty(key)){
4357
4358           // set the component to its value in the range [0,100]
4359           if (this.rgb[key] < 1){
4360             this.rgb[key] = q + (p - q) * this.rgb[key];
4361           }else if (this.rgb[key] < 3){
4362             this.rgb[key] = p;
4363           }else if (this.rgb[key] < 4){
4364             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4365           }else{
4366             this.rgb[key] = q;
4367           }
4368
4369           // set the component to its value in the range [0,255]
4370           this.rgb[key] *= 2.55;
4371
4372         }
4373
4374       }
4375
4376     }
4377
4378   },
4379
4380   /* Calculates and stores the HSV components of this HSLColor so that they can
4381    * be returned be the getHSL function.
4382    */
4383    calculateHSV : function(){
4384
4385     // set a temporary value
4386     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4387
4388     // store the HSV components
4389     this.hsv =
4390         {
4391           'h' : this.hsl.h,
4392           's' : 200 * t / (this.hsl.l + t),
4393           'v' : t + this.hsl.l
4394         };
4395
4396     // correct a division-by-zero error
4397     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4398
4399   }
4400  
4401
4402 });
4403 /*
4404  * Portions of this file are based on pieces of Yahoo User Interface Library
4405  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4406  * YUI licensed under the BSD License:
4407  * http://developer.yahoo.net/yui/license.txt
4408  * <script type="text/javascript">
4409  *
4410  */
4411 (function() {
4412
4413     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4414         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4415     };
4416
4417     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4418
4419     var fly = Roo.lib.AnimBase.fly;
4420     var Y = Roo.lib;
4421     var superclass = Y.ColorAnim.superclass;
4422     var proto = Y.ColorAnim.prototype;
4423
4424     proto.toString = function() {
4425         var el = this.getEl();
4426         var id = el.id || el.tagName;
4427         return ("ColorAnim " + id);
4428     };
4429
4430     proto.patterns.color = /color$/i;
4431     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4432     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4433     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4434     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4435
4436
4437     proto.parseColor = function(s) {
4438         if (s.length == 3) {
4439             return s;
4440         }
4441
4442         var c = this.patterns.hex.exec(s);
4443         if (c && c.length == 4) {
4444             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4445         }
4446
4447         c = this.patterns.rgb.exec(s);
4448         if (c && c.length == 4) {
4449             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4450         }
4451
4452         c = this.patterns.hex3.exec(s);
4453         if (c && c.length == 4) {
4454             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4455         }
4456
4457         return null;
4458     };
4459     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4460     proto.getAttribute = function(attr) {
4461         var el = this.getEl();
4462         if (this.patterns.color.test(attr)) {
4463             var val = fly(el).getStyle(attr);
4464
4465             if (this.patterns.transparent.test(val)) {
4466                 var parent = el.parentNode;
4467                 val = fly(parent).getStyle(attr);
4468
4469                 while (parent && this.patterns.transparent.test(val)) {
4470                     parent = parent.parentNode;
4471                     val = fly(parent).getStyle(attr);
4472                     if (parent.tagName.toUpperCase() == 'HTML') {
4473                         val = '#fff';
4474                     }
4475                 }
4476             }
4477         } else {
4478             val = superclass.getAttribute.call(this, attr);
4479         }
4480
4481         return val;
4482     };
4483     proto.getAttribute = function(attr) {
4484         var el = this.getEl();
4485         if (this.patterns.color.test(attr)) {
4486             var val = fly(el).getStyle(attr);
4487
4488             if (this.patterns.transparent.test(val)) {
4489                 var parent = el.parentNode;
4490                 val = fly(parent).getStyle(attr);
4491
4492                 while (parent && this.patterns.transparent.test(val)) {
4493                     parent = parent.parentNode;
4494                     val = fly(parent).getStyle(attr);
4495                     if (parent.tagName.toUpperCase() == 'HTML') {
4496                         val = '#fff';
4497                     }
4498                 }
4499             }
4500         } else {
4501             val = superclass.getAttribute.call(this, attr);
4502         }
4503
4504         return val;
4505     };
4506
4507     proto.doMethod = function(attr, start, end) {
4508         var val;
4509
4510         if (this.patterns.color.test(attr)) {
4511             val = [];
4512             for (var i = 0, len = start.length; i < len; ++i) {
4513                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4514             }
4515
4516             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4517         }
4518         else {
4519             val = superclass.doMethod.call(this, attr, start, end);
4520         }
4521
4522         return val;
4523     };
4524
4525     proto.setRuntimeAttribute = function(attr) {
4526         superclass.setRuntimeAttribute.call(this, attr);
4527
4528         if (this.patterns.color.test(attr)) {
4529             var attributes = this.attributes;
4530             var start = this.parseColor(this.runtimeAttributes[attr].start);
4531             var end = this.parseColor(this.runtimeAttributes[attr].end);
4532
4533             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4534                 end = this.parseColor(attributes[attr].by);
4535
4536                 for (var i = 0, len = start.length; i < len; ++i) {
4537                     end[i] = start[i] + end[i];
4538                 }
4539             }
4540
4541             this.runtimeAttributes[attr].start = start;
4542             this.runtimeAttributes[attr].end = end;
4543         }
4544     };
4545 })();
4546
4547 /*
4548  * Portions of this file are based on pieces of Yahoo User Interface Library
4549  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4550  * YUI licensed under the BSD License:
4551  * http://developer.yahoo.net/yui/license.txt
4552  * <script type="text/javascript">
4553  *
4554  */
4555 Roo.lib.Easing = {
4556
4557
4558     easeNone: function (t, b, c, d) {
4559         return c * t / d + b;
4560     },
4561
4562
4563     easeIn: function (t, b, c, d) {
4564         return c * (t /= d) * t + b;
4565     },
4566
4567
4568     easeOut: function (t, b, c, d) {
4569         return -c * (t /= d) * (t - 2) + b;
4570     },
4571
4572
4573     easeBoth: function (t, b, c, d) {
4574         if ((t /= d / 2) < 1) {
4575             return c / 2 * t * t + b;
4576         }
4577
4578         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4579     },
4580
4581
4582     easeInStrong: function (t, b, c, d) {
4583         return c * (t /= d) * t * t * t + b;
4584     },
4585
4586
4587     easeOutStrong: function (t, b, c, d) {
4588         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4589     },
4590
4591
4592     easeBothStrong: function (t, b, c, d) {
4593         if ((t /= d / 2) < 1) {
4594             return c / 2 * t * t * t * t + b;
4595         }
4596
4597         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4598     },
4599
4600
4601
4602     elasticIn: function (t, b, c, d, a, p) {
4603         if (t == 0) {
4604             return b;
4605         }
4606         if ((t /= d) == 1) {
4607             return b + c;
4608         }
4609         if (!p) {
4610             p = d * .3;
4611         }
4612
4613         if (!a || a < Math.abs(c)) {
4614             a = c;
4615             var s = p / 4;
4616         }
4617         else {
4618             var s = p / (2 * Math.PI) * Math.asin(c / a);
4619         }
4620
4621         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4622     },
4623
4624
4625     elasticOut: function (t, b, c, d, a, p) {
4626         if (t == 0) {
4627             return b;
4628         }
4629         if ((t /= d) == 1) {
4630             return b + c;
4631         }
4632         if (!p) {
4633             p = d * .3;
4634         }
4635
4636         if (!a || a < Math.abs(c)) {
4637             a = c;
4638             var s = p / 4;
4639         }
4640         else {
4641             var s = p / (2 * Math.PI) * Math.asin(c / a);
4642         }
4643
4644         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4645     },
4646
4647
4648     elasticBoth: function (t, b, c, d, a, p) {
4649         if (t == 0) {
4650             return b;
4651         }
4652
4653         if ((t /= d / 2) == 2) {
4654             return b + c;
4655         }
4656
4657         if (!p) {
4658             p = d * (.3 * 1.5);
4659         }
4660
4661         if (!a || a < Math.abs(c)) {
4662             a = c;
4663             var s = p / 4;
4664         }
4665         else {
4666             var s = p / (2 * Math.PI) * Math.asin(c / a);
4667         }
4668
4669         if (t < 1) {
4670             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4671                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4672         }
4673         return a * Math.pow(2, -10 * (t -= 1)) *
4674                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4675     },
4676
4677
4678
4679     backIn: function (t, b, c, d, s) {
4680         if (typeof s == 'undefined') {
4681             s = 1.70158;
4682         }
4683         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4684     },
4685
4686
4687     backOut: function (t, b, c, d, s) {
4688         if (typeof s == 'undefined') {
4689             s = 1.70158;
4690         }
4691         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4692     },
4693
4694
4695     backBoth: function (t, b, c, d, s) {
4696         if (typeof s == 'undefined') {
4697             s = 1.70158;
4698         }
4699
4700         if ((t /= d / 2 ) < 1) {
4701             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4702         }
4703         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4704     },
4705
4706
4707     bounceIn: function (t, b, c, d) {
4708         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4709     },
4710
4711
4712     bounceOut: function (t, b, c, d) {
4713         if ((t /= d) < (1 / 2.75)) {
4714             return c * (7.5625 * t * t) + b;
4715         } else if (t < (2 / 2.75)) {
4716             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4717         } else if (t < (2.5 / 2.75)) {
4718             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4719         }
4720         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4721     },
4722
4723
4724     bounceBoth: function (t, b, c, d) {
4725         if (t < d / 2) {
4726             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4727         }
4728         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4729     }
4730 };/*
4731  * Portions of this file are based on pieces of Yahoo User Interface Library
4732  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4733  * YUI licensed under the BSD License:
4734  * http://developer.yahoo.net/yui/license.txt
4735  * <script type="text/javascript">
4736  *
4737  */
4738     (function() {
4739         Roo.lib.Motion = function(el, attributes, duration, method) {
4740             if (el) {
4741                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4742             }
4743         };
4744
4745         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4746
4747
4748         var Y = Roo.lib;
4749         var superclass = Y.Motion.superclass;
4750         var proto = Y.Motion.prototype;
4751
4752         proto.toString = function() {
4753             var el = this.getEl();
4754             var id = el.id || el.tagName;
4755             return ("Motion " + id);
4756         };
4757
4758         proto.patterns.points = /^points$/i;
4759
4760         proto.setAttribute = function(attr, val, unit) {
4761             if (this.patterns.points.test(attr)) {
4762                 unit = unit || 'px';
4763                 superclass.setAttribute.call(this, 'left', val[0], unit);
4764                 superclass.setAttribute.call(this, 'top', val[1], unit);
4765             } else {
4766                 superclass.setAttribute.call(this, attr, val, unit);
4767             }
4768         };
4769
4770         proto.getAttribute = function(attr) {
4771             if (this.patterns.points.test(attr)) {
4772                 var val = [
4773                         superclass.getAttribute.call(this, 'left'),
4774                         superclass.getAttribute.call(this, 'top')
4775                         ];
4776             } else {
4777                 val = superclass.getAttribute.call(this, attr);
4778             }
4779
4780             return val;
4781         };
4782
4783         proto.doMethod = function(attr, start, end) {
4784             var val = null;
4785
4786             if (this.patterns.points.test(attr)) {
4787                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4788                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4789             } else {
4790                 val = superclass.doMethod.call(this, attr, start, end);
4791             }
4792             return val;
4793         };
4794
4795         proto.setRuntimeAttribute = function(attr) {
4796             if (this.patterns.points.test(attr)) {
4797                 var el = this.getEl();
4798                 var attributes = this.attributes;
4799                 var start;
4800                 var control = attributes['points']['control'] || [];
4801                 var end;
4802                 var i, len;
4803
4804                 if (control.length > 0 && !(control[0] instanceof Array)) {
4805                     control = [control];
4806                 } else {
4807                     var tmp = [];
4808                     for (i = 0,len = control.length; i < len; ++i) {
4809                         tmp[i] = control[i];
4810                     }
4811                     control = tmp;
4812                 }
4813
4814                 Roo.fly(el).position();
4815
4816                 if (isset(attributes['points']['from'])) {
4817                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4818                 }
4819                 else {
4820                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4821                 }
4822
4823                 start = this.getAttribute('points');
4824
4825
4826                 if (isset(attributes['points']['to'])) {
4827                     end = translateValues.call(this, attributes['points']['to'], start);
4828
4829                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4830                     for (i = 0,len = control.length; i < len; ++i) {
4831                         control[i] = translateValues.call(this, control[i], start);
4832                     }
4833
4834
4835                 } else if (isset(attributes['points']['by'])) {
4836                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4837
4838                     for (i = 0,len = control.length; i < len; ++i) {
4839                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4840                     }
4841                 }
4842
4843                 this.runtimeAttributes[attr] = [start];
4844
4845                 if (control.length > 0) {
4846                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4847                 }
4848
4849                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4850             }
4851             else {
4852                 superclass.setRuntimeAttribute.call(this, attr);
4853             }
4854         };
4855
4856         var translateValues = function(val, start) {
4857             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4858             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4859
4860             return val;
4861         };
4862
4863         var isset = function(prop) {
4864             return (typeof prop !== 'undefined');
4865         };
4866     })();
4867 /*
4868  * Portions of this file are based on pieces of Yahoo User Interface Library
4869  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4870  * YUI licensed under the BSD License:
4871  * http://developer.yahoo.net/yui/license.txt
4872  * <script type="text/javascript">
4873  *
4874  */
4875     (function() {
4876         Roo.lib.Scroll = function(el, attributes, duration, method) {
4877             if (el) {
4878                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4879             }
4880         };
4881
4882         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4883
4884
4885         var Y = Roo.lib;
4886         var superclass = Y.Scroll.superclass;
4887         var proto = Y.Scroll.prototype;
4888
4889         proto.toString = function() {
4890             var el = this.getEl();
4891             var id = el.id || el.tagName;
4892             return ("Scroll " + id);
4893         };
4894
4895         proto.doMethod = function(attr, start, end) {
4896             var val = null;
4897
4898             if (attr == 'scroll') {
4899                 val = [
4900                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4901                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4902                         ];
4903
4904             } else {
4905                 val = superclass.doMethod.call(this, attr, start, end);
4906             }
4907             return val;
4908         };
4909
4910         proto.getAttribute = function(attr) {
4911             var val = null;
4912             var el = this.getEl();
4913
4914             if (attr == 'scroll') {
4915                 val = [ el.scrollLeft, el.scrollTop ];
4916             } else {
4917                 val = superclass.getAttribute.call(this, attr);
4918             }
4919
4920             return val;
4921         };
4922
4923         proto.setAttribute = function(attr, val, unit) {
4924             var el = this.getEl();
4925
4926             if (attr == 'scroll') {
4927                 el.scrollLeft = val[0];
4928                 el.scrollTop = val[1];
4929             } else {
4930                 superclass.setAttribute.call(this, attr, val, unit);
4931             }
4932         };
4933     })();
4934 /**
4935  * Originally based of this code... - refactored for Roo...
4936  * https://github.com/aaalsaleh/undo-manager
4937  
4938  * undo-manager.js
4939  * @author  Abdulrahman Alsaleh 
4940  * @copyright 2015 Abdulrahman Alsaleh 
4941  * @license  MIT License (c) 
4942  *
4943  * Hackily modifyed by alan@roojs.com
4944  *
4945  *
4946  *  
4947  *
4948  *  TOTALLY UNTESTED...
4949  *
4950  *  Documentation to be done....
4951  */
4952  
4953
4954 /**
4955 * @class Roo.lib.UndoManager
4956 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4957 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4958
4959  * Usage:
4960  * <pre><code>
4961
4962
4963 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4964  
4965 </code></pre>
4966
4967 * For more information see this blog post with examples:
4968 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4969      - Create Elements using DOM, HTML fragments and Templates</a>. 
4970 * @constructor
4971 * @param {Number} limit how far back to go ... use 1000?
4972 * @param {Object} scope usually use document..
4973 */
4974
4975 Roo.lib.UndoManager = function (limit, undoScopeHost)
4976 {
4977     this.stack = [];
4978     this.limit = limit;
4979     this.scope = undoScopeHost;
4980     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4981     if (this.fireEvent) {
4982         this.bindEvents();
4983     }
4984     this.reset();
4985     
4986 };
4987         
4988 Roo.lib.UndoManager.prototype = {
4989     
4990     limit : false,
4991     stack : false,
4992     scope :  false,
4993     fireEvent : false,
4994     position : 0,
4995     length : 0,
4996     
4997     
4998      /**
4999      * To push and execute a transaction, the method undoManager.transact
5000      * must be called by passing a transaction object as the first argument, and a merge
5001      * flag as the second argument. A transaction object has the following properties:
5002      *
5003      * Usage:
5004 <pre><code>
5005 undoManager.transact({
5006     label: 'Typing',
5007     execute: function() { ... },
5008     undo: function() { ... },
5009     // redo same as execute
5010     redo: function() { this.execute(); }
5011 }, false);
5012
5013 // merge transaction
5014 undoManager.transact({
5015     label: 'Typing',
5016     execute: function() { ... },  // this will be run...
5017     undo: function() { ... }, // what to do when undo is run.
5018     // redo same as execute
5019     redo: function() { this.execute(); }
5020 }, true); 
5021 </code></pre> 
5022      *
5023      * 
5024      * @param {Object} transaction The transaction to add to the stack.
5025      * @return {String} The HTML fragment
5026      */
5027     
5028     
5029     transact : function (transaction, merge)
5030     {
5031         if (arguments.length < 2) {
5032             throw new TypeError('Not enough arguments to UndoManager.transact.');
5033         }
5034
5035         transaction.execute();
5036
5037         this.stack.splice(0, this.position);
5038         if (merge && this.length) {
5039             this.stack[0].push(transaction);
5040         } else {
5041             this.stack.unshift([transaction]);
5042         }
5043     
5044         this.position = 0;
5045
5046         if (this.limit && this.stack.length > this.limit) {
5047             this.length = this.stack.length = this.limit;
5048         } else {
5049             this.length = this.stack.length;
5050         }
5051
5052         if (this.fireEvent) {
5053             this.scope.dispatchEvent(
5054                 new CustomEvent('DOMTransaction', {
5055                     detail: {
5056                         transactions: this.stack[0].slice()
5057                     },
5058                     bubbles: true,
5059                     cancelable: false
5060                 })
5061             );
5062         }
5063         
5064         //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5065       
5066         
5067     },
5068
5069     undo : function ()
5070     {
5071         //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5072         
5073         if (this.position < this.length) {
5074             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5075                 this.stack[this.position][i].undo();
5076             }
5077             this.position++;
5078
5079             if (this.fireEvent) {
5080                 this.scope.dispatchEvent(
5081                     new CustomEvent('undo', {
5082                         detail: {
5083                             transactions: this.stack[this.position - 1].slice()
5084                         },
5085                         bubbles: true,
5086                         cancelable: false
5087                     })
5088                 );
5089             }
5090         }
5091     },
5092
5093     redo : function ()
5094     {
5095         if (this.position > 0) {
5096             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5097                 this.stack[this.position - 1][i].redo();
5098             }
5099             this.position--;
5100
5101             if (this.fireEvent) {
5102                 this.scope.dispatchEvent(
5103                     new CustomEvent('redo', {
5104                         detail: {
5105                             transactions: this.stack[this.position].slice()
5106                         },
5107                         bubbles: true,
5108                         cancelable: false
5109                     })
5110                 );
5111             }
5112         }
5113     },
5114
5115     item : function (index)
5116     {
5117         if (index >= 0 && index < this.length) {
5118             return this.stack[index].slice();
5119         }
5120         return null;
5121     },
5122
5123     clearUndo : function () {
5124         this.stack.length = this.length = this.position;
5125     },
5126
5127     clearRedo : function () {
5128         this.stack.splice(0, this.position);
5129         this.position = 0;
5130         this.length = this.stack.length;
5131     },
5132     /**
5133      * Reset the undo - probaly done on load to clear all history.
5134      */
5135     reset : function()
5136     {
5137         this.stack = [];
5138         this.position = 0;
5139         this.length = 0;
5140         this.current_html = this.scope.innerHTML;
5141         if (this.timer !== false) {
5142             clearTimeout(this.timer);
5143         }
5144         this.timer = false;
5145         this.merge = false;
5146         this.addEvent();
5147         
5148     },
5149     current_html : '',
5150     timer : false,
5151     merge : false,
5152     
5153     
5154     // this will handle the undo/redo on the element.?
5155     bindEvents : function()
5156     {
5157         var el  = this.scope;
5158         el.undoManager = this;
5159         
5160         
5161         this.scope.addEventListener('keydown', function(e) {
5162             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5163                 if (e.shiftKey) {
5164                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5165                 } else {
5166                     el.undoManager.undo(); // Ctrl/Command + Z
5167                 }
5168         
5169                 e.preventDefault();
5170             }
5171         });
5172         /// ignore keyup..
5173         this.scope.addEventListener('keyup', function(e) {
5174             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5175                 e.preventDefault();
5176             }
5177         });
5178         
5179         
5180         
5181         var t = this;
5182         
5183         el.addEventListener('input', function(e) {
5184             if(el.innerHTML == t.current_html) {
5185                 return;
5186             }
5187             // only record events every second.
5188             if (t.timer !== false) {
5189                clearTimeout(t.timer);
5190                t.timer = false;
5191             }
5192             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5193             
5194             t.addEvent(t.merge);
5195             t.merge = true; // ignore changes happening every second..
5196         });
5197         },
5198     /**
5199      * Manually add an event.
5200      * Normall called without arguements - and it will just get added to the stack.
5201      * 
5202      */
5203     
5204     addEvent : function(merge)
5205     {
5206         //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5207         // not sure if this should clear the timer 
5208         merge = typeof(merge) == 'undefined' ? false : merge; 
5209         
5210         this.scope.undoManager.transact({
5211             scope : this.scope,
5212             oldHTML: this.current_html,
5213             newHTML: this.scope.innerHTML,
5214             // nothing to execute (content already changed when input is fired)
5215             execute: function() { },
5216             undo: function() {
5217                 this.scope.innerHTML = this.current_html = this.oldHTML;
5218             },
5219             redo: function() {
5220                 this.scope.innerHTML = this.current_html = this.newHTML;
5221             }
5222         }, false); //merge);
5223         
5224         this.merge = merge;
5225         
5226         this.current_html = this.scope.innerHTML;
5227     }
5228     
5229     
5230      
5231     
5232     
5233     
5234 };
5235 /**
5236  * @class Roo.lib.Range
5237  * @constructor
5238  * This is a toolkit, normally used to copy features into a Dom Range element
5239  * Roo.lib.Range.wrap(x);
5240  *
5241  *
5242  *
5243  */
5244 Roo.lib.Range = function() { };
5245
5246 /**
5247  * Wrap a Dom Range object, to give it new features...
5248  * @static
5249  * @param {Range} the range to wrap
5250  */
5251 Roo.lib.Range.wrap = function(r) {
5252     return Roo.apply(r, Roo.lib.Range.prototype);
5253 };
5254 /**
5255  * find a parent node eg. LI / OL
5256  * @param {string|Array} node name or array of nodenames
5257  * @return {DomElement|false}
5258  */
5259 Roo.apply(Roo.lib.Range.prototype,
5260 {
5261     
5262     closest : function(str)
5263     {
5264         if (typeof(str) != 'string') {
5265             // assume it's a array.
5266             for(var i = 0;i < str.length;i++) {
5267                 var r = this.closest(str[i]);
5268                 if (r !== false) {
5269                     return r;
5270                 }
5271                 
5272             }
5273             return false;
5274         }
5275         str = str.toLowerCase();
5276         var n = this.commonAncestorContainer; // might not be a node
5277         while (n.nodeType != 1) {
5278             n = n.parentNode;
5279         }
5280         
5281         if (n.nodeName.toLowerCase() == str ) {
5282             return n;
5283         }
5284         if (n.nodeName.toLowerCase() == 'body') {
5285             return false;
5286         }
5287             
5288         return n.closest(str) || false;
5289         
5290     },
5291     cloneRange : function()
5292     {
5293         return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
5294     }
5295 });/**
5296  * @class Roo.lib.Selection
5297  * @constructor
5298  * This is a toolkit, normally used to copy features into a Dom Selection element
5299  * Roo.lib.Selection.wrap(x);
5300  *
5301  *
5302  *
5303  */
5304 Roo.lib.Selection = function() { };
5305
5306 /**
5307  * Wrap a Dom Range object, to give it new features...
5308  * @static
5309  * @param {Range} the range to wrap
5310  */
5311 Roo.lib.Selection.wrap = function(r, doc) {
5312     Roo.apply(r, Roo.lib.Selection.prototype);
5313     r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
5314     return r;
5315 };
5316 /**
5317  * find a parent node eg. LI / OL
5318  * @param {string|Array} node name or array of nodenames
5319  * @return {DomElement|false}
5320  */
5321 Roo.apply(Roo.lib.Selection.prototype,
5322 {
5323     /**
5324      * the owner document
5325      */
5326     ownerDocument : false,
5327     
5328     getRangeAt : function(n)
5329     {
5330         return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
5331     },
5332     
5333     /**
5334      * insert node at selection 
5335      * @param {DomElement|string} node
5336      * @param {string} cursor (after|in|none) where to place the cursor after inserting.
5337      */
5338     insertNode: function(node, cursor)
5339     {
5340         if (typeof(node) == 'string') {
5341             node = this.ownerDocument.createElement(node);
5342             if (cursor == 'in') {
5343                 node.innerHTML = '&nbsp;';
5344             }
5345         }
5346         
5347         var range = this.getRangeAt(0);
5348         
5349         if (this.type != 'Caret') {
5350             range.deleteContents();
5351         }
5352         var sn = node.childNodes[0]; // select the contents.
5353
5354         
5355         
5356         range.insertNode(node);
5357         if (cursor == 'after') {
5358             node.insertAdjacentHTML('afterend', '&nbsp;');
5359             sn = node.nextSibling;
5360         }
5361         
5362         if (cursor == 'none') {
5363             return;
5364         }
5365         
5366         this.cursorText(sn);
5367     },
5368     
5369     cursorText : function(n)
5370     {
5371        
5372         //var range = this.getRangeAt(0);
5373         range = Roo.lib.Range.wrap(new Range());
5374         //range.selectNode(n);
5375         
5376         var ix = Array.from(n.parentNode.childNodes).indexOf(n);
5377         range.setStart(n.parentNode,ix);
5378         range.setEnd(n.parentNode,ix+1);
5379         //range.collapse(false);
5380          
5381         this.removeAllRanges();
5382         this.addRange(range);
5383         
5384         Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
5385     },
5386     cursorAfter : function(n)
5387     {
5388         if (!n.nextSibling || n.nextSibling.nodeValue != '&nbsp;') {
5389             n.insertAdjacentHTML('afterend', '&nbsp;');
5390         }
5391         this.cursorText (n.nextSibling);
5392     }
5393         
5394     
5395 });/*
5396  * Based on:
5397  * Ext JS Library 1.1.1
5398  * Copyright(c) 2006-2007, Ext JS, LLC.
5399  *
5400  * Originally Released Under LGPL - original licence link has changed is not relivant.
5401  *
5402  * Fork - LGPL
5403  * <script type="text/javascript">
5404  */
5405
5406
5407 // nasty IE9 hack - what a pile of crap that is..
5408
5409  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5410     Range.prototype.createContextualFragment = function (html) {
5411         var doc = window.document;
5412         var container = doc.createElement("div");
5413         container.innerHTML = html;
5414         var frag = doc.createDocumentFragment(), n;
5415         while ((n = container.firstChild)) {
5416             frag.appendChild(n);
5417         }
5418         return frag;
5419     };
5420 }
5421
5422 /**
5423  * @class Roo.DomHelper
5424  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5425  * 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>.
5426  * @static
5427  */
5428 Roo.DomHelper = function(){
5429     var tempTableEl = null;
5430     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5431     var tableRe = /^table|tbody|tr|td$/i;
5432     var xmlns = {};
5433     // build as innerHTML where available
5434     /** @ignore */
5435     var createHtml = function(o){
5436         if(typeof o == 'string'){
5437             return o;
5438         }
5439         var b = "";
5440         if(!o.tag){
5441             o.tag = "div";
5442         }
5443         b += "<" + o.tag;
5444         for(var attr in o){
5445             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5446             if(attr == "style"){
5447                 var s = o["style"];
5448                 if(typeof s == "function"){
5449                     s = s.call();
5450                 }
5451                 if(typeof s == "string"){
5452                     b += ' style="' + s + '"';
5453                 }else if(typeof s == "object"){
5454                     b += ' style="';
5455                     for(var key in s){
5456                         if(typeof s[key] != "function"){
5457                             b += key + ":" + s[key] + ";";
5458                         }
5459                     }
5460                     b += '"';
5461                 }
5462             }else{
5463                 if(attr == "cls"){
5464                     b += ' class="' + o["cls"] + '"';
5465                 }else if(attr == "htmlFor"){
5466                     b += ' for="' + o["htmlFor"] + '"';
5467                 }else{
5468                     b += " " + attr + '="' + o[attr] + '"';
5469                 }
5470             }
5471         }
5472         if(emptyTags.test(o.tag)){
5473             b += "/>";
5474         }else{
5475             b += ">";
5476             var cn = o.children || o.cn;
5477             if(cn){
5478                 //http://bugs.kde.org/show_bug.cgi?id=71506
5479                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5480                     for(var i = 0, len = cn.length; i < len; i++) {
5481                         b += createHtml(cn[i], b);
5482                     }
5483                 }else{
5484                     b += createHtml(cn, b);
5485                 }
5486             }
5487             if(o.html){
5488                 b += o.html;
5489             }
5490             b += "</" + o.tag + ">";
5491         }
5492         return b;
5493     };
5494
5495     // build as dom
5496     /** @ignore */
5497     var createDom = function(o, parentNode){
5498          
5499         // defininition craeted..
5500         var ns = false;
5501         if (o.ns && o.ns != 'html') {
5502                
5503             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5504                 xmlns[o.ns] = o.xmlns;
5505                 ns = o.xmlns;
5506             }
5507             if (typeof(xmlns[o.ns]) == 'undefined') {
5508                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5509             }
5510             ns = xmlns[o.ns];
5511         }
5512         
5513         
5514         if (typeof(o) == 'string') {
5515             return parentNode.appendChild(document.createTextNode(o));
5516         }
5517         o.tag = o.tag || div;
5518         if (o.ns && Roo.isIE) {
5519             ns = false;
5520             o.tag = o.ns + ':' + o.tag;
5521             
5522         }
5523         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5524         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5525         for(var attr in o){
5526             
5527             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5528                     attr == "style" || typeof o[attr] == "function") { continue; }
5529                     
5530             if(attr=="cls" && Roo.isIE){
5531                 el.className = o["cls"];
5532             }else{
5533                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5534                 else { 
5535                     el[attr] = o[attr];
5536                 }
5537             }
5538         }
5539         Roo.DomHelper.applyStyles(el, o.style);
5540         var cn = o.children || o.cn;
5541         if(cn){
5542             //http://bugs.kde.org/show_bug.cgi?id=71506
5543              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5544                 for(var i = 0, len = cn.length; i < len; i++) {
5545                     createDom(cn[i], el);
5546                 }
5547             }else{
5548                 createDom(cn, el);
5549             }
5550         }
5551         if(o.html){
5552             el.innerHTML = o.html;
5553         }
5554         if(parentNode){
5555            parentNode.appendChild(el);
5556         }
5557         return el;
5558     };
5559
5560     var ieTable = function(depth, s, h, e){
5561         tempTableEl.innerHTML = [s, h, e].join('');
5562         var i = -1, el = tempTableEl;
5563         while(++i < depth && el.firstChild){
5564             el = el.firstChild;
5565         }
5566         return el;
5567     };
5568
5569     // kill repeat to save bytes
5570     var ts = '<table>',
5571         te = '</table>',
5572         tbs = ts+'<tbody>',
5573         tbe = '</tbody>'+te,
5574         trs = tbs + '<tr>',
5575         tre = '</tr>'+tbe;
5576
5577     /**
5578      * @ignore
5579      * Nasty code for IE's broken table implementation
5580      */
5581     var insertIntoTable = function(tag, where, el, html){
5582         if(!tempTableEl){
5583             tempTableEl = document.createElement('div');
5584         }
5585         var node;
5586         var before = null;
5587         if(tag == 'td'){
5588             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5589                 return;
5590             }
5591             if(where == 'beforebegin'){
5592                 before = el;
5593                 el = el.parentNode;
5594             } else{
5595                 before = el.nextSibling;
5596                 el = el.parentNode;
5597             }
5598             node = ieTable(4, trs, html, tre);
5599         }
5600         else if(tag == 'tr'){
5601             if(where == 'beforebegin'){
5602                 before = el;
5603                 el = el.parentNode;
5604                 node = ieTable(3, tbs, html, tbe);
5605             } else if(where == 'afterend'){
5606                 before = el.nextSibling;
5607                 el = el.parentNode;
5608                 node = ieTable(3, tbs, html, tbe);
5609             } else{ // INTO a TR
5610                 if(where == 'afterbegin'){
5611                     before = el.firstChild;
5612                 }
5613                 node = ieTable(4, trs, html, tre);
5614             }
5615         } else if(tag == 'tbody'){
5616             if(where == 'beforebegin'){
5617                 before = el;
5618                 el = el.parentNode;
5619                 node = ieTable(2, ts, html, te);
5620             } else if(where == 'afterend'){
5621                 before = el.nextSibling;
5622                 el = el.parentNode;
5623                 node = ieTable(2, ts, html, te);
5624             } else{
5625                 if(where == 'afterbegin'){
5626                     before = el.firstChild;
5627                 }
5628                 node = ieTable(3, tbs, html, tbe);
5629             }
5630         } else{ // TABLE
5631             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5632                 return;
5633             }
5634             if(where == 'afterbegin'){
5635                 before = el.firstChild;
5636             }
5637             node = ieTable(2, ts, html, te);
5638         }
5639         el.insertBefore(node, before);
5640         return node;
5641     };
5642     
5643     // this is a bit like the react update code...
5644     // 
5645     
5646     var updateNode = function(from, to)
5647     {
5648         // should we handle non-standard elements?
5649         Roo.log(["UpdateNode" , from, to]);
5650         if (from.nodeType != to.nodeType) {
5651             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5652             from.parentNode.replaceChild(to, from);
5653         }
5654         
5655         if (from.nodeType == 3) {
5656             // assume it's text?!
5657             if (from.data == to.data) {
5658                 return;
5659             }
5660             from.data = to.data;
5661             return;
5662         }
5663         if (!from.parentNode) {
5664             // not sure why this is happening?
5665             return;
5666         }
5667         // assume 'to' doesnt have '1/3 nodetypes!
5668         // not sure why, by from, parent node might not exist?
5669         if (from.nodeType !=1 || from.tagName != to.tagName) {
5670             Roo.log(["ReplaceChild" , from, to ]);
5671             
5672             from.parentNode.replaceChild(to, from);
5673             return;
5674         }
5675         // compare attributes
5676         var ar = Array.from(from.attributes);
5677         for(var i = 0; i< ar.length;i++) {
5678             if (to.hasAttribute(ar[i].name)) {
5679                 continue;
5680             }
5681             if (ar[i].name == 'id') { // always keep ids?
5682                continue;
5683             }
5684             //if (ar[i].name == 'style') {
5685             //   throw "style removed?";
5686             //}
5687             Roo.log("removeAttribute" + ar[i].name);
5688             from.removeAttribute(ar[i].name);
5689         }
5690         ar = to.attributes;
5691         for(var i = 0; i< ar.length;i++) {
5692             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5693                 Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
5694                 continue;
5695             }
5696             Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5697             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5698         }
5699         // children
5700         var far = Array.from(from.childNodes);
5701         var tar = Array.from(to.childNodes);
5702         // if the lengths are different.. then it's probably a editable content change, rather than
5703         // a change of the block definition..
5704         
5705         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5706          /*if (from.innerHTML == to.innerHTML) {
5707             return;
5708         }
5709         if (far.length != tar.length) {
5710             from.innerHTML = to.innerHTML;
5711             return;
5712         }
5713         */
5714         
5715         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5716             if (i >= far.length) {
5717                 from.appendChild(tar[i]);
5718                 Roo.log(["add", tar[i]]);
5719                 
5720             } else if ( i  >= tar.length) {
5721                 from.removeChild(far[i]);
5722                 Roo.log(["remove", far[i]]);
5723             } else {
5724                 
5725                 updateNode(far[i], tar[i]);
5726             }    
5727         }
5728         
5729         
5730         
5731         
5732     };
5733     
5734     
5735
5736     return {
5737         /** True to force the use of DOM instead of html fragments @type Boolean */
5738         useDom : false,
5739     
5740         /**
5741          * Returns the markup for the passed Element(s) config
5742          * @param {Object} o The Dom object spec (and children)
5743          * @return {String}
5744          */
5745         markup : function(o){
5746             return createHtml(o);
5747         },
5748     
5749         /**
5750          * Applies a style specification to an element
5751          * @param {String/HTMLElement} el The element to apply styles to
5752          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5753          * a function which returns such a specification.
5754          */
5755         applyStyles : function(el, styles){
5756             if(styles){
5757                el = Roo.fly(el);
5758                if(typeof styles == "string"){
5759                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5760                    var matches;
5761                    while ((matches = re.exec(styles)) != null){
5762                        el.setStyle(matches[1], matches[2]);
5763                    }
5764                }else if (typeof styles == "object"){
5765                    for (var style in styles){
5766                       el.setStyle(style, styles[style]);
5767                    }
5768                }else if (typeof styles == "function"){
5769                     Roo.DomHelper.applyStyles(el, styles.call());
5770                }
5771             }
5772         },
5773     
5774         /**
5775          * Inserts an HTML fragment into the Dom
5776          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5777          * @param {HTMLElement} el The context element
5778          * @param {String} html The HTML fragmenet
5779          * @return {HTMLElement} The new node
5780          */
5781         insertHtml : function(where, el, html){
5782             where = where.toLowerCase();
5783             if(el.insertAdjacentHTML){
5784                 if(tableRe.test(el.tagName)){
5785                     var rs;
5786                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5787                         return rs;
5788                     }
5789                 }
5790                 switch(where){
5791                     case "beforebegin":
5792                         el.insertAdjacentHTML('BeforeBegin', html);
5793                         return el.previousSibling;
5794                     case "afterbegin":
5795                         el.insertAdjacentHTML('AfterBegin', html);
5796                         return el.firstChild;
5797                     case "beforeend":
5798                         el.insertAdjacentHTML('BeforeEnd', html);
5799                         return el.lastChild;
5800                     case "afterend":
5801                         el.insertAdjacentHTML('AfterEnd', html);
5802                         return el.nextSibling;
5803                 }
5804                 throw 'Illegal insertion point -> "' + where + '"';
5805             }
5806             var range = el.ownerDocument.createRange();
5807             var frag;
5808             switch(where){
5809                  case "beforebegin":
5810                     range.setStartBefore(el);
5811                     frag = range.createContextualFragment(html);
5812                     el.parentNode.insertBefore(frag, el);
5813                     return el.previousSibling;
5814                  case "afterbegin":
5815                     if(el.firstChild){
5816                         range.setStartBefore(el.firstChild);
5817                         frag = range.createContextualFragment(html);
5818                         el.insertBefore(frag, el.firstChild);
5819                         return el.firstChild;
5820                     }else{
5821                         el.innerHTML = html;
5822                         return el.firstChild;
5823                     }
5824                 case "beforeend":
5825                     if(el.lastChild){
5826                         range.setStartAfter(el.lastChild);
5827                         frag = range.createContextualFragment(html);
5828                         el.appendChild(frag);
5829                         return el.lastChild;
5830                     }else{
5831                         el.innerHTML = html;
5832                         return el.lastChild;
5833                     }
5834                 case "afterend":
5835                     range.setStartAfter(el);
5836                     frag = range.createContextualFragment(html);
5837                     el.parentNode.insertBefore(frag, el.nextSibling);
5838                     return el.nextSibling;
5839                 }
5840                 throw 'Illegal insertion point -> "' + where + '"';
5841         },
5842     
5843         /**
5844          * Creates new Dom element(s) and inserts them before el
5845          * @param {String/HTMLElement/Element} el The context element
5846          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5847          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5848          * @return {HTMLElement/Roo.Element} The new node
5849          */
5850         insertBefore : function(el, o, returnElement){
5851             return this.doInsert(el, o, returnElement, "beforeBegin");
5852         },
5853     
5854         /**
5855          * Creates new Dom element(s) and inserts them after el
5856          * @param {String/HTMLElement/Element} el The context element
5857          * @param {Object} o The Dom object spec (and children)
5858          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5859          * @return {HTMLElement/Roo.Element} The new node
5860          */
5861         insertAfter : function(el, o, returnElement){
5862             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5863         },
5864     
5865         /**
5866          * Creates new Dom element(s) and inserts them as the first child of el
5867          * @param {String/HTMLElement/Element} el The context element
5868          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5869          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5870          * @return {HTMLElement/Roo.Element} The new node
5871          */
5872         insertFirst : function(el, o, returnElement){
5873             return this.doInsert(el, o, returnElement, "afterBegin");
5874         },
5875     
5876         // private
5877         doInsert : function(el, o, returnElement, pos, sibling){
5878             el = Roo.getDom(el);
5879             var newNode;
5880             if(this.useDom || o.ns){
5881                 newNode = createDom(o, null);
5882                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5883             }else{
5884                 var html = createHtml(o);
5885                 newNode = this.insertHtml(pos, el, html);
5886             }
5887             return returnElement ? Roo.get(newNode, true) : newNode;
5888         },
5889     
5890         /**
5891          * Creates new Dom element(s) and appends them to el
5892          * @param {String/HTMLElement/Element} el The context element
5893          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5894          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5895          * @return {HTMLElement/Roo.Element} The new node
5896          */
5897         append : function(el, o, returnElement){
5898             el = Roo.getDom(el);
5899             var newNode;
5900             if(this.useDom || o.ns){
5901                 newNode = createDom(o, null);
5902                 el.appendChild(newNode);
5903             }else{
5904                 var html = createHtml(o);
5905                 newNode = this.insertHtml("beforeEnd", el, html);
5906             }
5907             return returnElement ? Roo.get(newNode, true) : newNode;
5908         },
5909     
5910         /**
5911          * Creates new Dom element(s) and overwrites the contents of el with them
5912          * @param {String/HTMLElement/Element} el The context element
5913          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5914          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5915          * @return {HTMLElement/Roo.Element} The new node
5916          */
5917         overwrite : function(el, o, returnElement)
5918         {
5919             el = Roo.getDom(el);
5920             if (o.ns) {
5921               
5922                 while (el.childNodes.length) {
5923                     el.removeChild(el.firstChild);
5924                 }
5925                 createDom(o, el);
5926             } else {
5927                 el.innerHTML = createHtml(o);   
5928             }
5929             
5930             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5931         },
5932     
5933         /**
5934          * Creates a new Roo.DomHelper.Template from the Dom object spec
5935          * @param {Object} o The Dom object spec (and children)
5936          * @return {Roo.DomHelper.Template} The new template
5937          */
5938         createTemplate : function(o){
5939             var html = createHtml(o);
5940             return new Roo.Template(html);
5941         },
5942          /**
5943          * Updates the first element with the spec from the o (replacing if necessary)
5944          * This iterates through the children, and updates attributes / children etc..
5945          * @param {String/HTMLElement/Element} el The context element
5946          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5947          */
5948         
5949         update : function(el, o)
5950         {
5951             updateNode(Roo.getDom(el), createDom(o));
5952             
5953         }
5954         
5955         
5956     };
5957 }();
5958 /*
5959  * Based on:
5960  * Ext JS Library 1.1.1
5961  * Copyright(c) 2006-2007, Ext JS, LLC.
5962  *
5963  * Originally Released Under LGPL - original licence link has changed is not relivant.
5964  *
5965  * Fork - LGPL
5966  * <script type="text/javascript">
5967  */
5968  
5969 /**
5970 * @class Roo.Template
5971 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5972 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5973 * Usage:
5974 <pre><code>
5975 var t = new Roo.Template({
5976     html :  '&lt;div name="{id}"&gt;' + 
5977         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5978         '&lt;/div&gt;',
5979     myformat: function (value, allValues) {
5980         return 'XX' + value;
5981     }
5982 });
5983 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5984 </code></pre>
5985 * For more information see this blog post with examples:
5986 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5987      - Create Elements using DOM, HTML fragments and Templates</a>. 
5988 * @constructor
5989 * @param {Object} cfg - Configuration object.
5990 */
5991 Roo.Template = function(cfg){
5992     // BC!
5993     if(cfg instanceof Array){
5994         cfg = cfg.join("");
5995     }else if(arguments.length > 1){
5996         cfg = Array.prototype.join.call(arguments, "");
5997     }
5998     
5999     
6000     if (typeof(cfg) == 'object') {
6001         Roo.apply(this,cfg)
6002     } else {
6003         // bc
6004         this.html = cfg;
6005     }
6006     if (this.url) {
6007         this.load();
6008     }
6009     
6010 };
6011 Roo.Template.prototype = {
6012     
6013     /**
6014      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
6015      */
6016     onLoad : false,
6017     
6018     
6019     /**
6020      * @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..
6021      *                    it should be fixed so that template is observable...
6022      */
6023     url : false,
6024     /**
6025      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
6026      */
6027     html : '',
6028     
6029     
6030     compiled : false,
6031     loaded : false,
6032     /**
6033      * Returns an HTML fragment of this template with the specified values applied.
6034      * @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'})
6035      * @return {String} The HTML fragment
6036      */
6037     
6038    
6039     
6040     applyTemplate : function(values){
6041         //Roo.log(["applyTemplate", values]);
6042         try {
6043            
6044             if(this.compiled){
6045                 return this.compiled(values);
6046             }
6047             var useF = this.disableFormats !== true;
6048             var fm = Roo.util.Format, tpl = this;
6049             var fn = function(m, name, format, args){
6050                 if(format && useF){
6051                     if(format.substr(0, 5) == "this."){
6052                         return tpl.call(format.substr(5), values[name], values);
6053                     }else{
6054                         if(args){
6055                             // quoted values are required for strings in compiled templates, 
6056                             // but for non compiled we need to strip them
6057                             // quoted reversed for jsmin
6058                             var re = /^\s*['"](.*)["']\s*$/;
6059                             args = args.split(',');
6060                             for(var i = 0, len = args.length; i < len; i++){
6061                                 args[i] = args[i].replace(re, "$1");
6062                             }
6063                             args = [values[name]].concat(args);
6064                         }else{
6065                             args = [values[name]];
6066                         }
6067                         return fm[format].apply(fm, args);
6068                     }
6069                 }else{
6070                     return values[name] !== undefined ? values[name] : "";
6071                 }
6072             };
6073             return this.html.replace(this.re, fn);
6074         } catch (e) {
6075             Roo.log(e);
6076             throw e;
6077         }
6078          
6079     },
6080     
6081     loading : false,
6082       
6083     load : function ()
6084     {
6085          
6086         if (this.loading) {
6087             return;
6088         }
6089         var _t = this;
6090         
6091         this.loading = true;
6092         this.compiled = false;
6093         
6094         var cx = new Roo.data.Connection();
6095         cx.request({
6096             url : this.url,
6097             method : 'GET',
6098             success : function (response) {
6099                 _t.loading = false;
6100                 _t.url = false;
6101                 
6102                 _t.set(response.responseText,true);
6103                 _t.loaded = true;
6104                 if (_t.onLoad) {
6105                     _t.onLoad();
6106                 }
6107              },
6108             failure : function(response) {
6109                 Roo.log("Template failed to load from " + _t.url);
6110                 _t.loading = false;
6111             }
6112         });
6113     },
6114
6115     /**
6116      * Sets the HTML used as the template and optionally compiles it.
6117      * @param {String} html
6118      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
6119      * @return {Roo.Template} this
6120      */
6121     set : function(html, compile){
6122         this.html = html;
6123         this.compiled = false;
6124         if(compile){
6125             this.compile();
6126         }
6127         return this;
6128     },
6129     
6130     /**
6131      * True to disable format functions (defaults to false)
6132      * @type Boolean
6133      */
6134     disableFormats : false,
6135     
6136     /**
6137     * The regular expression used to match template variables 
6138     * @type RegExp
6139     * @property 
6140     */
6141     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
6142     
6143     /**
6144      * Compiles the template into an internal function, eliminating the RegEx overhead.
6145      * @return {Roo.Template} this
6146      */
6147     compile : function(){
6148         var fm = Roo.util.Format;
6149         var useF = this.disableFormats !== true;
6150         var sep = Roo.isGecko ? "+" : ",";
6151         var fn = function(m, name, format, args){
6152             if(format && useF){
6153                 args = args ? ',' + args : "";
6154                 if(format.substr(0, 5) != "this."){
6155                     format = "fm." + format + '(';
6156                 }else{
6157                     format = 'this.call("'+ format.substr(5) + '", ';
6158                     args = ", values";
6159                 }
6160             }else{
6161                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
6162             }
6163             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
6164         };
6165         var body;
6166         // branched to use + in gecko and [].join() in others
6167         if(Roo.isGecko){
6168             body = "this.compiled = function(values){ return '" +
6169                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
6170                     "';};";
6171         }else{
6172             body = ["this.compiled = function(values){ return ['"];
6173             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
6174             body.push("'].join('');};");
6175             body = body.join('');
6176         }
6177         /**
6178          * eval:var:values
6179          * eval:var:fm
6180          */
6181         eval(body);
6182         return this;
6183     },
6184     
6185     // private function used to call members
6186     call : function(fnName, value, allValues){
6187         return this[fnName](value, allValues);
6188     },
6189     
6190     /**
6191      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6192      * @param {String/HTMLElement/Roo.Element} el The context element
6193      * @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'})
6194      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6195      * @return {HTMLElement/Roo.Element} The new node or Element
6196      */
6197     insertFirst: function(el, values, returnElement){
6198         return this.doInsert('afterBegin', el, values, returnElement);
6199     },
6200
6201     /**
6202      * Applies the supplied values to the template and inserts the new node(s) before el.
6203      * @param {String/HTMLElement/Roo.Element} el The context element
6204      * @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'})
6205      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6206      * @return {HTMLElement/Roo.Element} The new node or Element
6207      */
6208     insertBefore: function(el, values, returnElement){
6209         return this.doInsert('beforeBegin', el, values, returnElement);
6210     },
6211
6212     /**
6213      * Applies the supplied values to the template and inserts the new node(s) after el.
6214      * @param {String/HTMLElement/Roo.Element} el The context element
6215      * @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'})
6216      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6217      * @return {HTMLElement/Roo.Element} The new node or Element
6218      */
6219     insertAfter : function(el, values, returnElement){
6220         return this.doInsert('afterEnd', el, values, returnElement);
6221     },
6222     
6223     /**
6224      * Applies the supplied values to the template and appends the new node(s) to el.
6225      * @param {String/HTMLElement/Roo.Element} el The context element
6226      * @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'})
6227      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6228      * @return {HTMLElement/Roo.Element} The new node or Element
6229      */
6230     append : function(el, values, returnElement){
6231         return this.doInsert('beforeEnd', el, values, returnElement);
6232     },
6233
6234     doInsert : function(where, el, values, returnEl){
6235         el = Roo.getDom(el);
6236         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6237         return returnEl ? Roo.get(newNode, true) : newNode;
6238     },
6239
6240     /**
6241      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6242      * @param {String/HTMLElement/Roo.Element} el The context element
6243      * @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'})
6244      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6245      * @return {HTMLElement/Roo.Element} The new node or Element
6246      */
6247     overwrite : function(el, values, returnElement){
6248         el = Roo.getDom(el);
6249         el.innerHTML = this.applyTemplate(values);
6250         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6251     }
6252 };
6253 /**
6254  * Alias for {@link #applyTemplate}
6255  * @method
6256  */
6257 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6258
6259 // backwards compat
6260 Roo.DomHelper.Template = Roo.Template;
6261
6262 /**
6263  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6264  * @param {String/HTMLElement} el A DOM element or its id
6265  * @returns {Roo.Template} The created template
6266  * @static
6267  */
6268 Roo.Template.from = function(el){
6269     el = Roo.getDom(el);
6270     return new Roo.Template(el.value || el.innerHTML);
6271 };/*
6272  * Based on:
6273  * Ext JS Library 1.1.1
6274  * Copyright(c) 2006-2007, Ext JS, LLC.
6275  *
6276  * Originally Released Under LGPL - original licence link has changed is not relivant.
6277  *
6278  * Fork - LGPL
6279  * <script type="text/javascript">
6280  */
6281  
6282
6283 /*
6284  * This is code is also distributed under MIT license for use
6285  * with jQuery and prototype JavaScript libraries.
6286  */
6287 /**
6288  * @class Roo.DomQuery
6289 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).
6290 <p>
6291 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>
6292
6293 <p>
6294 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.
6295 </p>
6296 <h4>Element Selectors:</h4>
6297 <ul class="list">
6298     <li> <b>*</b> any element</li>
6299     <li> <b>E</b> an element with the tag E</li>
6300     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6301     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6302     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6303     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6304 </ul>
6305 <h4>Attribute Selectors:</h4>
6306 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6307 <ul class="list">
6308     <li> <b>E[foo]</b> has an attribute "foo"</li>
6309     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6310     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6311     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6312     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6313     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6314     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6315 </ul>
6316 <h4>Pseudo Classes:</h4>
6317 <ul class="list">
6318     <li> <b>E:first-child</b> E is the first child of its parent</li>
6319     <li> <b>E:last-child</b> E is the last child of its parent</li>
6320     <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>
6321     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6322     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6323     <li> <b>E:only-child</b> E is the only child of its parent</li>
6324     <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>
6325     <li> <b>E:first</b> the first E in the resultset</li>
6326     <li> <b>E:last</b> the last E in the resultset</li>
6327     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6328     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6329     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6330     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6331     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6332     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6333     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6334     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6335     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6336 </ul>
6337 <h4>CSS Value Selectors:</h4>
6338 <ul class="list">
6339     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6340     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6341     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6342     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6343     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6344     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6345 </ul>
6346  * @static
6347  */
6348 Roo.DomQuery = function(){
6349     var cache = {}, simpleCache = {}, valueCache = {};
6350     var nonSpace = /\S/;
6351     var trimRe = /^\s+|\s+$/g;
6352     var tplRe = /\{(\d+)\}/g;
6353     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6354     var tagTokenRe = /^(#)?([\w-\*]+)/;
6355     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6356
6357     function child(p, index){
6358         var i = 0;
6359         var n = p.firstChild;
6360         while(n){
6361             if(n.nodeType == 1){
6362                if(++i == index){
6363                    return n;
6364                }
6365             }
6366             n = n.nextSibling;
6367         }
6368         return null;
6369     };
6370
6371     function next(n){
6372         while((n = n.nextSibling) && n.nodeType != 1);
6373         return n;
6374     };
6375
6376     function prev(n){
6377         while((n = n.previousSibling) && n.nodeType != 1);
6378         return n;
6379     };
6380
6381     function children(d){
6382         var n = d.firstChild, ni = -1;
6383             while(n){
6384                 var nx = n.nextSibling;
6385                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6386                     d.removeChild(n);
6387                 }else{
6388                     n.nodeIndex = ++ni;
6389                 }
6390                 n = nx;
6391             }
6392             return this;
6393         };
6394
6395     function byClassName(c, a, v){
6396         if(!v){
6397             return c;
6398         }
6399         var r = [], ri = -1, cn;
6400         for(var i = 0, ci; ci = c[i]; i++){
6401             
6402             
6403             if((' '+
6404                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6405                  +' ').indexOf(v) != -1){
6406                 r[++ri] = ci;
6407             }
6408         }
6409         return r;
6410     };
6411
6412     function attrValue(n, attr){
6413         if(!n.tagName && typeof n.length != "undefined"){
6414             n = n[0];
6415         }
6416         if(!n){
6417             return null;
6418         }
6419         if(attr == "for"){
6420             return n.htmlFor;
6421         }
6422         if(attr == "class" || attr == "className"){
6423             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6424         }
6425         return n.getAttribute(attr) || n[attr];
6426
6427     };
6428
6429     function getNodes(ns, mode, tagName){
6430         var result = [], ri = -1, cs;
6431         if(!ns){
6432             return result;
6433         }
6434         tagName = tagName || "*";
6435         if(typeof ns.getElementsByTagName != "undefined"){
6436             ns = [ns];
6437         }
6438         if(!mode){
6439             for(var i = 0, ni; ni = ns[i]; i++){
6440                 cs = ni.getElementsByTagName(tagName);
6441                 for(var j = 0, ci; ci = cs[j]; j++){
6442                     result[++ri] = ci;
6443                 }
6444             }
6445         }else if(mode == "/" || mode == ">"){
6446             var utag = tagName.toUpperCase();
6447             for(var i = 0, ni, cn; ni = ns[i]; i++){
6448                 cn = ni.children || ni.childNodes;
6449                 for(var j = 0, cj; cj = cn[j]; j++){
6450                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6451                         result[++ri] = cj;
6452                     }
6453                 }
6454             }
6455         }else if(mode == "+"){
6456             var utag = tagName.toUpperCase();
6457             for(var i = 0, n; n = ns[i]; i++){
6458                 while((n = n.nextSibling) && n.nodeType != 1);
6459                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6460                     result[++ri] = n;
6461                 }
6462             }
6463         }else if(mode == "~"){
6464             for(var i = 0, n; n = ns[i]; i++){
6465                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6466                 if(n){
6467                     result[++ri] = n;
6468                 }
6469             }
6470         }
6471         return result;
6472     };
6473
6474     function concat(a, b){
6475         if(b.slice){
6476             return a.concat(b);
6477         }
6478         for(var i = 0, l = b.length; i < l; i++){
6479             a[a.length] = b[i];
6480         }
6481         return a;
6482     }
6483
6484     function byTag(cs, tagName){
6485         if(cs.tagName || cs == document){
6486             cs = [cs];
6487         }
6488         if(!tagName){
6489             return cs;
6490         }
6491         var r = [], ri = -1;
6492         tagName = tagName.toLowerCase();
6493         for(var i = 0, ci; ci = cs[i]; i++){
6494             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6495                 r[++ri] = ci;
6496             }
6497         }
6498         return r;
6499     };
6500
6501     function byId(cs, attr, id){
6502         if(cs.tagName || cs == document){
6503             cs = [cs];
6504         }
6505         if(!id){
6506             return cs;
6507         }
6508         var r = [], ri = -1;
6509         for(var i = 0,ci; ci = cs[i]; i++){
6510             if(ci && ci.id == id){
6511                 r[++ri] = ci;
6512                 return r;
6513             }
6514         }
6515         return r;
6516     };
6517
6518     function byAttribute(cs, attr, value, op, custom){
6519         var r = [], ri = -1, st = custom=="{";
6520         var f = Roo.DomQuery.operators[op];
6521         for(var i = 0, ci; ci = cs[i]; i++){
6522             var a;
6523             if(st){
6524                 a = Roo.DomQuery.getStyle(ci, attr);
6525             }
6526             else if(attr == "class" || attr == "className"){
6527                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6528             }else if(attr == "for"){
6529                 a = ci.htmlFor;
6530             }else if(attr == "href"){
6531                 a = ci.getAttribute("href", 2);
6532             }else{
6533                 a = ci.getAttribute(attr);
6534             }
6535             if((f && f(a, value)) || (!f && a)){
6536                 r[++ri] = ci;
6537             }
6538         }
6539         return r;
6540     };
6541
6542     function byPseudo(cs, name, value){
6543         return Roo.DomQuery.pseudos[name](cs, value);
6544     };
6545
6546     // This is for IE MSXML which does not support expandos.
6547     // IE runs the same speed using setAttribute, however FF slows way down
6548     // and Safari completely fails so they need to continue to use expandos.
6549     var isIE = window.ActiveXObject ? true : false;
6550
6551     // this eval is stop the compressor from
6552     // renaming the variable to something shorter
6553     
6554     /** eval:var:batch */
6555     var batch = 30803; 
6556
6557     var key = 30803;
6558
6559     function nodupIEXml(cs){
6560         var d = ++key;
6561         cs[0].setAttribute("_nodup", d);
6562         var r = [cs[0]];
6563         for(var i = 1, len = cs.length; i < len; i++){
6564             var c = cs[i];
6565             if(!c.getAttribute("_nodup") != d){
6566                 c.setAttribute("_nodup", d);
6567                 r[r.length] = c;
6568             }
6569         }
6570         for(var i = 0, len = cs.length; i < len; i++){
6571             cs[i].removeAttribute("_nodup");
6572         }
6573         return r;
6574     }
6575
6576     function nodup(cs){
6577         if(!cs){
6578             return [];
6579         }
6580         var len = cs.length, c, i, r = cs, cj, ri = -1;
6581         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6582             return cs;
6583         }
6584         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6585             return nodupIEXml(cs);
6586         }
6587         var d = ++key;
6588         cs[0]._nodup = d;
6589         for(i = 1; c = cs[i]; i++){
6590             if(c._nodup != d){
6591                 c._nodup = d;
6592             }else{
6593                 r = [];
6594                 for(var j = 0; j < i; j++){
6595                     r[++ri] = cs[j];
6596                 }
6597                 for(j = i+1; cj = cs[j]; j++){
6598                     if(cj._nodup != d){
6599                         cj._nodup = d;
6600                         r[++ri] = cj;
6601                     }
6602                 }
6603                 return r;
6604             }
6605         }
6606         return r;
6607     }
6608
6609     function quickDiffIEXml(c1, c2){
6610         var d = ++key;
6611         for(var i = 0, len = c1.length; i < len; i++){
6612             c1[i].setAttribute("_qdiff", d);
6613         }
6614         var r = [];
6615         for(var i = 0, len = c2.length; i < len; i++){
6616             if(c2[i].getAttribute("_qdiff") != d){
6617                 r[r.length] = c2[i];
6618             }
6619         }
6620         for(var i = 0, len = c1.length; i < len; i++){
6621            c1[i].removeAttribute("_qdiff");
6622         }
6623         return r;
6624     }
6625
6626     function quickDiff(c1, c2){
6627         var len1 = c1.length;
6628         if(!len1){
6629             return c2;
6630         }
6631         if(isIE && c1[0].selectSingleNode){
6632             return quickDiffIEXml(c1, c2);
6633         }
6634         var d = ++key;
6635         for(var i = 0; i < len1; i++){
6636             c1[i]._qdiff = d;
6637         }
6638         var r = [];
6639         for(var i = 0, len = c2.length; i < len; i++){
6640             if(c2[i]._qdiff != d){
6641                 r[r.length] = c2[i];
6642             }
6643         }
6644         return r;
6645     }
6646
6647     function quickId(ns, mode, root, id){
6648         if(ns == root){
6649            var d = root.ownerDocument || root;
6650            return d.getElementById(id);
6651         }
6652         ns = getNodes(ns, mode, "*");
6653         return byId(ns, null, id);
6654     }
6655
6656     return {
6657         getStyle : function(el, name){
6658             return Roo.fly(el).getStyle(name);
6659         },
6660         /**
6661          * Compiles a selector/xpath query into a reusable function. The returned function
6662          * takes one parameter "root" (optional), which is the context node from where the query should start.
6663          * @param {String} selector The selector/xpath query
6664          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6665          * @return {Function}
6666          */
6667         compile : function(path, type){
6668             type = type || "select";
6669             
6670             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6671             var q = path, mode, lq;
6672             var tk = Roo.DomQuery.matchers;
6673             var tklen = tk.length;
6674             var mm;
6675
6676             // accept leading mode switch
6677             var lmode = q.match(modeRe);
6678             if(lmode && lmode[1]){
6679                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6680                 q = q.replace(lmode[1], "");
6681             }
6682             // strip leading slashes
6683             while(path.substr(0, 1)=="/"){
6684                 path = path.substr(1);
6685             }
6686
6687             while(q && lq != q){
6688                 lq = q;
6689                 var tm = q.match(tagTokenRe);
6690                 if(type == "select"){
6691                     if(tm){
6692                         if(tm[1] == "#"){
6693                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6694                         }else{
6695                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6696                         }
6697                         q = q.replace(tm[0], "");
6698                     }else if(q.substr(0, 1) != '@'){
6699                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6700                     }
6701                 }else{
6702                     if(tm){
6703                         if(tm[1] == "#"){
6704                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6705                         }else{
6706                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6707                         }
6708                         q = q.replace(tm[0], "");
6709                     }
6710                 }
6711                 while(!(mm = q.match(modeRe))){
6712                     var matched = false;
6713                     for(var j = 0; j < tklen; j++){
6714                         var t = tk[j];
6715                         var m = q.match(t.re);
6716                         if(m){
6717                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6718                                                     return m[i];
6719                                                 });
6720                             q = q.replace(m[0], "");
6721                             matched = true;
6722                             break;
6723                         }
6724                     }
6725                     // prevent infinite loop on bad selector
6726                     if(!matched){
6727                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6728                     }
6729                 }
6730                 if(mm[1]){
6731                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6732                     q = q.replace(mm[1], "");
6733                 }
6734             }
6735             fn[fn.length] = "return nodup(n);\n}";
6736             
6737              /** 
6738               * list of variables that need from compression as they are used by eval.
6739              *  eval:var:batch 
6740              *  eval:var:nodup
6741              *  eval:var:byTag
6742              *  eval:var:ById
6743              *  eval:var:getNodes
6744              *  eval:var:quickId
6745              *  eval:var:mode
6746              *  eval:var:root
6747              *  eval:var:n
6748              *  eval:var:byClassName
6749              *  eval:var:byPseudo
6750              *  eval:var:byAttribute
6751              *  eval:var:attrValue
6752              * 
6753              **/ 
6754             eval(fn.join(""));
6755             return f;
6756         },
6757
6758         /**
6759          * Selects a group of elements.
6760          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6761          * @param {Node} root (optional) The start of the query (defaults to document).
6762          * @return {Array}
6763          */
6764         select : function(path, root, type){
6765             if(!root || root == document){
6766                 root = document;
6767             }
6768             if(typeof root == "string"){
6769                 root = document.getElementById(root);
6770             }
6771             var paths = path.split(",");
6772             var results = [];
6773             for(var i = 0, len = paths.length; i < len; i++){
6774                 var p = paths[i].replace(trimRe, "");
6775                 if(!cache[p]){
6776                     cache[p] = Roo.DomQuery.compile(p);
6777                     if(!cache[p]){
6778                         throw p + " is not a valid selector";
6779                     }
6780                 }
6781                 var result = cache[p](root);
6782                 if(result && result != document){
6783                     results = results.concat(result);
6784                 }
6785             }
6786             if(paths.length > 1){
6787                 return nodup(results);
6788             }
6789             return results;
6790         },
6791
6792         /**
6793          * Selects a single element.
6794          * @param {String} selector The selector/xpath query
6795          * @param {Node} root (optional) The start of the query (defaults to document).
6796          * @return {Element}
6797          */
6798         selectNode : function(path, root){
6799             return Roo.DomQuery.select(path, root)[0];
6800         },
6801
6802         /**
6803          * Selects the value of a node, optionally replacing null with the defaultValue.
6804          * @param {String} selector The selector/xpath query
6805          * @param {Node} root (optional) The start of the query (defaults to document).
6806          * @param {String} defaultValue
6807          */
6808         selectValue : function(path, root, defaultValue){
6809             path = path.replace(trimRe, "");
6810             if(!valueCache[path]){
6811                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6812             }
6813             var n = valueCache[path](root);
6814             n = n[0] ? n[0] : n;
6815             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6816             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6817         },
6818
6819         /**
6820          * Selects the value of a node, parsing integers and floats.
6821          * @param {String} selector The selector/xpath query
6822          * @param {Node} root (optional) The start of the query (defaults to document).
6823          * @param {Number} defaultValue
6824          * @return {Number}
6825          */
6826         selectNumber : function(path, root, defaultValue){
6827             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6828             return parseFloat(v);
6829         },
6830
6831         /**
6832          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6833          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6834          * @param {String} selector The simple selector to test
6835          * @return {Boolean}
6836          */
6837         is : function(el, ss){
6838             if(typeof el == "string"){
6839                 el = document.getElementById(el);
6840             }
6841             var isArray = (el instanceof Array);
6842             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6843             return isArray ? (result.length == el.length) : (result.length > 0);
6844         },
6845
6846         /**
6847          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6848          * @param {Array} el An array of elements to filter
6849          * @param {String} selector The simple selector to test
6850          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6851          * the selector instead of the ones that match
6852          * @return {Array}
6853          */
6854         filter : function(els, ss, nonMatches){
6855             ss = ss.replace(trimRe, "");
6856             if(!simpleCache[ss]){
6857                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6858             }
6859             var result = simpleCache[ss](els);
6860             return nonMatches ? quickDiff(result, els) : result;
6861         },
6862
6863         /**
6864          * Collection of matching regular expressions and code snippets.
6865          */
6866         matchers : [{
6867                 re: /^\.([\w-]+)/,
6868                 select: 'n = byClassName(n, null, " {1} ");'
6869             }, {
6870                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6871                 select: 'n = byPseudo(n, "{1}", "{2}");'
6872             },{
6873                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6874                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6875             }, {
6876                 re: /^#([\w-]+)/,
6877                 select: 'n = byId(n, null, "{1}");'
6878             },{
6879                 re: /^@([\w-]+)/,
6880                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6881             }
6882         ],
6883
6884         /**
6885          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6886          * 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;.
6887          */
6888         operators : {
6889             "=" : function(a, v){
6890                 return a == v;
6891             },
6892             "!=" : function(a, v){
6893                 return a != v;
6894             },
6895             "^=" : function(a, v){
6896                 return a && a.substr(0, v.length) == v;
6897             },
6898             "$=" : function(a, v){
6899                 return a && a.substr(a.length-v.length) == v;
6900             },
6901             "*=" : function(a, v){
6902                 return a && a.indexOf(v) !== -1;
6903             },
6904             "%=" : function(a, v){
6905                 return (a % v) == 0;
6906             },
6907             "|=" : function(a, v){
6908                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6909             },
6910             "~=" : function(a, v){
6911                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6912             }
6913         },
6914
6915         /**
6916          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6917          * and the argument (if any) supplied in the selector.
6918          */
6919         pseudos : {
6920             "first-child" : function(c){
6921                 var r = [], ri = -1, n;
6922                 for(var i = 0, ci; ci = n = c[i]; i++){
6923                     while((n = n.previousSibling) && n.nodeType != 1);
6924                     if(!n){
6925                         r[++ri] = ci;
6926                     }
6927                 }
6928                 return r;
6929             },
6930
6931             "last-child" : function(c){
6932                 var r = [], ri = -1, n;
6933                 for(var i = 0, ci; ci = n = c[i]; i++){
6934                     while((n = n.nextSibling) && n.nodeType != 1);
6935                     if(!n){
6936                         r[++ri] = ci;
6937                     }
6938                 }
6939                 return r;
6940             },
6941
6942             "nth-child" : function(c, a) {
6943                 var r = [], ri = -1;
6944                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6945                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6946                 for(var i = 0, n; n = c[i]; i++){
6947                     var pn = n.parentNode;
6948                     if (batch != pn._batch) {
6949                         var j = 0;
6950                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6951                             if(cn.nodeType == 1){
6952                                cn.nodeIndex = ++j;
6953                             }
6954                         }
6955                         pn._batch = batch;
6956                     }
6957                     if (f == 1) {
6958                         if (l == 0 || n.nodeIndex == l){
6959                             r[++ri] = n;
6960                         }
6961                     } else if ((n.nodeIndex + l) % f == 0){
6962                         r[++ri] = n;
6963                     }
6964                 }
6965
6966                 return r;
6967             },
6968
6969             "only-child" : function(c){
6970                 var r = [], ri = -1;;
6971                 for(var i = 0, ci; ci = c[i]; i++){
6972                     if(!prev(ci) && !next(ci)){
6973                         r[++ri] = ci;
6974                     }
6975                 }
6976                 return r;
6977             },
6978
6979             "empty" : function(c){
6980                 var r = [], ri = -1;
6981                 for(var i = 0, ci; ci = c[i]; i++){
6982                     var cns = ci.childNodes, j = 0, cn, empty = true;
6983                     while(cn = cns[j]){
6984                         ++j;
6985                         if(cn.nodeType == 1 || cn.nodeType == 3){
6986                             empty = false;
6987                             break;
6988                         }
6989                     }
6990                     if(empty){
6991                         r[++ri] = ci;
6992                     }
6993                 }
6994                 return r;
6995             },
6996
6997             "contains" : function(c, v){
6998                 var r = [], ri = -1;
6999                 for(var i = 0, ci; ci = c[i]; i++){
7000                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
7001                         r[++ri] = ci;
7002                     }
7003                 }
7004                 return r;
7005             },
7006
7007             "nodeValue" : function(c, v){
7008                 var r = [], ri = -1;
7009                 for(var i = 0, ci; ci = c[i]; i++){
7010                     if(ci.firstChild && ci.firstChild.nodeValue == v){
7011                         r[++ri] = ci;
7012                     }
7013                 }
7014                 return r;
7015             },
7016
7017             "checked" : function(c){
7018                 var r = [], ri = -1;
7019                 for(var i = 0, ci; ci = c[i]; i++){
7020                     if(ci.checked == true){
7021                         r[++ri] = ci;
7022                     }
7023                 }
7024                 return r;
7025             },
7026
7027             "not" : function(c, ss){
7028                 return Roo.DomQuery.filter(c, ss, true);
7029             },
7030
7031             "odd" : function(c){
7032                 return this["nth-child"](c, "odd");
7033             },
7034
7035             "even" : function(c){
7036                 return this["nth-child"](c, "even");
7037             },
7038
7039             "nth" : function(c, a){
7040                 return c[a-1] || [];
7041             },
7042
7043             "first" : function(c){
7044                 return c[0] || [];
7045             },
7046
7047             "last" : function(c){
7048                 return c[c.length-1] || [];
7049             },
7050
7051             "has" : function(c, ss){
7052                 var s = Roo.DomQuery.select;
7053                 var r = [], ri = -1;
7054                 for(var i = 0, ci; ci = c[i]; i++){
7055                     if(s(ss, ci).length > 0){
7056                         r[++ri] = ci;
7057                     }
7058                 }
7059                 return r;
7060             },
7061
7062             "next" : function(c, ss){
7063                 var is = Roo.DomQuery.is;
7064                 var r = [], ri = -1;
7065                 for(var i = 0, ci; ci = c[i]; i++){
7066                     var n = next(ci);
7067                     if(n && is(n, ss)){
7068                         r[++ri] = ci;
7069                     }
7070                 }
7071                 return r;
7072             },
7073
7074             "prev" : function(c, ss){
7075                 var is = Roo.DomQuery.is;
7076                 var r = [], ri = -1;
7077                 for(var i = 0, ci; ci = c[i]; i++){
7078                     var n = prev(ci);
7079                     if(n && is(n, ss)){
7080                         r[++ri] = ci;
7081                     }
7082                 }
7083                 return r;
7084             }
7085         }
7086     };
7087 }();
7088
7089 /**
7090  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
7091  * @param {String} path The selector/xpath query
7092  * @param {Node} root (optional) The start of the query (defaults to document).
7093  * @return {Array}
7094  * @member Roo
7095  * @method query
7096  */
7097 Roo.query = Roo.DomQuery.select;
7098 /*
7099  * Based on:
7100  * Ext JS Library 1.1.1
7101  * Copyright(c) 2006-2007, Ext JS, LLC.
7102  *
7103  * Originally Released Under LGPL - original licence link has changed is not relivant.
7104  *
7105  * Fork - LGPL
7106  * <script type="text/javascript">
7107  */
7108
7109 /**
7110  * @class Roo.util.Observable
7111  * Base class that provides a common interface for publishing events. Subclasses are expected to
7112  * to have a property "events" with all the events defined.<br>
7113  * For example:
7114  * <pre><code>
7115  Employee = function(name){
7116     this.name = name;
7117     this.addEvents({
7118         "fired" : true,
7119         "quit" : true
7120     });
7121  }
7122  Roo.extend(Employee, Roo.util.Observable);
7123 </code></pre>
7124  * @param {Object} config properties to use (incuding events / listeners)
7125  */
7126
7127 Roo.util.Observable = function(cfg){
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     config = config || {};
16870     if(config.tagName || config.dom || typeof config == "string"){ // element object
16871         config = {el: config, id: config.id || config};
16872     }
16873     this.initialConfig = config;
16874
16875     Roo.apply(this, config);
16876     this.addEvents({
16877         /**
16878          * @event disable
16879          * Fires after the component is disabled.
16880              * @param {Roo.Component} this
16881              */
16882         disable : true,
16883         /**
16884          * @event enable
16885          * Fires after the component is enabled.
16886              * @param {Roo.Component} this
16887              */
16888         enable : true,
16889         /**
16890          * @event beforeshow
16891          * Fires before the component is shown.  Return false to stop the show.
16892              * @param {Roo.Component} this
16893              */
16894         beforeshow : true,
16895         /**
16896          * @event show
16897          * Fires after the component is shown.
16898              * @param {Roo.Component} this
16899              */
16900         show : true,
16901         /**
16902          * @event beforehide
16903          * Fires before the component is hidden. Return false to stop the hide.
16904              * @param {Roo.Component} this
16905              */
16906         beforehide : true,
16907         /**
16908          * @event hide
16909          * Fires after the component is hidden.
16910              * @param {Roo.Component} this
16911              */
16912         hide : true,
16913         /**
16914          * @event beforerender
16915          * Fires before the component is rendered. Return false to stop the render.
16916              * @param {Roo.Component} this
16917              */
16918         beforerender : true,
16919         /**
16920          * @event render
16921          * Fires after the component is rendered.
16922              * @param {Roo.Component} this
16923              */
16924         render : true,
16925         /**
16926          * @event beforedestroy
16927          * Fires before the component is destroyed. Return false to stop the destroy.
16928              * @param {Roo.Component} this
16929              */
16930         beforedestroy : true,
16931         /**
16932          * @event destroy
16933          * Fires after the component is destroyed.
16934              * @param {Roo.Component} this
16935              */
16936         destroy : true
16937     });
16938     if(!this.id){
16939         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16940     }
16941     Roo.ComponentMgr.register(this);
16942     Roo.Component.superclass.constructor.call(this);
16943     this.initComponent();
16944     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16945         this.render(this.renderTo);
16946         delete this.renderTo;
16947     }
16948 };
16949
16950 /** @private */
16951 Roo.Component.AUTO_ID = 1000;
16952
16953 Roo.extend(Roo.Component, Roo.util.Observable, {
16954     /**
16955      * @scope Roo.Component.prototype
16956      * @type {Boolean}
16957      * true if this component is hidden. Read-only.
16958      */
16959     hidden : false,
16960     /**
16961      * @type {Boolean}
16962      * true if this component is disabled. Read-only.
16963      */
16964     disabled : false,
16965     /**
16966      * @type {Boolean}
16967      * true if this component has been rendered. Read-only.
16968      */
16969     rendered : false,
16970     
16971     /** @cfg {String} disableClass
16972      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16973      */
16974     disabledClass : "x-item-disabled",
16975         /** @cfg {Boolean} allowDomMove
16976          * Whether the component can move the Dom node when rendering (defaults to true).
16977          */
16978     allowDomMove : true,
16979     /** @cfg {String} hideMode (display|visibility)
16980      * How this component should hidden. Supported values are
16981      * "visibility" (css visibility), "offsets" (negative offset position) and
16982      * "display" (css display) - defaults to "display".
16983      */
16984     hideMode: 'display',
16985
16986     /** @private */
16987     ctype : "Roo.Component",
16988
16989     /**
16990      * @cfg {String} actionMode 
16991      * which property holds the element that used for  hide() / show() / disable() / enable()
16992      * default is 'el' for forms you probably want to set this to fieldEl 
16993      */
16994     actionMode : "el",
16995
16996     /** @private */
16997     getActionEl : function(){
16998         return this[this.actionMode];
16999     },
17000
17001     initComponent : Roo.emptyFn,
17002     /**
17003      * If this is a lazy rendering component, render it to its container element.
17004      * @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.
17005      */
17006     render : function(container, position){
17007         
17008         if(this.rendered){
17009             return this;
17010         }
17011         
17012         if(this.fireEvent("beforerender", this) === false){
17013             return false;
17014         }
17015         
17016         if(!container && this.el){
17017             this.el = Roo.get(this.el);
17018             container = this.el.dom.parentNode;
17019             this.allowDomMove = false;
17020         }
17021         this.container = Roo.get(container);
17022         this.rendered = true;
17023         if(position !== undefined){
17024             if(typeof position == 'number'){
17025                 position = this.container.dom.childNodes[position];
17026             }else{
17027                 position = Roo.getDom(position);
17028             }
17029         }
17030         this.onRender(this.container, position || null);
17031         if(this.cls){
17032             this.el.addClass(this.cls);
17033             delete this.cls;
17034         }
17035         if(this.style){
17036             this.el.applyStyles(this.style);
17037             delete this.style;
17038         }
17039         this.fireEvent("render", this);
17040         this.afterRender(this.container);
17041         if(this.hidden){
17042             this.hide();
17043         }
17044         if(this.disabled){
17045             this.disable();
17046         }
17047
17048         return this;
17049         
17050     },
17051
17052     /** @private */
17053     // default function is not really useful
17054     onRender : function(ct, position){
17055         if(this.el){
17056             this.el = Roo.get(this.el);
17057             if(this.allowDomMove !== false){
17058                 ct.dom.insertBefore(this.el.dom, position);
17059             }
17060         }
17061     },
17062
17063     /** @private */
17064     getAutoCreate : function(){
17065         var cfg = typeof this.autoCreate == "object" ?
17066                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17067         if(this.id && !cfg.id){
17068             cfg.id = this.id;
17069         }
17070         return cfg;
17071     },
17072
17073     /** @private */
17074     afterRender : Roo.emptyFn,
17075
17076     /**
17077      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17078      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17079      */
17080     destroy : function(){
17081         if(this.fireEvent("beforedestroy", this) !== false){
17082             this.purgeListeners();
17083             this.beforeDestroy();
17084             if(this.rendered){
17085                 this.el.removeAllListeners();
17086                 this.el.remove();
17087                 if(this.actionMode == "container"){
17088                     this.container.remove();
17089                 }
17090             }
17091             this.onDestroy();
17092             Roo.ComponentMgr.unregister(this);
17093             this.fireEvent("destroy", this);
17094         }
17095     },
17096
17097         /** @private */
17098     beforeDestroy : function(){
17099
17100     },
17101
17102         /** @private */
17103         onDestroy : function(){
17104
17105     },
17106
17107     /**
17108      * Returns the underlying {@link Roo.Element}.
17109      * @return {Roo.Element} The element
17110      */
17111     getEl : function(){
17112         return this.el;
17113     },
17114
17115     /**
17116      * Returns the id of this component.
17117      * @return {String}
17118      */
17119     getId : function(){
17120         return this.id;
17121     },
17122
17123     /**
17124      * Try to focus this component.
17125      * @param {Boolean} selectText True to also select the text in this component (if applicable)
17126      * @return {Roo.Component} this
17127      */
17128     focus : function(selectText){
17129         if(this.rendered){
17130             this.el.focus();
17131             if(selectText === true){
17132                 this.el.dom.select();
17133             }
17134         }
17135         return this;
17136     },
17137
17138     /** @private */
17139     blur : function(){
17140         if(this.rendered){
17141             this.el.blur();
17142         }
17143         return this;
17144     },
17145
17146     /**
17147      * Disable this component.
17148      * @return {Roo.Component} this
17149      */
17150     disable : function(){
17151         if(this.rendered){
17152             this.onDisable();
17153         }
17154         this.disabled = true;
17155         this.fireEvent("disable", this);
17156         return this;
17157     },
17158
17159         // private
17160     onDisable : function(){
17161         this.getActionEl().addClass(this.disabledClass);
17162         this.el.dom.disabled = true;
17163     },
17164
17165     /**
17166      * Enable this component.
17167      * @return {Roo.Component} this
17168      */
17169     enable : function(){
17170         if(this.rendered){
17171             this.onEnable();
17172         }
17173         this.disabled = false;
17174         this.fireEvent("enable", this);
17175         return this;
17176     },
17177
17178         // private
17179     onEnable : function(){
17180         this.getActionEl().removeClass(this.disabledClass);
17181         this.el.dom.disabled = false;
17182     },
17183
17184     /**
17185      * Convenience function for setting disabled/enabled by boolean.
17186      * @param {Boolean} disabled
17187      */
17188     setDisabled : function(disabled){
17189         this[disabled ? "disable" : "enable"]();
17190     },
17191
17192     /**
17193      * Show this component.
17194      * @return {Roo.Component} this
17195      */
17196     show: function(){
17197         if(this.fireEvent("beforeshow", this) !== false){
17198             this.hidden = false;
17199             if(this.rendered){
17200                 this.onShow();
17201             }
17202             this.fireEvent("show", this);
17203         }
17204         return this;
17205     },
17206
17207     // private
17208     onShow : function(){
17209         var ae = this.getActionEl();
17210         if(this.hideMode == 'visibility'){
17211             ae.dom.style.visibility = "visible";
17212         }else if(this.hideMode == 'offsets'){
17213             ae.removeClass('x-hidden');
17214         }else{
17215             ae.dom.style.display = "";
17216         }
17217     },
17218
17219     /**
17220      * Hide this component.
17221      * @return {Roo.Component} this
17222      */
17223     hide: function(){
17224         if(this.fireEvent("beforehide", this) !== false){
17225             this.hidden = true;
17226             if(this.rendered){
17227                 this.onHide();
17228             }
17229             this.fireEvent("hide", this);
17230         }
17231         return this;
17232     },
17233
17234     // private
17235     onHide : function(){
17236         var ae = this.getActionEl();
17237         if(this.hideMode == 'visibility'){
17238             ae.dom.style.visibility = "hidden";
17239         }else if(this.hideMode == 'offsets'){
17240             ae.addClass('x-hidden');
17241         }else{
17242             ae.dom.style.display = "none";
17243         }
17244     },
17245
17246     /**
17247      * Convenience function to hide or show this component by boolean.
17248      * @param {Boolean} visible True to show, false to hide
17249      * @return {Roo.Component} this
17250      */
17251     setVisible: function(visible){
17252         if(visible) {
17253             this.show();
17254         }else{
17255             this.hide();
17256         }
17257         return this;
17258     },
17259
17260     /**
17261      * Returns true if this component is visible.
17262      */
17263     isVisible : function(){
17264         return this.getActionEl().isVisible();
17265     },
17266
17267     cloneConfig : function(overrides){
17268         overrides = overrides || {};
17269         var id = overrides.id || Roo.id();
17270         var cfg = Roo.applyIf(overrides, this.initialConfig);
17271         cfg.id = id; // prevent dup id
17272         return new this.constructor(cfg);
17273     }
17274 });/*
17275  * Based on:
17276  * Ext JS Library 1.1.1
17277  * Copyright(c) 2006-2007, Ext JS, LLC.
17278  *
17279  * Originally Released Under LGPL - original licence link has changed is not relivant.
17280  *
17281  * Fork - LGPL
17282  * <script type="text/javascript">
17283  */
17284
17285 /**
17286  * @class Roo.BoxComponent
17287  * @extends Roo.Component
17288  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17289  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17290  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17291  * layout containers.
17292  * @constructor
17293  * @param {Roo.Element/String/Object} config The configuration options.
17294  */
17295 Roo.BoxComponent = function(config){
17296     Roo.Component.call(this, config);
17297     this.addEvents({
17298         /**
17299          * @event resize
17300          * Fires after the component is resized.
17301              * @param {Roo.Component} this
17302              * @param {Number} adjWidth The box-adjusted width that was set
17303              * @param {Number} adjHeight The box-adjusted height that was set
17304              * @param {Number} rawWidth The width that was originally specified
17305              * @param {Number} rawHeight The height that was originally specified
17306              */
17307         resize : true,
17308         /**
17309          * @event move
17310          * Fires after the component is moved.
17311              * @param {Roo.Component} this
17312              * @param {Number} x The new x position
17313              * @param {Number} y The new y position
17314              */
17315         move : true
17316     });
17317 };
17318
17319 Roo.extend(Roo.BoxComponent, Roo.Component, {
17320     // private, set in afterRender to signify that the component has been rendered
17321     boxReady : false,
17322     // private, used to defer height settings to subclasses
17323     deferHeight: false,
17324     /** @cfg {Number} width
17325      * width (optional) size of component
17326      */
17327      /** @cfg {Number} height
17328      * height (optional) size of component
17329      */
17330      
17331     /**
17332      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17333      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17334      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17335      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17336      * @return {Roo.BoxComponent} this
17337      */
17338     setSize : function(w, h){
17339         // support for standard size objects
17340         if(typeof w == 'object'){
17341             h = w.height;
17342             w = w.width;
17343         }
17344         // not rendered
17345         if(!this.boxReady){
17346             this.width = w;
17347             this.height = h;
17348             return this;
17349         }
17350
17351         // prevent recalcs when not needed
17352         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17353             return this;
17354         }
17355         this.lastSize = {width: w, height: h};
17356
17357         var adj = this.adjustSize(w, h);
17358         var aw = adj.width, ah = adj.height;
17359         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17360             var rz = this.getResizeEl();
17361             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17362                 rz.setSize(aw, ah);
17363             }else if(!this.deferHeight && ah !== undefined){
17364                 rz.setHeight(ah);
17365             }else if(aw !== undefined){
17366                 rz.setWidth(aw);
17367             }
17368             this.onResize(aw, ah, w, h);
17369             this.fireEvent('resize', this, aw, ah, w, h);
17370         }
17371         return this;
17372     },
17373
17374     /**
17375      * Gets the current size of the component's underlying element.
17376      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17377      */
17378     getSize : function(){
17379         return this.el.getSize();
17380     },
17381
17382     /**
17383      * Gets the current XY position of the component's underlying element.
17384      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17385      * @return {Array} The XY position of the element (e.g., [100, 200])
17386      */
17387     getPosition : function(local){
17388         if(local === true){
17389             return [this.el.getLeft(true), this.el.getTop(true)];
17390         }
17391         return this.xy || this.el.getXY();
17392     },
17393
17394     /**
17395      * Gets the current box measurements of the component's underlying element.
17396      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17397      * @returns {Object} box An object in the format {x, y, width, height}
17398      */
17399     getBox : function(local){
17400         var s = this.el.getSize();
17401         if(local){
17402             s.x = this.el.getLeft(true);
17403             s.y = this.el.getTop(true);
17404         }else{
17405             var xy = this.xy || this.el.getXY();
17406             s.x = xy[0];
17407             s.y = xy[1];
17408         }
17409         return s;
17410     },
17411
17412     /**
17413      * Sets the current box measurements of the component's underlying element.
17414      * @param {Object} box An object in the format {x, y, width, height}
17415      * @returns {Roo.BoxComponent} this
17416      */
17417     updateBox : function(box){
17418         this.setSize(box.width, box.height);
17419         this.setPagePosition(box.x, box.y);
17420         return this;
17421     },
17422
17423     // protected
17424     getResizeEl : function(){
17425         return this.resizeEl || this.el;
17426     },
17427
17428     // protected
17429     getPositionEl : function(){
17430         return this.positionEl || this.el;
17431     },
17432
17433     /**
17434      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17435      * This method fires the move event.
17436      * @param {Number} left The new left
17437      * @param {Number} top The new top
17438      * @returns {Roo.BoxComponent} this
17439      */
17440     setPosition : function(x, y){
17441         this.x = x;
17442         this.y = y;
17443         if(!this.boxReady){
17444             return this;
17445         }
17446         var adj = this.adjustPosition(x, y);
17447         var ax = adj.x, ay = adj.y;
17448
17449         var el = this.getPositionEl();
17450         if(ax !== undefined || ay !== undefined){
17451             if(ax !== undefined && ay !== undefined){
17452                 el.setLeftTop(ax, ay);
17453             }else if(ax !== undefined){
17454                 el.setLeft(ax);
17455             }else if(ay !== undefined){
17456                 el.setTop(ay);
17457             }
17458             this.onPosition(ax, ay);
17459             this.fireEvent('move', this, ax, ay);
17460         }
17461         return this;
17462     },
17463
17464     /**
17465      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17466      * This method fires the move event.
17467      * @param {Number} x The new x position
17468      * @param {Number} y The new y position
17469      * @returns {Roo.BoxComponent} this
17470      */
17471     setPagePosition : function(x, y){
17472         this.pageX = x;
17473         this.pageY = y;
17474         if(!this.boxReady){
17475             return;
17476         }
17477         if(x === undefined || y === undefined){ // cannot translate undefined points
17478             return;
17479         }
17480         var p = this.el.translatePoints(x, y);
17481         this.setPosition(p.left, p.top);
17482         return this;
17483     },
17484
17485     // private
17486     onRender : function(ct, position){
17487         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17488         if(this.resizeEl){
17489             this.resizeEl = Roo.get(this.resizeEl);
17490         }
17491         if(this.positionEl){
17492             this.positionEl = Roo.get(this.positionEl);
17493         }
17494     },
17495
17496     // private
17497     afterRender : function(){
17498         Roo.BoxComponent.superclass.afterRender.call(this);
17499         this.boxReady = true;
17500         this.setSize(this.width, this.height);
17501         if(this.x || this.y){
17502             this.setPosition(this.x, this.y);
17503         }
17504         if(this.pageX || this.pageY){
17505             this.setPagePosition(this.pageX, this.pageY);
17506         }
17507     },
17508
17509     /**
17510      * Force the component's size to recalculate based on the underlying element's current height and width.
17511      * @returns {Roo.BoxComponent} this
17512      */
17513     syncSize : function(){
17514         delete this.lastSize;
17515         this.setSize(this.el.getWidth(), this.el.getHeight());
17516         return this;
17517     },
17518
17519     /**
17520      * Called after the component is resized, this method is empty by default but can be implemented by any
17521      * subclass that needs to perform custom logic after a resize occurs.
17522      * @param {Number} adjWidth The box-adjusted width that was set
17523      * @param {Number} adjHeight The box-adjusted height that was set
17524      * @param {Number} rawWidth The width that was originally specified
17525      * @param {Number} rawHeight The height that was originally specified
17526      */
17527     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17528
17529     },
17530
17531     /**
17532      * Called after the component is moved, this method is empty by default but can be implemented by any
17533      * subclass that needs to perform custom logic after a move occurs.
17534      * @param {Number} x The new x position
17535      * @param {Number} y The new y position
17536      */
17537     onPosition : function(x, y){
17538
17539     },
17540
17541     // private
17542     adjustSize : function(w, h){
17543         if(this.autoWidth){
17544             w = 'auto';
17545         }
17546         if(this.autoHeight){
17547             h = 'auto';
17548         }
17549         return {width : w, height: h};
17550     },
17551
17552     // private
17553     adjustPosition : function(x, y){
17554         return {x : x, y: y};
17555     }
17556 });/*
17557  * Based on:
17558  * Ext JS Library 1.1.1
17559  * Copyright(c) 2006-2007, Ext JS, LLC.
17560  *
17561  * Originally Released Under LGPL - original licence link has changed is not relivant.
17562  *
17563  * Fork - LGPL
17564  * <script type="text/javascript">
17565  */
17566  (function(){ 
17567 /**
17568  * @class Roo.Layer
17569  * @extends Roo.Element
17570  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17571  * automatic maintaining of shadow/shim positions.
17572  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17573  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17574  * you can pass a string with a CSS class name. False turns off the shadow.
17575  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17576  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17577  * @cfg {String} cls CSS class to add to the element
17578  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17579  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17580  * @constructor
17581  * @param {Object} config An object with config options.
17582  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17583  */
17584
17585 Roo.Layer = function(config, existingEl){
17586     config = config || {};
17587     var dh = Roo.DomHelper;
17588     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17589     if(existingEl){
17590         this.dom = Roo.getDom(existingEl);
17591     }
17592     if(!this.dom){
17593         var o = config.dh || {tag: "div", cls: "x-layer"};
17594         this.dom = dh.append(pel, o);
17595     }
17596     if(config.cls){
17597         this.addClass(config.cls);
17598     }
17599     this.constrain = config.constrain !== false;
17600     this.visibilityMode = Roo.Element.VISIBILITY;
17601     if(config.id){
17602         this.id = this.dom.id = config.id;
17603     }else{
17604         this.id = Roo.id(this.dom);
17605     }
17606     this.zindex = config.zindex || this.getZIndex();
17607     this.position("absolute", this.zindex);
17608     if(config.shadow){
17609         this.shadowOffset = config.shadowOffset || 4;
17610         this.shadow = new Roo.Shadow({
17611             offset : this.shadowOffset,
17612             mode : config.shadow
17613         });
17614     }else{
17615         this.shadowOffset = 0;
17616     }
17617     this.useShim = config.shim !== false && Roo.useShims;
17618     this.useDisplay = config.useDisplay;
17619     this.hide();
17620 };
17621
17622 var supr = Roo.Element.prototype;
17623
17624 // shims are shared among layer to keep from having 100 iframes
17625 var shims = [];
17626
17627 Roo.extend(Roo.Layer, Roo.Element, {
17628
17629     getZIndex : function(){
17630         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17631     },
17632
17633     getShim : function(){
17634         if(!this.useShim){
17635             return null;
17636         }
17637         if(this.shim){
17638             return this.shim;
17639         }
17640         var shim = shims.shift();
17641         if(!shim){
17642             shim = this.createShim();
17643             shim.enableDisplayMode('block');
17644             shim.dom.style.display = 'none';
17645             shim.dom.style.visibility = 'visible';
17646         }
17647         var pn = this.dom.parentNode;
17648         if(shim.dom.parentNode != pn){
17649             pn.insertBefore(shim.dom, this.dom);
17650         }
17651         shim.setStyle('z-index', this.getZIndex()-2);
17652         this.shim = shim;
17653         return shim;
17654     },
17655
17656     hideShim : function(){
17657         if(this.shim){
17658             this.shim.setDisplayed(false);
17659             shims.push(this.shim);
17660             delete this.shim;
17661         }
17662     },
17663
17664     disableShadow : function(){
17665         if(this.shadow){
17666             this.shadowDisabled = true;
17667             this.shadow.hide();
17668             this.lastShadowOffset = this.shadowOffset;
17669             this.shadowOffset = 0;
17670         }
17671     },
17672
17673     enableShadow : function(show){
17674         if(this.shadow){
17675             this.shadowDisabled = false;
17676             this.shadowOffset = this.lastShadowOffset;
17677             delete this.lastShadowOffset;
17678             if(show){
17679                 this.sync(true);
17680             }
17681         }
17682     },
17683
17684     // private
17685     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17686     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17687     sync : function(doShow){
17688         var sw = this.shadow;
17689         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17690             var sh = this.getShim();
17691
17692             var w = this.getWidth(),
17693                 h = this.getHeight();
17694
17695             var l = this.getLeft(true),
17696                 t = this.getTop(true);
17697
17698             if(sw && !this.shadowDisabled){
17699                 if(doShow && !sw.isVisible()){
17700                     sw.show(this);
17701                 }else{
17702                     sw.realign(l, t, w, h);
17703                 }
17704                 if(sh){
17705                     if(doShow){
17706                        sh.show();
17707                     }
17708                     // fit the shim behind the shadow, so it is shimmed too
17709                     var a = sw.adjusts, s = sh.dom.style;
17710                     s.left = (Math.min(l, l+a.l))+"px";
17711                     s.top = (Math.min(t, t+a.t))+"px";
17712                     s.width = (w+a.w)+"px";
17713                     s.height = (h+a.h)+"px";
17714                 }
17715             }else if(sh){
17716                 if(doShow){
17717                    sh.show();
17718                 }
17719                 sh.setSize(w, h);
17720                 sh.setLeftTop(l, t);
17721             }
17722             
17723         }
17724     },
17725
17726     // private
17727     destroy : function(){
17728         this.hideShim();
17729         if(this.shadow){
17730             this.shadow.hide();
17731         }
17732         this.removeAllListeners();
17733         var pn = this.dom.parentNode;
17734         if(pn){
17735             pn.removeChild(this.dom);
17736         }
17737         Roo.Element.uncache(this.id);
17738     },
17739
17740     remove : function(){
17741         this.destroy();
17742     },
17743
17744     // private
17745     beginUpdate : function(){
17746         this.updating = true;
17747     },
17748
17749     // private
17750     endUpdate : function(){
17751         this.updating = false;
17752         this.sync(true);
17753     },
17754
17755     // private
17756     hideUnders : function(negOffset){
17757         if(this.shadow){
17758             this.shadow.hide();
17759         }
17760         this.hideShim();
17761     },
17762
17763     // private
17764     constrainXY : function(){
17765         if(this.constrain){
17766             var vw = Roo.lib.Dom.getViewWidth(),
17767                 vh = Roo.lib.Dom.getViewHeight();
17768             var s = Roo.get(document).getScroll();
17769
17770             var xy = this.getXY();
17771             var x = xy[0], y = xy[1];   
17772             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17773             // only move it if it needs it
17774             var moved = false;
17775             // first validate right/bottom
17776             if((x + w) > vw+s.left){
17777                 x = vw - w - this.shadowOffset;
17778                 moved = true;
17779             }
17780             if((y + h) > vh+s.top){
17781                 y = vh - h - this.shadowOffset;
17782                 moved = true;
17783             }
17784             // then make sure top/left isn't negative
17785             if(x < s.left){
17786                 x = s.left;
17787                 moved = true;
17788             }
17789             if(y < s.top){
17790                 y = s.top;
17791                 moved = true;
17792             }
17793             if(moved){
17794                 if(this.avoidY){
17795                     var ay = this.avoidY;
17796                     if(y <= ay && (y+h) >= ay){
17797                         y = ay-h-5;   
17798                     }
17799                 }
17800                 xy = [x, y];
17801                 this.storeXY(xy);
17802                 supr.setXY.call(this, xy);
17803                 this.sync();
17804             }
17805         }
17806     },
17807
17808     isVisible : function(){
17809         return this.visible;    
17810     },
17811
17812     // private
17813     showAction : function(){
17814         this.visible = true; // track visibility to prevent getStyle calls
17815         if(this.useDisplay === true){
17816             this.setDisplayed("");
17817         }else if(this.lastXY){
17818             supr.setXY.call(this, this.lastXY);
17819         }else if(this.lastLT){
17820             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17821         }
17822     },
17823
17824     // private
17825     hideAction : function(){
17826         this.visible = false;
17827         if(this.useDisplay === true){
17828             this.setDisplayed(false);
17829         }else{
17830             this.setLeftTop(-10000,-10000);
17831         }
17832     },
17833
17834     // overridden Element method
17835     setVisible : function(v, a, d, c, e){
17836         if(v){
17837             this.showAction();
17838         }
17839         if(a && v){
17840             var cb = function(){
17841                 this.sync(true);
17842                 if(c){
17843                     c();
17844                 }
17845             }.createDelegate(this);
17846             supr.setVisible.call(this, true, true, d, cb, e);
17847         }else{
17848             if(!v){
17849                 this.hideUnders(true);
17850             }
17851             var cb = c;
17852             if(a){
17853                 cb = function(){
17854                     this.hideAction();
17855                     if(c){
17856                         c();
17857                     }
17858                 }.createDelegate(this);
17859             }
17860             supr.setVisible.call(this, v, a, d, cb, e);
17861             if(v){
17862                 this.sync(true);
17863             }else if(!a){
17864                 this.hideAction();
17865             }
17866         }
17867     },
17868
17869     storeXY : function(xy){
17870         delete this.lastLT;
17871         this.lastXY = xy;
17872     },
17873
17874     storeLeftTop : function(left, top){
17875         delete this.lastXY;
17876         this.lastLT = [left, top];
17877     },
17878
17879     // private
17880     beforeFx : function(){
17881         this.beforeAction();
17882         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17883     },
17884
17885     // private
17886     afterFx : function(){
17887         Roo.Layer.superclass.afterFx.apply(this, arguments);
17888         this.sync(this.isVisible());
17889     },
17890
17891     // private
17892     beforeAction : function(){
17893         if(!this.updating && this.shadow){
17894             this.shadow.hide();
17895         }
17896     },
17897
17898     // overridden Element method
17899     setLeft : function(left){
17900         this.storeLeftTop(left, this.getTop(true));
17901         supr.setLeft.apply(this, arguments);
17902         this.sync();
17903     },
17904
17905     setTop : function(top){
17906         this.storeLeftTop(this.getLeft(true), top);
17907         supr.setTop.apply(this, arguments);
17908         this.sync();
17909     },
17910
17911     setLeftTop : function(left, top){
17912         this.storeLeftTop(left, top);
17913         supr.setLeftTop.apply(this, arguments);
17914         this.sync();
17915     },
17916
17917     setXY : function(xy, a, d, c, e){
17918         this.fixDisplay();
17919         this.beforeAction();
17920         this.storeXY(xy);
17921         var cb = this.createCB(c);
17922         supr.setXY.call(this, xy, a, d, cb, e);
17923         if(!a){
17924             cb();
17925         }
17926     },
17927
17928     // private
17929     createCB : function(c){
17930         var el = this;
17931         return function(){
17932             el.constrainXY();
17933             el.sync(true);
17934             if(c){
17935                 c();
17936             }
17937         };
17938     },
17939
17940     // overridden Element method
17941     setX : function(x, a, d, c, e){
17942         this.setXY([x, this.getY()], a, d, c, e);
17943     },
17944
17945     // overridden Element method
17946     setY : function(y, a, d, c, e){
17947         this.setXY([this.getX(), y], a, d, c, e);
17948     },
17949
17950     // overridden Element method
17951     setSize : function(w, h, a, d, c, e){
17952         this.beforeAction();
17953         var cb = this.createCB(c);
17954         supr.setSize.call(this, w, h, a, d, cb, e);
17955         if(!a){
17956             cb();
17957         }
17958     },
17959
17960     // overridden Element method
17961     setWidth : function(w, a, d, c, e){
17962         this.beforeAction();
17963         var cb = this.createCB(c);
17964         supr.setWidth.call(this, w, a, d, cb, e);
17965         if(!a){
17966             cb();
17967         }
17968     },
17969
17970     // overridden Element method
17971     setHeight : function(h, a, d, c, e){
17972         this.beforeAction();
17973         var cb = this.createCB(c);
17974         supr.setHeight.call(this, h, a, d, cb, e);
17975         if(!a){
17976             cb();
17977         }
17978     },
17979
17980     // overridden Element method
17981     setBounds : function(x, y, w, h, a, d, c, e){
17982         this.beforeAction();
17983         var cb = this.createCB(c);
17984         if(!a){
17985             this.storeXY([x, y]);
17986             supr.setXY.call(this, [x, y]);
17987             supr.setSize.call(this, w, h, a, d, cb, e);
17988             cb();
17989         }else{
17990             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17991         }
17992         return this;
17993     },
17994     
17995     /**
17996      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17997      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17998      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17999      * @param {Number} zindex The new z-index to set
18000      * @return {this} The Layer
18001      */
18002     setZIndex : function(zindex){
18003         this.zindex = zindex;
18004         this.setStyle("z-index", zindex + 2);
18005         if(this.shadow){
18006             this.shadow.setZIndex(zindex + 1);
18007         }
18008         if(this.shim){
18009             this.shim.setStyle("z-index", zindex);
18010         }
18011     }
18012 });
18013 })();/*
18014  * Original code for Roojs - LGPL
18015  * <script type="text/javascript">
18016  */
18017  
18018 /**
18019  * @class Roo.XComponent
18020  * A delayed Element creator...
18021  * Or a way to group chunks of interface together.
18022  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
18023  *  used in conjunction with XComponent.build() it will create an instance of each element,
18024  *  then call addxtype() to build the User interface.
18025  * 
18026  * Mypart.xyx = new Roo.XComponent({
18027
18028     parent : 'Mypart.xyz', // empty == document.element.!!
18029     order : '001',
18030     name : 'xxxx'
18031     region : 'xxxx'
18032     disabled : function() {} 
18033      
18034     tree : function() { // return an tree of xtype declared components
18035         var MODULE = this;
18036         return 
18037         {
18038             xtype : 'NestedLayoutPanel',
18039             // technicall
18040         }
18041      ]
18042  *})
18043  *
18044  *
18045  * It can be used to build a big heiracy, with parent etc.
18046  * or you can just use this to render a single compoent to a dom element
18047  * MYPART.render(Roo.Element | String(id) | dom_element )
18048  *
18049  *
18050  * Usage patterns.
18051  *
18052  * Classic Roo
18053  *
18054  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18055  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18056  *
18057  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18058  *
18059  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
18060  * - if mulitple topModules exist, the last one is defined as the top module.
18061  *
18062  * Embeded Roo
18063  * 
18064  * When the top level or multiple modules are to embedded into a existing HTML page,
18065  * the parent element can container '#id' of the element where the module will be drawn.
18066  *
18067  * Bootstrap Roo
18068  *
18069  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18070  * it relies more on a include mechanism, where sub modules are included into an outer page.
18071  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18072  * 
18073  * Bootstrap Roo Included elements
18074  *
18075  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18076  * hence confusing the component builder as it thinks there are multiple top level elements. 
18077  *
18078  * String Over-ride & Translations
18079  *
18080  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18081  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18082  * are needed. @see Roo.XComponent.overlayString  
18083  * 
18084  * 
18085  * 
18086  * @extends Roo.util.Observable
18087  * @constructor
18088  * @param cfg {Object} configuration of component
18089  * 
18090  */
18091 Roo.XComponent = function(cfg) {
18092     Roo.apply(this, cfg);
18093     this.addEvents({ 
18094         /**
18095              * @event built
18096              * Fires when this the componnt is built
18097              * @param {Roo.XComponent} c the component
18098              */
18099         'built' : true
18100         
18101     });
18102     this.region = this.region || 'center'; // default..
18103     Roo.XComponent.register(this);
18104     this.modules = false;
18105     this.el = false; // where the layout goes..
18106     
18107     
18108 }
18109 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18110     /**
18111      * @property el
18112      * The created element (with Roo.factory())
18113      * @type {Roo.Layout}
18114      */
18115     el  : false,
18116     
18117     /**
18118      * @property el
18119      * for BC  - use el in new code
18120      * @type {Roo.Layout}
18121      */
18122     panel : false,
18123     
18124     /**
18125      * @property layout
18126      * for BC  - use el in new code
18127      * @type {Roo.Layout}
18128      */
18129     layout : false,
18130     
18131      /**
18132      * @cfg {Function|boolean} disabled
18133      * If this module is disabled by some rule, return true from the funtion
18134      */
18135     disabled : false,
18136     
18137     /**
18138      * @cfg {String} parent 
18139      * Name of parent element which it get xtype added to..
18140      */
18141     parent: false,
18142     
18143     /**
18144      * @cfg {String} order
18145      * Used to set the order in which elements are created (usefull for multiple tabs)
18146      */
18147     
18148     order : false,
18149     /**
18150      * @cfg {String} name
18151      * String to display while loading.
18152      */
18153     name : false,
18154     /**
18155      * @cfg {String} region
18156      * Region to render component to (defaults to center)
18157      */
18158     region : 'center',
18159     
18160     /**
18161      * @cfg {Array} items
18162      * A single item array - the first element is the root of the tree..
18163      * It's done this way to stay compatible with the Xtype system...
18164      */
18165     items : false,
18166     
18167     /**
18168      * @property _tree
18169      * The method that retuns the tree of parts that make up this compoennt 
18170      * @type {function}
18171      */
18172     _tree  : false,
18173     
18174      /**
18175      * render
18176      * render element to dom or tree
18177      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18178      */
18179     
18180     render : function(el)
18181     {
18182         
18183         el = el || false;
18184         var hp = this.parent ? 1 : 0;
18185         Roo.debug &&  Roo.log(this);
18186         
18187         var tree = this._tree ? this._tree() : this.tree();
18188
18189         
18190         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18191             // if parent is a '#.....' string, then let's use that..
18192             var ename = this.parent.substr(1);
18193             this.parent = false;
18194             Roo.debug && Roo.log(ename);
18195             switch (ename) {
18196                 case 'bootstrap-body':
18197                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
18198                         // this is the BorderLayout standard?
18199                        this.parent = { el : true };
18200                        break;
18201                     }
18202                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18203                         // need to insert stuff...
18204                         this.parent =  {
18205                              el : new Roo.bootstrap.layout.Border({
18206                                  el : document.body, 
18207                      
18208                                  center: {
18209                                     titlebar: false,
18210                                     autoScroll:false,
18211                                     closeOnTab: true,
18212                                     tabPosition: 'top',
18213                                       //resizeTabs: true,
18214                                     alwaysShowTabs: true,
18215                                     hideTabs: false
18216                                      //minTabWidth: 140
18217                                  }
18218                              })
18219                         
18220                          };
18221                          break;
18222                     }
18223                          
18224                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18225                         this.parent = { el :  new  Roo.bootstrap.Body() };
18226                         Roo.debug && Roo.log("setting el to doc body");
18227                          
18228                     } else {
18229                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18230                     }
18231                     break;
18232                 case 'bootstrap':
18233                     this.parent = { el : true};
18234                     // fall through
18235                 default:
18236                     el = Roo.get(ename);
18237                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18238                         this.parent = { el : true};
18239                     }
18240                     
18241                     break;
18242             }
18243                 
18244             
18245             if (!el && !this.parent) {
18246                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18247                 return;
18248             }
18249         }
18250         
18251         Roo.debug && Roo.log("EL:");
18252         Roo.debug && Roo.log(el);
18253         Roo.debug && Roo.log("this.parent.el:");
18254         Roo.debug && Roo.log(this.parent.el);
18255         
18256
18257         // altertive root elements ??? - we need a better way to indicate these.
18258         var is_alt = Roo.XComponent.is_alt ||
18259                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18260                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18261                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18262         
18263         
18264         
18265         if (!this.parent && is_alt) {
18266             //el = Roo.get(document.body);
18267             this.parent = { el : true };
18268         }
18269             
18270             
18271         
18272         if (!this.parent) {
18273             
18274             Roo.debug && Roo.log("no parent - creating one");
18275             
18276             el = el ? Roo.get(el) : false;      
18277             
18278             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18279                 
18280                 this.parent =  {
18281                     el : new Roo.bootstrap.layout.Border({
18282                         el: el || document.body,
18283                     
18284                         center: {
18285                             titlebar: false,
18286                             autoScroll:false,
18287                             closeOnTab: true,
18288                             tabPosition: 'top',
18289                              //resizeTabs: true,
18290                             alwaysShowTabs: false,
18291                             hideTabs: true,
18292                             minTabWidth: 140,
18293                             overflow: 'visible'
18294                          }
18295                      })
18296                 };
18297             } else {
18298             
18299                 // it's a top level one..
18300                 this.parent =  {
18301                     el : new Roo.BorderLayout(el || document.body, {
18302                         center: {
18303                             titlebar: false,
18304                             autoScroll:false,
18305                             closeOnTab: true,
18306                             tabPosition: 'top',
18307                              //resizeTabs: true,
18308                             alwaysShowTabs: el && hp? false :  true,
18309                             hideTabs: el || !hp ? true :  false,
18310                             minTabWidth: 140
18311                          }
18312                     })
18313                 };
18314             }
18315         }
18316         
18317         if (!this.parent.el) {
18318                 // probably an old style ctor, which has been disabled.
18319                 return;
18320
18321         }
18322                 // The 'tree' method is  '_tree now' 
18323             
18324         tree.region = tree.region || this.region;
18325         var is_body = false;
18326         if (this.parent.el === true) {
18327             // bootstrap... - body..
18328             if (el) {
18329                 tree.el = el;
18330             }
18331             this.parent.el = Roo.factory(tree);
18332             is_body = true;
18333         }
18334         
18335         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18336         this.fireEvent('built', this);
18337         
18338         this.panel = this.el;
18339         this.layout = this.panel.layout;
18340         this.parentLayout = this.parent.layout  || false;  
18341          
18342     }
18343     
18344 });
18345
18346 Roo.apply(Roo.XComponent, {
18347     /**
18348      * @property  hideProgress
18349      * true to disable the building progress bar.. usefull on single page renders.
18350      * @type Boolean
18351      */
18352     hideProgress : false,
18353     /**
18354      * @property  buildCompleted
18355      * True when the builder has completed building the interface.
18356      * @type Boolean
18357      */
18358     buildCompleted : false,
18359      
18360     /**
18361      * @property  topModule
18362      * the upper most module - uses document.element as it's constructor.
18363      * @type Object
18364      */
18365      
18366     topModule  : false,
18367       
18368     /**
18369      * @property  modules
18370      * array of modules to be created by registration system.
18371      * @type {Array} of Roo.XComponent
18372      */
18373     
18374     modules : [],
18375     /**
18376      * @property  elmodules
18377      * array of modules to be created by which use #ID 
18378      * @type {Array} of Roo.XComponent
18379      */
18380      
18381     elmodules : [],
18382
18383      /**
18384      * @property  is_alt
18385      * Is an alternative Root - normally used by bootstrap or other systems,
18386      *    where the top element in the tree can wrap 'body' 
18387      * @type {boolean}  (default false)
18388      */
18389      
18390     is_alt : false,
18391     /**
18392      * @property  build_from_html
18393      * Build elements from html - used by bootstrap HTML stuff 
18394      *    - this is cleared after build is completed
18395      * @type {boolean}    (default false)
18396      */
18397      
18398     build_from_html : false,
18399     /**
18400      * Register components to be built later.
18401      *
18402      * This solves the following issues
18403      * - Building is not done on page load, but after an authentication process has occured.
18404      * - Interface elements are registered on page load
18405      * - Parent Interface elements may not be loaded before child, so this handles that..
18406      * 
18407      *
18408      * example:
18409      * 
18410      * MyApp.register({
18411           order : '000001',
18412           module : 'Pman.Tab.projectMgr',
18413           region : 'center',
18414           parent : 'Pman.layout',
18415           disabled : false,  // or use a function..
18416         })
18417      
18418      * * @param {Object} details about module
18419      */
18420     register : function(obj) {
18421                 
18422         Roo.XComponent.event.fireEvent('register', obj);
18423         switch(typeof(obj.disabled) ) {
18424                 
18425             case 'undefined':
18426                 break;
18427             
18428             case 'function':
18429                 if ( obj.disabled() ) {
18430                         return;
18431                 }
18432                 break;
18433             
18434             default:
18435                 if (obj.disabled || obj.region == '#disabled') {
18436                         return;
18437                 }
18438                 break;
18439         }
18440                 
18441         this.modules.push(obj);
18442          
18443     },
18444     /**
18445      * convert a string to an object..
18446      * eg. 'AAA.BBB' -> finds AAA.BBB
18447
18448      */
18449     
18450     toObject : function(str)
18451     {
18452         if (!str || typeof(str) == 'object') {
18453             return str;
18454         }
18455         if (str.substring(0,1) == '#') {
18456             return str;
18457         }
18458
18459         var ar = str.split('.');
18460         var rt, o;
18461         rt = ar.shift();
18462             /** eval:var:o */
18463         try {
18464             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18465         } catch (e) {
18466             throw "Module not found : " + str;
18467         }
18468         
18469         if (o === false) {
18470             throw "Module not found : " + str;
18471         }
18472         Roo.each(ar, function(e) {
18473             if (typeof(o[e]) == 'undefined') {
18474                 throw "Module not found : " + str;
18475             }
18476             o = o[e];
18477         });
18478         
18479         return o;
18480         
18481     },
18482     
18483     
18484     /**
18485      * move modules into their correct place in the tree..
18486      * 
18487      */
18488     preBuild : function ()
18489     {
18490         var _t = this;
18491         Roo.each(this.modules , function (obj)
18492         {
18493             Roo.XComponent.event.fireEvent('beforebuild', obj);
18494             
18495             var opar = obj.parent;
18496             try { 
18497                 obj.parent = this.toObject(opar);
18498             } catch(e) {
18499                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18500                 return;
18501             }
18502             
18503             if (!obj.parent) {
18504                 Roo.debug && Roo.log("GOT top level module");
18505                 Roo.debug && Roo.log(obj);
18506                 obj.modules = new Roo.util.MixedCollection(false, 
18507                     function(o) { return o.order + '' }
18508                 );
18509                 this.topModule = obj;
18510                 return;
18511             }
18512                         // parent is a string (usually a dom element name..)
18513             if (typeof(obj.parent) == 'string') {
18514                 this.elmodules.push(obj);
18515                 return;
18516             }
18517             if (obj.parent.constructor != Roo.XComponent) {
18518                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18519             }
18520             if (!obj.parent.modules) {
18521                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18522                     function(o) { return o.order + '' }
18523                 );
18524             }
18525             if (obj.parent.disabled) {
18526                 obj.disabled = true;
18527             }
18528             obj.parent.modules.add(obj);
18529         }, this);
18530     },
18531     
18532      /**
18533      * make a list of modules to build.
18534      * @return {Array} list of modules. 
18535      */ 
18536     
18537     buildOrder : function()
18538     {
18539         var _this = this;
18540         var cmp = function(a,b) {   
18541             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18542         };
18543         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18544             throw "No top level modules to build";
18545         }
18546         
18547         // make a flat list in order of modules to build.
18548         var mods = this.topModule ? [ this.topModule ] : [];
18549                 
18550         
18551         // elmodules (is a list of DOM based modules )
18552         Roo.each(this.elmodules, function(e) {
18553             mods.push(e);
18554             if (!this.topModule &&
18555                 typeof(e.parent) == 'string' &&
18556                 e.parent.substring(0,1) == '#' &&
18557                 Roo.get(e.parent.substr(1))
18558                ) {
18559                 
18560                 _this.topModule = e;
18561             }
18562             
18563         });
18564
18565         
18566         // add modules to their parents..
18567         var addMod = function(m) {
18568             Roo.debug && Roo.log("build Order: add: " + m.name);
18569                 
18570             mods.push(m);
18571             if (m.modules && !m.disabled) {
18572                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18573                 m.modules.keySort('ASC',  cmp );
18574                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18575     
18576                 m.modules.each(addMod);
18577             } else {
18578                 Roo.debug && Roo.log("build Order: no child modules");
18579             }
18580             // not sure if this is used any more..
18581             if (m.finalize) {
18582                 m.finalize.name = m.name + " (clean up) ";
18583                 mods.push(m.finalize);
18584             }
18585             
18586         }
18587         if (this.topModule && this.topModule.modules) { 
18588             this.topModule.modules.keySort('ASC',  cmp );
18589             this.topModule.modules.each(addMod);
18590         } 
18591         return mods;
18592     },
18593     
18594      /**
18595      * Build the registered modules.
18596      * @param {Object} parent element.
18597      * @param {Function} optional method to call after module has been added.
18598      * 
18599      */ 
18600    
18601     build : function(opts) 
18602     {
18603         
18604         if (typeof(opts) != 'undefined') {
18605             Roo.apply(this,opts);
18606         }
18607         
18608         this.preBuild();
18609         var mods = this.buildOrder();
18610       
18611         //this.allmods = mods;
18612         //Roo.debug && Roo.log(mods);
18613         //return;
18614         if (!mods.length) { // should not happen
18615             throw "NO modules!!!";
18616         }
18617         
18618         
18619         var msg = "Building Interface...";
18620         // flash it up as modal - so we store the mask!?
18621         if (!this.hideProgress && Roo.MessageBox) {
18622             Roo.MessageBox.show({ title: 'loading' });
18623             Roo.MessageBox.show({
18624                title: "Please wait...",
18625                msg: msg,
18626                width:450,
18627                progress:true,
18628                buttons : false,
18629                closable:false,
18630                modal: false
18631               
18632             });
18633         }
18634         var total = mods.length;
18635         
18636         var _this = this;
18637         var progressRun = function() {
18638             if (!mods.length) {
18639                 Roo.debug && Roo.log('hide?');
18640                 if (!this.hideProgress && Roo.MessageBox) {
18641                     Roo.MessageBox.hide();
18642                 }
18643                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18644                 
18645                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18646                 
18647                 // THE END...
18648                 return false;   
18649             }
18650             
18651             var m = mods.shift();
18652             
18653             
18654             Roo.debug && Roo.log(m);
18655             // not sure if this is supported any more.. - modules that are are just function
18656             if (typeof(m) == 'function') { 
18657                 m.call(this);
18658                 return progressRun.defer(10, _this);
18659             } 
18660             
18661             
18662             msg = "Building Interface " + (total  - mods.length) + 
18663                     " of " + total + 
18664                     (m.name ? (' - ' + m.name) : '');
18665                         Roo.debug && Roo.log(msg);
18666             if (!_this.hideProgress &&  Roo.MessageBox) { 
18667                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18668             }
18669             
18670          
18671             // is the module disabled?
18672             var disabled = (typeof(m.disabled) == 'function') ?
18673                 m.disabled.call(m.module.disabled) : m.disabled;    
18674             
18675             
18676             if (disabled) {
18677                 return progressRun(); // we do not update the display!
18678             }
18679             
18680             // now build 
18681             
18682                         
18683                         
18684             m.render();
18685             // it's 10 on top level, and 1 on others??? why...
18686             return progressRun.defer(10, _this);
18687              
18688         }
18689         progressRun.defer(1, _this);
18690      
18691         
18692         
18693     },
18694     /**
18695      * Overlay a set of modified strings onto a component
18696      * This is dependant on our builder exporting the strings and 'named strings' elements.
18697      * 
18698      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18699      * @param {Object} associative array of 'named' string and it's new value.
18700      * 
18701      */
18702         overlayStrings : function( component, strings )
18703     {
18704         if (typeof(component['_named_strings']) == 'undefined') {
18705             throw "ERROR: component does not have _named_strings";
18706         }
18707         for ( var k in strings ) {
18708             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18709             if (md !== false) {
18710                 component['_strings'][md] = strings[k];
18711             } else {
18712                 Roo.log('could not find named string: ' + k + ' in');
18713                 Roo.log(component);
18714             }
18715             
18716         }
18717         
18718     },
18719     
18720         
18721         /**
18722          * Event Object.
18723          *
18724          *
18725          */
18726         event: false, 
18727     /**
18728          * wrapper for event.on - aliased later..  
18729          * Typically use to register a event handler for register:
18730          *
18731          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18732          *
18733          */
18734     on : false
18735    
18736     
18737     
18738 });
18739
18740 Roo.XComponent.event = new Roo.util.Observable({
18741                 events : { 
18742                         /**
18743                          * @event register
18744                          * Fires when an Component is registered,
18745                          * set the disable property on the Component to stop registration.
18746                          * @param {Roo.XComponent} c the component being registerd.
18747                          * 
18748                          */
18749                         'register' : true,
18750             /**
18751                          * @event beforebuild
18752                          * Fires before each Component is built
18753                          * can be used to apply permissions.
18754                          * @param {Roo.XComponent} c the component being registerd.
18755                          * 
18756                          */
18757                         'beforebuild' : true,
18758                         /**
18759                          * @event buildcomplete
18760                          * Fires on the top level element when all elements have been built
18761                          * @param {Roo.XComponent} the top level component.
18762                          */
18763                         'buildcomplete' : true
18764                         
18765                 }
18766 });
18767
18768 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18769  //
18770  /**
18771  * marked - a markdown parser
18772  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18773  * https://github.com/chjj/marked
18774  */
18775
18776
18777 /**
18778  *
18779  * Roo.Markdown - is a very crude wrapper around marked..
18780  *
18781  * usage:
18782  * 
18783  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18784  * 
18785  * Note: move the sample code to the bottom of this
18786  * file before uncommenting it.
18787  *
18788  */
18789
18790 Roo.Markdown = {};
18791 Roo.Markdown.toHtml = function(text) {
18792     
18793     var c = new Roo.Markdown.marked.setOptions({
18794             renderer: new Roo.Markdown.marked.Renderer(),
18795             gfm: true,
18796             tables: true,
18797             breaks: false,
18798             pedantic: false,
18799             sanitize: false,
18800             smartLists: true,
18801             smartypants: false
18802           });
18803     // A FEW HACKS!!?
18804     
18805     text = text.replace(/\\\n/g,' ');
18806     return Roo.Markdown.marked(text);
18807 };
18808 //
18809 // converter
18810 //
18811 // Wraps all "globals" so that the only thing
18812 // exposed is makeHtml().
18813 //
18814 (function() {
18815     
18816      /**
18817          * eval:var:escape
18818          * eval:var:unescape
18819          * eval:var:replace
18820          */
18821       
18822     /**
18823      * Helpers
18824      */
18825     
18826     var escape = function (html, encode) {
18827       return html
18828         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18829         .replace(/</g, '&lt;')
18830         .replace(/>/g, '&gt;')
18831         .replace(/"/g, '&quot;')
18832         .replace(/'/g, '&#39;');
18833     }
18834     
18835     var unescape = function (html) {
18836         // explicitly match decimal, hex, and named HTML entities 
18837       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18838         n = n.toLowerCase();
18839         if (n === 'colon') { return ':'; }
18840         if (n.charAt(0) === '#') {
18841           return n.charAt(1) === 'x'
18842             ? String.fromCharCode(parseInt(n.substring(2), 16))
18843             : String.fromCharCode(+n.substring(1));
18844         }
18845         return '';
18846       });
18847     }
18848     
18849     var replace = function (regex, opt) {
18850       regex = regex.source;
18851       opt = opt || '';
18852       return function self(name, val) {
18853         if (!name) { return new RegExp(regex, opt); }
18854         val = val.source || val;
18855         val = val.replace(/(^|[^\[])\^/g, '$1');
18856         regex = regex.replace(name, val);
18857         return self;
18858       };
18859     }
18860
18861
18862          /**
18863          * eval:var:noop
18864     */
18865     var noop = function () {}
18866     noop.exec = noop;
18867     
18868          /**
18869          * eval:var:merge
18870     */
18871     var merge = function (obj) {
18872       var i = 1
18873         , target
18874         , key;
18875     
18876       for (; i < arguments.length; i++) {
18877         target = arguments[i];
18878         for (key in target) {
18879           if (Object.prototype.hasOwnProperty.call(target, key)) {
18880             obj[key] = target[key];
18881           }
18882         }
18883       }
18884     
18885       return obj;
18886     }
18887     
18888     
18889     /**
18890      * Block-Level Grammar
18891      */
18892     
18893     
18894     
18895     
18896     var block = {
18897       newline: /^\n+/,
18898       code: /^( {4}[^\n]+\n*)+/,
18899       fences: noop,
18900       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18901       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18902       nptable: noop,
18903       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18904       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18905       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18906       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18907       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18908       table: noop,
18909       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18910       text: /^[^\n]+/
18911     };
18912     
18913     block.bullet = /(?:[*+-]|\d+\.)/;
18914     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18915     block.item = replace(block.item, 'gm')
18916       (/bull/g, block.bullet)
18917       ();
18918     
18919     block.list = replace(block.list)
18920       (/bull/g, block.bullet)
18921       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18922       ('def', '\\n+(?=' + block.def.source + ')')
18923       ();
18924     
18925     block.blockquote = replace(block.blockquote)
18926       ('def', block.def)
18927       ();
18928     
18929     block._tag = '(?!(?:'
18930       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18931       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18932       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18933     
18934     block.html = replace(block.html)
18935       ('comment', /<!--[\s\S]*?-->/)
18936       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18937       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18938       (/tag/g, block._tag)
18939       ();
18940     
18941     block.paragraph = replace(block.paragraph)
18942       ('hr', block.hr)
18943       ('heading', block.heading)
18944       ('lheading', block.lheading)
18945       ('blockquote', block.blockquote)
18946       ('tag', '<' + block._tag)
18947       ('def', block.def)
18948       ();
18949     
18950     /**
18951      * Normal Block Grammar
18952      */
18953     
18954     block.normal = merge({}, block);
18955     
18956     /**
18957      * GFM Block Grammar
18958      */
18959     
18960     block.gfm = merge({}, block.normal, {
18961       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18962       paragraph: /^/,
18963       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18964     });
18965     
18966     block.gfm.paragraph = replace(block.paragraph)
18967       ('(?!', '(?!'
18968         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18969         + block.list.source.replace('\\1', '\\3') + '|')
18970       ();
18971     
18972     /**
18973      * GFM + Tables Block Grammar
18974      */
18975     
18976     block.tables = merge({}, block.gfm, {
18977       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18978       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18979     });
18980     
18981     /**
18982      * Block Lexer
18983      */
18984     
18985     var Lexer = function (options) {
18986       this.tokens = [];
18987       this.tokens.links = {};
18988       this.options = options || marked.defaults;
18989       this.rules = block.normal;
18990     
18991       if (this.options.gfm) {
18992         if (this.options.tables) {
18993           this.rules = block.tables;
18994         } else {
18995           this.rules = block.gfm;
18996         }
18997       }
18998     }
18999     
19000     /**
19001      * Expose Block Rules
19002      */
19003     
19004     Lexer.rules = block;
19005     
19006     /**
19007      * Static Lex Method
19008      */
19009     
19010     Lexer.lex = function(src, options) {
19011       var lexer = new Lexer(options);
19012       return lexer.lex(src);
19013     };
19014     
19015     /**
19016      * Preprocessing
19017      */
19018     
19019     Lexer.prototype.lex = function(src) {
19020       src = src
19021         .replace(/\r\n|\r/g, '\n')
19022         .replace(/\t/g, '    ')
19023         .replace(/\u00a0/g, ' ')
19024         .replace(/\u2424/g, '\n');
19025     
19026       return this.token(src, true);
19027     };
19028     
19029     /**
19030      * Lexing
19031      */
19032     
19033     Lexer.prototype.token = function(src, top, bq) {
19034       var src = src.replace(/^ +$/gm, '')
19035         , next
19036         , loose
19037         , cap
19038         , bull
19039         , b
19040         , item
19041         , space
19042         , i
19043         , l;
19044     
19045       while (src) {
19046         // newline
19047         if (cap = this.rules.newline.exec(src)) {
19048           src = src.substring(cap[0].length);
19049           if (cap[0].length > 1) {
19050             this.tokens.push({
19051               type: 'space'
19052             });
19053           }
19054         }
19055     
19056         // code
19057         if (cap = this.rules.code.exec(src)) {
19058           src = src.substring(cap[0].length);
19059           cap = cap[0].replace(/^ {4}/gm, '');
19060           this.tokens.push({
19061             type: 'code',
19062             text: !this.options.pedantic
19063               ? cap.replace(/\n+$/, '')
19064               : cap
19065           });
19066           continue;
19067         }
19068     
19069         // fences (gfm)
19070         if (cap = this.rules.fences.exec(src)) {
19071           src = src.substring(cap[0].length);
19072           this.tokens.push({
19073             type: 'code',
19074             lang: cap[2],
19075             text: cap[3] || ''
19076           });
19077           continue;
19078         }
19079     
19080         // heading
19081         if (cap = this.rules.heading.exec(src)) {
19082           src = src.substring(cap[0].length);
19083           this.tokens.push({
19084             type: 'heading',
19085             depth: cap[1].length,
19086             text: cap[2]
19087           });
19088           continue;
19089         }
19090     
19091         // table no leading pipe (gfm)
19092         if (top && (cap = this.rules.nptable.exec(src))) {
19093           src = src.substring(cap[0].length);
19094     
19095           item = {
19096             type: 'table',
19097             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19098             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19099             cells: cap[3].replace(/\n$/, '').split('\n')
19100           };
19101     
19102           for (i = 0; i < item.align.length; i++) {
19103             if (/^ *-+: *$/.test(item.align[i])) {
19104               item.align[i] = 'right';
19105             } else if (/^ *:-+: *$/.test(item.align[i])) {
19106               item.align[i] = 'center';
19107             } else if (/^ *:-+ *$/.test(item.align[i])) {
19108               item.align[i] = 'left';
19109             } else {
19110               item.align[i] = null;
19111             }
19112           }
19113     
19114           for (i = 0; i < item.cells.length; i++) {
19115             item.cells[i] = item.cells[i].split(/ *\| */);
19116           }
19117     
19118           this.tokens.push(item);
19119     
19120           continue;
19121         }
19122     
19123         // lheading
19124         if (cap = this.rules.lheading.exec(src)) {
19125           src = src.substring(cap[0].length);
19126           this.tokens.push({
19127             type: 'heading',
19128             depth: cap[2] === '=' ? 1 : 2,
19129             text: cap[1]
19130           });
19131           continue;
19132         }
19133     
19134         // hr
19135         if (cap = this.rules.hr.exec(src)) {
19136           src = src.substring(cap[0].length);
19137           this.tokens.push({
19138             type: 'hr'
19139           });
19140           continue;
19141         }
19142     
19143         // blockquote
19144         if (cap = this.rules.blockquote.exec(src)) {
19145           src = src.substring(cap[0].length);
19146     
19147           this.tokens.push({
19148             type: 'blockquote_start'
19149           });
19150     
19151           cap = cap[0].replace(/^ *> ?/gm, '');
19152     
19153           // Pass `top` to keep the current
19154           // "toplevel" state. This is exactly
19155           // how markdown.pl works.
19156           this.token(cap, top, true);
19157     
19158           this.tokens.push({
19159             type: 'blockquote_end'
19160           });
19161     
19162           continue;
19163         }
19164     
19165         // list
19166         if (cap = this.rules.list.exec(src)) {
19167           src = src.substring(cap[0].length);
19168           bull = cap[2];
19169     
19170           this.tokens.push({
19171             type: 'list_start',
19172             ordered: bull.length > 1
19173           });
19174     
19175           // Get each top-level item.
19176           cap = cap[0].match(this.rules.item);
19177     
19178           next = false;
19179           l = cap.length;
19180           i = 0;
19181     
19182           for (; i < l; i++) {
19183             item = cap[i];
19184     
19185             // Remove the list item's bullet
19186             // so it is seen as the next token.
19187             space = item.length;
19188             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19189     
19190             // Outdent whatever the
19191             // list item contains. Hacky.
19192             if (~item.indexOf('\n ')) {
19193               space -= item.length;
19194               item = !this.options.pedantic
19195                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19196                 : item.replace(/^ {1,4}/gm, '');
19197             }
19198     
19199             // Determine whether the next list item belongs here.
19200             // Backpedal if it does not belong in this list.
19201             if (this.options.smartLists && i !== l - 1) {
19202               b = block.bullet.exec(cap[i + 1])[0];
19203               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19204                 src = cap.slice(i + 1).join('\n') + src;
19205                 i = l - 1;
19206               }
19207             }
19208     
19209             // Determine whether item is loose or not.
19210             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19211             // for discount behavior.
19212             loose = next || /\n\n(?!\s*$)/.test(item);
19213             if (i !== l - 1) {
19214               next = item.charAt(item.length - 1) === '\n';
19215               if (!loose) { loose = next; }
19216             }
19217     
19218             this.tokens.push({
19219               type: loose
19220                 ? 'loose_item_start'
19221                 : 'list_item_start'
19222             });
19223     
19224             // Recurse.
19225             this.token(item, false, bq);
19226     
19227             this.tokens.push({
19228               type: 'list_item_end'
19229             });
19230           }
19231     
19232           this.tokens.push({
19233             type: 'list_end'
19234           });
19235     
19236           continue;
19237         }
19238     
19239         // html
19240         if (cap = this.rules.html.exec(src)) {
19241           src = src.substring(cap[0].length);
19242           this.tokens.push({
19243             type: this.options.sanitize
19244               ? 'paragraph'
19245               : 'html',
19246             pre: !this.options.sanitizer
19247               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19248             text: cap[0]
19249           });
19250           continue;
19251         }
19252     
19253         // def
19254         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19255           src = src.substring(cap[0].length);
19256           this.tokens.links[cap[1].toLowerCase()] = {
19257             href: cap[2],
19258             title: cap[3]
19259           };
19260           continue;
19261         }
19262     
19263         // table (gfm)
19264         if (top && (cap = this.rules.table.exec(src))) {
19265           src = src.substring(cap[0].length);
19266     
19267           item = {
19268             type: 'table',
19269             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19270             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19271             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19272           };
19273     
19274           for (i = 0; i < item.align.length; i++) {
19275             if (/^ *-+: *$/.test(item.align[i])) {
19276               item.align[i] = 'right';
19277             } else if (/^ *:-+: *$/.test(item.align[i])) {
19278               item.align[i] = 'center';
19279             } else if (/^ *:-+ *$/.test(item.align[i])) {
19280               item.align[i] = 'left';
19281             } else {
19282               item.align[i] = null;
19283             }
19284           }
19285     
19286           for (i = 0; i < item.cells.length; i++) {
19287             item.cells[i] = item.cells[i]
19288               .replace(/^ *\| *| *\| *$/g, '')
19289               .split(/ *\| */);
19290           }
19291     
19292           this.tokens.push(item);
19293     
19294           continue;
19295         }
19296     
19297         // top-level paragraph
19298         if (top && (cap = this.rules.paragraph.exec(src))) {
19299           src = src.substring(cap[0].length);
19300           this.tokens.push({
19301             type: 'paragraph',
19302             text: cap[1].charAt(cap[1].length - 1) === '\n'
19303               ? cap[1].slice(0, -1)
19304               : cap[1]
19305           });
19306           continue;
19307         }
19308     
19309         // text
19310         if (cap = this.rules.text.exec(src)) {
19311           // Top-level should never reach here.
19312           src = src.substring(cap[0].length);
19313           this.tokens.push({
19314             type: 'text',
19315             text: cap[0]
19316           });
19317           continue;
19318         }
19319     
19320         if (src) {
19321           throw new
19322             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19323         }
19324       }
19325     
19326       return this.tokens;
19327     };
19328     
19329     /**
19330      * Inline-Level Grammar
19331      */
19332     
19333     var inline = {
19334       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19335       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19336       url: noop,
19337       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19338       link: /^!?\[(inside)\]\(href\)/,
19339       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19340       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19341       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19342       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19343       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19344       br: /^ {2,}\n(?!\s*$)/,
19345       del: noop,
19346       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19347     };
19348     
19349     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19350     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19351     
19352     inline.link = replace(inline.link)
19353       ('inside', inline._inside)
19354       ('href', inline._href)
19355       ();
19356     
19357     inline.reflink = replace(inline.reflink)
19358       ('inside', inline._inside)
19359       ();
19360     
19361     /**
19362      * Normal Inline Grammar
19363      */
19364     
19365     inline.normal = merge({}, inline);
19366     
19367     /**
19368      * Pedantic Inline Grammar
19369      */
19370     
19371     inline.pedantic = merge({}, inline.normal, {
19372       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19373       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19374     });
19375     
19376     /**
19377      * GFM Inline Grammar
19378      */
19379     
19380     inline.gfm = merge({}, inline.normal, {
19381       escape: replace(inline.escape)('])', '~|])')(),
19382       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19383       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19384       text: replace(inline.text)
19385         (']|', '~]|')
19386         ('|', '|https?://|')
19387         ()
19388     });
19389     
19390     /**
19391      * GFM + Line Breaks Inline Grammar
19392      */
19393     
19394     inline.breaks = merge({}, inline.gfm, {
19395       br: replace(inline.br)('{2,}', '*')(),
19396       text: replace(inline.gfm.text)('{2,}', '*')()
19397     });
19398     
19399     /**
19400      * Inline Lexer & Compiler
19401      */
19402     
19403     var InlineLexer  = function (links, options) {
19404       this.options = options || marked.defaults;
19405       this.links = links;
19406       this.rules = inline.normal;
19407       this.renderer = this.options.renderer || new Renderer;
19408       this.renderer.options = this.options;
19409     
19410       if (!this.links) {
19411         throw new
19412           Error('Tokens array requires a `links` property.');
19413       }
19414     
19415       if (this.options.gfm) {
19416         if (this.options.breaks) {
19417           this.rules = inline.breaks;
19418         } else {
19419           this.rules = inline.gfm;
19420         }
19421       } else if (this.options.pedantic) {
19422         this.rules = inline.pedantic;
19423       }
19424     }
19425     
19426     /**
19427      * Expose Inline Rules
19428      */
19429     
19430     InlineLexer.rules = inline;
19431     
19432     /**
19433      * Static Lexing/Compiling Method
19434      */
19435     
19436     InlineLexer.output = function(src, links, options) {
19437       var inline = new InlineLexer(links, options);
19438       return inline.output(src);
19439     };
19440     
19441     /**
19442      * Lexing/Compiling
19443      */
19444     
19445     InlineLexer.prototype.output = function(src) {
19446       var out = ''
19447         , link
19448         , text
19449         , href
19450         , cap;
19451     
19452       while (src) {
19453         // escape
19454         if (cap = this.rules.escape.exec(src)) {
19455           src = src.substring(cap[0].length);
19456           out += cap[1];
19457           continue;
19458         }
19459     
19460         // autolink
19461         if (cap = this.rules.autolink.exec(src)) {
19462           src = src.substring(cap[0].length);
19463           if (cap[2] === '@') {
19464             text = cap[1].charAt(6) === ':'
19465               ? this.mangle(cap[1].substring(7))
19466               : this.mangle(cap[1]);
19467             href = this.mangle('mailto:') + text;
19468           } else {
19469             text = escape(cap[1]);
19470             href = text;
19471           }
19472           out += this.renderer.link(href, null, text);
19473           continue;
19474         }
19475     
19476         // url (gfm)
19477         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19478           src = src.substring(cap[0].length);
19479           text = escape(cap[1]);
19480           href = text;
19481           out += this.renderer.link(href, null, text);
19482           continue;
19483         }
19484     
19485         // tag
19486         if (cap = this.rules.tag.exec(src)) {
19487           if (!this.inLink && /^<a /i.test(cap[0])) {
19488             this.inLink = true;
19489           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19490             this.inLink = false;
19491           }
19492           src = src.substring(cap[0].length);
19493           out += this.options.sanitize
19494             ? this.options.sanitizer
19495               ? this.options.sanitizer(cap[0])
19496               : escape(cap[0])
19497             : cap[0];
19498           continue;
19499         }
19500     
19501         // link
19502         if (cap = this.rules.link.exec(src)) {
19503           src = src.substring(cap[0].length);
19504           this.inLink = true;
19505           out += this.outputLink(cap, {
19506             href: cap[2],
19507             title: cap[3]
19508           });
19509           this.inLink = false;
19510           continue;
19511         }
19512     
19513         // reflink, nolink
19514         if ((cap = this.rules.reflink.exec(src))
19515             || (cap = this.rules.nolink.exec(src))) {
19516           src = src.substring(cap[0].length);
19517           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19518           link = this.links[link.toLowerCase()];
19519           if (!link || !link.href) {
19520             out += cap[0].charAt(0);
19521             src = cap[0].substring(1) + src;
19522             continue;
19523           }
19524           this.inLink = true;
19525           out += this.outputLink(cap, link);
19526           this.inLink = false;
19527           continue;
19528         }
19529     
19530         // strong
19531         if (cap = this.rules.strong.exec(src)) {
19532           src = src.substring(cap[0].length);
19533           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19534           continue;
19535         }
19536     
19537         // em
19538         if (cap = this.rules.em.exec(src)) {
19539           src = src.substring(cap[0].length);
19540           out += this.renderer.em(this.output(cap[2] || cap[1]));
19541           continue;
19542         }
19543     
19544         // code
19545         if (cap = this.rules.code.exec(src)) {
19546           src = src.substring(cap[0].length);
19547           out += this.renderer.codespan(escape(cap[2], true));
19548           continue;
19549         }
19550     
19551         // br
19552         if (cap = this.rules.br.exec(src)) {
19553           src = src.substring(cap[0].length);
19554           out += this.renderer.br();
19555           continue;
19556         }
19557     
19558         // del (gfm)
19559         if (cap = this.rules.del.exec(src)) {
19560           src = src.substring(cap[0].length);
19561           out += this.renderer.del(this.output(cap[1]));
19562           continue;
19563         }
19564     
19565         // text
19566         if (cap = this.rules.text.exec(src)) {
19567           src = src.substring(cap[0].length);
19568           out += this.renderer.text(escape(this.smartypants(cap[0])));
19569           continue;
19570         }
19571     
19572         if (src) {
19573           throw new
19574             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19575         }
19576       }
19577     
19578       return out;
19579     };
19580     
19581     /**
19582      * Compile Link
19583      */
19584     
19585     InlineLexer.prototype.outputLink = function(cap, link) {
19586       var href = escape(link.href)
19587         , title = link.title ? escape(link.title) : null;
19588     
19589       return cap[0].charAt(0) !== '!'
19590         ? this.renderer.link(href, title, this.output(cap[1]))
19591         : this.renderer.image(href, title, escape(cap[1]));
19592     };
19593     
19594     /**
19595      * Smartypants Transformations
19596      */
19597     
19598     InlineLexer.prototype.smartypants = function(text) {
19599       if (!this.options.smartypants)  { return text; }
19600       return text
19601         // em-dashes
19602         .replace(/---/g, '\u2014')
19603         // en-dashes
19604         .replace(/--/g, '\u2013')
19605         // opening singles
19606         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19607         // closing singles & apostrophes
19608         .replace(/'/g, '\u2019')
19609         // opening doubles
19610         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19611         // closing doubles
19612         .replace(/"/g, '\u201d')
19613         // ellipses
19614         .replace(/\.{3}/g, '\u2026');
19615     };
19616     
19617     /**
19618      * Mangle Links
19619      */
19620     
19621     InlineLexer.prototype.mangle = function(text) {
19622       if (!this.options.mangle) { return text; }
19623       var out = ''
19624         , l = text.length
19625         , i = 0
19626         , ch;
19627     
19628       for (; i < l; i++) {
19629         ch = text.charCodeAt(i);
19630         if (Math.random() > 0.5) {
19631           ch = 'x' + ch.toString(16);
19632         }
19633         out += '&#' + ch + ';';
19634       }
19635     
19636       return out;
19637     };
19638     
19639     /**
19640      * Renderer
19641      */
19642     
19643      /**
19644          * eval:var:Renderer
19645     */
19646     
19647     var Renderer   = function (options) {
19648       this.options = options || {};
19649     }
19650     
19651     Renderer.prototype.code = function(code, lang, escaped) {
19652       if (this.options.highlight) {
19653         var out = this.options.highlight(code, lang);
19654         if (out != null && out !== code) {
19655           escaped = true;
19656           code = out;
19657         }
19658       } else {
19659             // hack!!! - it's already escapeD?
19660             escaped = true;
19661       }
19662     
19663       if (!lang) {
19664         return '<pre><code>'
19665           + (escaped ? code : escape(code, true))
19666           + '\n</code></pre>';
19667       }
19668     
19669       return '<pre><code class="'
19670         + this.options.langPrefix
19671         + escape(lang, true)
19672         + '">'
19673         + (escaped ? code : escape(code, true))
19674         + '\n</code></pre>\n';
19675     };
19676     
19677     Renderer.prototype.blockquote = function(quote) {
19678       return '<blockquote>\n' + quote + '</blockquote>\n';
19679     };
19680     
19681     Renderer.prototype.html = function(html) {
19682       return html;
19683     };
19684     
19685     Renderer.prototype.heading = function(text, level, raw) {
19686       return '<h'
19687         + level
19688         + ' id="'
19689         + this.options.headerPrefix
19690         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19691         + '">'
19692         + text
19693         + '</h'
19694         + level
19695         + '>\n';
19696     };
19697     
19698     Renderer.prototype.hr = function() {
19699       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19700     };
19701     
19702     Renderer.prototype.list = function(body, ordered) {
19703       var type = ordered ? 'ol' : 'ul';
19704       return '<' + type + '>\n' + body + '</' + type + '>\n';
19705     };
19706     
19707     Renderer.prototype.listitem = function(text) {
19708       return '<li>' + text + '</li>\n';
19709     };
19710     
19711     Renderer.prototype.paragraph = function(text) {
19712       return '<p>' + text + '</p>\n';
19713     };
19714     
19715     Renderer.prototype.table = function(header, body) {
19716       return '<table class="table table-striped">\n'
19717         + '<thead>\n'
19718         + header
19719         + '</thead>\n'
19720         + '<tbody>\n'
19721         + body
19722         + '</tbody>\n'
19723         + '</table>\n';
19724     };
19725     
19726     Renderer.prototype.tablerow = function(content) {
19727       return '<tr>\n' + content + '</tr>\n';
19728     };
19729     
19730     Renderer.prototype.tablecell = function(content, flags) {
19731       var type = flags.header ? 'th' : 'td';
19732       var tag = flags.align
19733         ? '<' + type + ' style="text-align:' + flags.align + '">'
19734         : '<' + type + '>';
19735       return tag + content + '</' + type + '>\n';
19736     };
19737     
19738     // span level renderer
19739     Renderer.prototype.strong = function(text) {
19740       return '<strong>' + text + '</strong>';
19741     };
19742     
19743     Renderer.prototype.em = function(text) {
19744       return '<em>' + text + '</em>';
19745     };
19746     
19747     Renderer.prototype.codespan = function(text) {
19748       return '<code>' + text + '</code>';
19749     };
19750     
19751     Renderer.prototype.br = function() {
19752       return this.options.xhtml ? '<br/>' : '<br>';
19753     };
19754     
19755     Renderer.prototype.del = function(text) {
19756       return '<del>' + text + '</del>';
19757     };
19758     
19759     Renderer.prototype.link = function(href, title, text) {
19760       if (this.options.sanitize) {
19761         try {
19762           var prot = decodeURIComponent(unescape(href))
19763             .replace(/[^\w:]/g, '')
19764             .toLowerCase();
19765         } catch (e) {
19766           return '';
19767         }
19768         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19769           return '';
19770         }
19771       }
19772       var out = '<a href="' + href + '"';
19773       if (title) {
19774         out += ' title="' + title + '"';
19775       }
19776       out += '>' + text + '</a>';
19777       return out;
19778     };
19779     
19780     Renderer.prototype.image = function(href, title, text) {
19781       var out = '<img src="' + href + '" alt="' + text + '"';
19782       if (title) {
19783         out += ' title="' + title + '"';
19784       }
19785       out += this.options.xhtml ? '/>' : '>';
19786       return out;
19787     };
19788     
19789     Renderer.prototype.text = function(text) {
19790       return text;
19791     };
19792     
19793     /**
19794      * Parsing & Compiling
19795      */
19796          /**
19797          * eval:var:Parser
19798     */
19799     
19800     var Parser= function (options) {
19801       this.tokens = [];
19802       this.token = null;
19803       this.options = options || marked.defaults;
19804       this.options.renderer = this.options.renderer || new Renderer;
19805       this.renderer = this.options.renderer;
19806       this.renderer.options = this.options;
19807     }
19808     
19809     /**
19810      * Static Parse Method
19811      */
19812     
19813     Parser.parse = function(src, options, renderer) {
19814       var parser = new Parser(options, renderer);
19815       return parser.parse(src);
19816     };
19817     
19818     /**
19819      * Parse Loop
19820      */
19821     
19822     Parser.prototype.parse = function(src) {
19823       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19824       this.tokens = src.reverse();
19825     
19826       var out = '';
19827       while (this.next()) {
19828         out += this.tok();
19829       }
19830     
19831       return out;
19832     };
19833     
19834     /**
19835      * Next Token
19836      */
19837     
19838     Parser.prototype.next = function() {
19839       return this.token = this.tokens.pop();
19840     };
19841     
19842     /**
19843      * Preview Next Token
19844      */
19845     
19846     Parser.prototype.peek = function() {
19847       return this.tokens[this.tokens.length - 1] || 0;
19848     };
19849     
19850     /**
19851      * Parse Text Tokens
19852      */
19853     
19854     Parser.prototype.parseText = function() {
19855       var body = this.token.text;
19856     
19857       while (this.peek().type === 'text') {
19858         body += '\n' + this.next().text;
19859       }
19860     
19861       return this.inline.output(body);
19862     };
19863     
19864     /**
19865      * Parse Current Token
19866      */
19867     
19868     Parser.prototype.tok = function() {
19869       switch (this.token.type) {
19870         case 'space': {
19871           return '';
19872         }
19873         case 'hr': {
19874           return this.renderer.hr();
19875         }
19876         case 'heading': {
19877           return this.renderer.heading(
19878             this.inline.output(this.token.text),
19879             this.token.depth,
19880             this.token.text);
19881         }
19882         case 'code': {
19883           return this.renderer.code(this.token.text,
19884             this.token.lang,
19885             this.token.escaped);
19886         }
19887         case 'table': {
19888           var header = ''
19889             , body = ''
19890             , i
19891             , row
19892             , cell
19893             , flags
19894             , j;
19895     
19896           // header
19897           cell = '';
19898           for (i = 0; i < this.token.header.length; i++) {
19899             flags = { header: true, align: this.token.align[i] };
19900             cell += this.renderer.tablecell(
19901               this.inline.output(this.token.header[i]),
19902               { header: true, align: this.token.align[i] }
19903             );
19904           }
19905           header += this.renderer.tablerow(cell);
19906     
19907           for (i = 0; i < this.token.cells.length; i++) {
19908             row = this.token.cells[i];
19909     
19910             cell = '';
19911             for (j = 0; j < row.length; j++) {
19912               cell += this.renderer.tablecell(
19913                 this.inline.output(row[j]),
19914                 { header: false, align: this.token.align[j] }
19915               );
19916             }
19917     
19918             body += this.renderer.tablerow(cell);
19919           }
19920           return this.renderer.table(header, body);
19921         }
19922         case 'blockquote_start': {
19923           var body = '';
19924     
19925           while (this.next().type !== 'blockquote_end') {
19926             body += this.tok();
19927           }
19928     
19929           return this.renderer.blockquote(body);
19930         }
19931         case 'list_start': {
19932           var body = ''
19933             , ordered = this.token.ordered;
19934     
19935           while (this.next().type !== 'list_end') {
19936             body += this.tok();
19937           }
19938     
19939           return this.renderer.list(body, ordered);
19940         }
19941         case 'list_item_start': {
19942           var body = '';
19943     
19944           while (this.next().type !== 'list_item_end') {
19945             body += this.token.type === 'text'
19946               ? this.parseText()
19947               : this.tok();
19948           }
19949     
19950           return this.renderer.listitem(body);
19951         }
19952         case 'loose_item_start': {
19953           var body = '';
19954     
19955           while (this.next().type !== 'list_item_end') {
19956             body += this.tok();
19957           }
19958     
19959           return this.renderer.listitem(body);
19960         }
19961         case 'html': {
19962           var html = !this.token.pre && !this.options.pedantic
19963             ? this.inline.output(this.token.text)
19964             : this.token.text;
19965           return this.renderer.html(html);
19966         }
19967         case 'paragraph': {
19968           return this.renderer.paragraph(this.inline.output(this.token.text));
19969         }
19970         case 'text': {
19971           return this.renderer.paragraph(this.parseText());
19972         }
19973       }
19974     };
19975   
19976     
19977     /**
19978      * Marked
19979      */
19980          /**
19981          * eval:var:marked
19982     */
19983     var marked = function (src, opt, callback) {
19984       if (callback || typeof opt === 'function') {
19985         if (!callback) {
19986           callback = opt;
19987           opt = null;
19988         }
19989     
19990         opt = merge({}, marked.defaults, opt || {});
19991     
19992         var highlight = opt.highlight
19993           , tokens
19994           , pending
19995           , i = 0;
19996     
19997         try {
19998           tokens = Lexer.lex(src, opt)
19999         } catch (e) {
20000           return callback(e);
20001         }
20002     
20003         pending = tokens.length;
20004          /**
20005          * eval:var:done
20006     */
20007         var done = function(err) {
20008           if (err) {
20009             opt.highlight = highlight;
20010             return callback(err);
20011           }
20012     
20013           var out;
20014     
20015           try {
20016             out = Parser.parse(tokens, opt);
20017           } catch (e) {
20018             err = e;
20019           }
20020     
20021           opt.highlight = highlight;
20022     
20023           return err
20024             ? callback(err)
20025             : callback(null, out);
20026         };
20027     
20028         if (!highlight || highlight.length < 3) {
20029           return done();
20030         }
20031     
20032         delete opt.highlight;
20033     
20034         if (!pending) { return done(); }
20035     
20036         for (; i < tokens.length; i++) {
20037           (function(token) {
20038             if (token.type !== 'code') {
20039               return --pending || done();
20040             }
20041             return highlight(token.text, token.lang, function(err, code) {
20042               if (err) { return done(err); }
20043               if (code == null || code === token.text) {
20044                 return --pending || done();
20045               }
20046               token.text = code;
20047               token.escaped = true;
20048               --pending || done();
20049             });
20050           })(tokens[i]);
20051         }
20052     
20053         return;
20054       }
20055       try {
20056         if (opt) { opt = merge({}, marked.defaults, opt); }
20057         return Parser.parse(Lexer.lex(src, opt), opt);
20058       } catch (e) {
20059         e.message += '\nPlease report this to https://github.com/chjj/marked.';
20060         if ((opt || marked.defaults).silent) {
20061           return '<p>An error occured:</p><pre>'
20062             + escape(e.message + '', true)
20063             + '</pre>';
20064         }
20065         throw e;
20066       }
20067     }
20068     
20069     /**
20070      * Options
20071      */
20072     
20073     marked.options =
20074     marked.setOptions = function(opt) {
20075       merge(marked.defaults, opt);
20076       return marked;
20077     };
20078     
20079     marked.defaults = {
20080       gfm: true,
20081       tables: true,
20082       breaks: false,
20083       pedantic: false,
20084       sanitize: false,
20085       sanitizer: null,
20086       mangle: true,
20087       smartLists: false,
20088       silent: false,
20089       highlight: null,
20090       langPrefix: 'lang-',
20091       smartypants: false,
20092       headerPrefix: '',
20093       renderer: new Renderer,
20094       xhtml: false
20095     };
20096     
20097     /**
20098      * Expose
20099      */
20100     
20101     marked.Parser = Parser;
20102     marked.parser = Parser.parse;
20103     
20104     marked.Renderer = Renderer;
20105     
20106     marked.Lexer = Lexer;
20107     marked.lexer = Lexer.lex;
20108     
20109     marked.InlineLexer = InlineLexer;
20110     marked.inlineLexer = InlineLexer.output;
20111     
20112     marked.parse = marked;
20113     
20114     Roo.Markdown.marked = marked;
20115
20116 })();/*
20117  * Based on:
20118  * Ext JS Library 1.1.1
20119  * Copyright(c) 2006-2007, Ext JS, LLC.
20120  *
20121  * Originally Released Under LGPL - original licence link has changed is not relivant.
20122  *
20123  * Fork - LGPL
20124  * <script type="text/javascript">
20125  */
20126
20127
20128
20129 /*
20130  * These classes are derivatives of the similarly named classes in the YUI Library.
20131  * The original license:
20132  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20133  * Code licensed under the BSD License:
20134  * http://developer.yahoo.net/yui/license.txt
20135  */
20136
20137 (function() {
20138
20139 var Event=Roo.EventManager;
20140 var Dom=Roo.lib.Dom;
20141
20142 /**
20143  * @class Roo.dd.DragDrop
20144  * @extends Roo.util.Observable
20145  * Defines the interface and base operation of items that that can be
20146  * dragged or can be drop targets.  It was designed to be extended, overriding
20147  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20148  * Up to three html elements can be associated with a DragDrop instance:
20149  * <ul>
20150  * <li>linked element: the element that is passed into the constructor.
20151  * This is the element which defines the boundaries for interaction with
20152  * other DragDrop objects.</li>
20153  * <li>handle element(s): The drag operation only occurs if the element that
20154  * was clicked matches a handle element.  By default this is the linked
20155  * element, but there are times that you will want only a portion of the
20156  * linked element to initiate the drag operation, and the setHandleElId()
20157  * method provides a way to define this.</li>
20158  * <li>drag element: this represents the element that would be moved along
20159  * with the cursor during a drag operation.  By default, this is the linked
20160  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
20161  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20162  * </li>
20163  * </ul>
20164  * This class should not be instantiated until the onload event to ensure that
20165  * the associated elements are available.
20166  * The following would define a DragDrop obj that would interact with any
20167  * other DragDrop obj in the "group1" group:
20168  * <pre>
20169  *  dd = new Roo.dd.DragDrop("div1", "group1");
20170  * </pre>
20171  * Since none of the event handlers have been implemented, nothing would
20172  * actually happen if you were to run the code above.  Normally you would
20173  * override this class or one of the default implementations, but you can
20174  * also override the methods you want on an instance of the class...
20175  * <pre>
20176  *  dd.onDragDrop = function(e, id) {
20177  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
20178  *  }
20179  * </pre>
20180  * @constructor
20181  * @param {String} id of the element that is linked to this instance
20182  * @param {String} sGroup the group of related DragDrop objects
20183  * @param {object} config an object containing configurable attributes
20184  *                Valid properties for DragDrop:
20185  *                    padding, isTarget, maintainOffset, primaryButtonOnly
20186  */
20187 Roo.dd.DragDrop = function(id, sGroup, config) {
20188     if (id) {
20189         this.init(id, sGroup, config);
20190     }
20191     
20192 };
20193
20194 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20195
20196     /**
20197      * The id of the element associated with this object.  This is what we
20198      * refer to as the "linked element" because the size and position of
20199      * this element is used to determine when the drag and drop objects have
20200      * interacted.
20201      * @property id
20202      * @type String
20203      */
20204     id: null,
20205
20206     /**
20207      * Configuration attributes passed into the constructor
20208      * @property config
20209      * @type object
20210      */
20211     config: null,
20212
20213     /**
20214      * The id of the element that will be dragged.  By default this is same
20215      * as the linked element , but could be changed to another element. Ex:
20216      * Roo.dd.DDProxy
20217      * @property dragElId
20218      * @type String
20219      * @private
20220      */
20221     dragElId: null,
20222
20223     /**
20224      * the id of the element that initiates the drag operation.  By default
20225      * this is the linked element, but could be changed to be a child of this
20226      * element.  This lets us do things like only starting the drag when the
20227      * header element within the linked html element is clicked.
20228      * @property handleElId
20229      * @type String
20230      * @private
20231      */
20232     handleElId: null,
20233
20234     /**
20235      * An associative array of HTML tags that will be ignored if clicked.
20236      * @property invalidHandleTypes
20237      * @type {string: string}
20238      */
20239     invalidHandleTypes: null,
20240
20241     /**
20242      * An associative array of ids for elements that will be ignored if clicked
20243      * @property invalidHandleIds
20244      * @type {string: string}
20245      */
20246     invalidHandleIds: null,
20247
20248     /**
20249      * An indexted array of css class names for elements that will be ignored
20250      * if clicked.
20251      * @property invalidHandleClasses
20252      * @type string[]
20253      */
20254     invalidHandleClasses: null,
20255
20256     /**
20257      * The linked element's absolute X position at the time the drag was
20258      * started
20259      * @property startPageX
20260      * @type int
20261      * @private
20262      */
20263     startPageX: 0,
20264
20265     /**
20266      * The linked element's absolute X position at the time the drag was
20267      * started
20268      * @property startPageY
20269      * @type int
20270      * @private
20271      */
20272     startPageY: 0,
20273
20274     /**
20275      * The group defines a logical collection of DragDrop objects that are
20276      * related.  Instances only get events when interacting with other
20277      * DragDrop object in the same group.  This lets us define multiple
20278      * groups using a single DragDrop subclass if we want.
20279      * @property groups
20280      * @type {string: string}
20281      */
20282     groups: null,
20283
20284     /**
20285      * Individual drag/drop instances can be locked.  This will prevent
20286      * onmousedown start drag.
20287      * @property locked
20288      * @type boolean
20289      * @private
20290      */
20291     locked: false,
20292
20293     /**
20294      * Lock this instance
20295      * @method lock
20296      */
20297     lock: function() { this.locked = true; },
20298
20299     /**
20300      * Unlock this instace
20301      * @method unlock
20302      */
20303     unlock: function() { this.locked = false; },
20304
20305     /**
20306      * By default, all insances can be a drop target.  This can be disabled by
20307      * setting isTarget to false.
20308      * @method isTarget
20309      * @type boolean
20310      */
20311     isTarget: true,
20312
20313     /**
20314      * The padding configured for this drag and drop object for calculating
20315      * the drop zone intersection with this object.
20316      * @method padding
20317      * @type int[]
20318      */
20319     padding: null,
20320
20321     /**
20322      * Cached reference to the linked element
20323      * @property _domRef
20324      * @private
20325      */
20326     _domRef: null,
20327
20328     /**
20329      * Internal typeof flag
20330      * @property __ygDragDrop
20331      * @private
20332      */
20333     __ygDragDrop: true,
20334
20335     /**
20336      * Set to true when horizontal contraints are applied
20337      * @property constrainX
20338      * @type boolean
20339      * @private
20340      */
20341     constrainX: false,
20342
20343     /**
20344      * Set to true when vertical contraints are applied
20345      * @property constrainY
20346      * @type boolean
20347      * @private
20348      */
20349     constrainY: false,
20350
20351     /**
20352      * The left constraint
20353      * @property minX
20354      * @type int
20355      * @private
20356      */
20357     minX: 0,
20358
20359     /**
20360      * The right constraint
20361      * @property maxX
20362      * @type int
20363      * @private
20364      */
20365     maxX: 0,
20366
20367     /**
20368      * The up constraint
20369      * @property minY
20370      * @type int
20371      * @type int
20372      * @private
20373      */
20374     minY: 0,
20375
20376     /**
20377      * The down constraint
20378      * @property maxY
20379      * @type int
20380      * @private
20381      */
20382     maxY: 0,
20383
20384     /**
20385      * Maintain offsets when we resetconstraints.  Set to true when you want
20386      * the position of the element relative to its parent to stay the same
20387      * when the page changes
20388      *
20389      * @property maintainOffset
20390      * @type boolean
20391      */
20392     maintainOffset: false,
20393
20394     /**
20395      * Array of pixel locations the element will snap to if we specified a
20396      * horizontal graduation/interval.  This array is generated automatically
20397      * when you define a tick interval.
20398      * @property xTicks
20399      * @type int[]
20400      */
20401     xTicks: null,
20402
20403     /**
20404      * Array of pixel locations the element will snap to if we specified a
20405      * vertical graduation/interval.  This array is generated automatically
20406      * when you define a tick interval.
20407      * @property yTicks
20408      * @type int[]
20409      */
20410     yTicks: null,
20411
20412     /**
20413      * By default the drag and drop instance will only respond to the primary
20414      * button click (left button for a right-handed mouse).  Set to true to
20415      * allow drag and drop to start with any mouse click that is propogated
20416      * by the browser
20417      * @property primaryButtonOnly
20418      * @type boolean
20419      */
20420     primaryButtonOnly: true,
20421
20422     /**
20423      * The availabe property is false until the linked dom element is accessible.
20424      * @property available
20425      * @type boolean
20426      */
20427     available: false,
20428
20429     /**
20430      * By default, drags can only be initiated if the mousedown occurs in the
20431      * region the linked element is.  This is done in part to work around a
20432      * bug in some browsers that mis-report the mousedown if the previous
20433      * mouseup happened outside of the window.  This property is set to true
20434      * if outer handles are defined.
20435      *
20436      * @property hasOuterHandles
20437      * @type boolean
20438      * @default false
20439      */
20440     hasOuterHandles: false,
20441
20442     /**
20443      * Code that executes immediately before the startDrag event
20444      * @method b4StartDrag
20445      * @private
20446      */
20447     b4StartDrag: function(x, y) { },
20448
20449     /**
20450      * Abstract method called after a drag/drop object is clicked
20451      * and the drag or mousedown time thresholds have beeen met.
20452      * @method startDrag
20453      * @param {int} X click location
20454      * @param {int} Y click location
20455      */
20456     startDrag: function(x, y) { /* override this */ },
20457
20458     /**
20459      * Code that executes immediately before the onDrag event
20460      * @method b4Drag
20461      * @private
20462      */
20463     b4Drag: function(e) { },
20464
20465     /**
20466      * Abstract method called during the onMouseMove event while dragging an
20467      * object.
20468      * @method onDrag
20469      * @param {Event} e the mousemove event
20470      */
20471     onDrag: function(e) { /* override this */ },
20472
20473     /**
20474      * Abstract method called when this element fist begins hovering over
20475      * another DragDrop obj
20476      * @method onDragEnter
20477      * @param {Event} e the mousemove event
20478      * @param {String|DragDrop[]} id In POINT mode, the element
20479      * id this is hovering over.  In INTERSECT mode, an array of one or more
20480      * dragdrop items being hovered over.
20481      */
20482     onDragEnter: function(e, id) { /* override this */ },
20483
20484     /**
20485      * Code that executes immediately before the onDragOver event
20486      * @method b4DragOver
20487      * @private
20488      */
20489     b4DragOver: function(e) { },
20490
20491     /**
20492      * Abstract method called when this element is hovering over another
20493      * DragDrop obj
20494      * @method onDragOver
20495      * @param {Event} e the mousemove event
20496      * @param {String|DragDrop[]} id In POINT mode, the element
20497      * id this is hovering over.  In INTERSECT mode, an array of dd items
20498      * being hovered over.
20499      */
20500     onDragOver: function(e, id) { /* override this */ },
20501
20502     /**
20503      * Code that executes immediately before the onDragOut event
20504      * @method b4DragOut
20505      * @private
20506      */
20507     b4DragOut: function(e) { },
20508
20509     /**
20510      * Abstract method called when we are no longer hovering over an element
20511      * @method onDragOut
20512      * @param {Event} e the mousemove event
20513      * @param {String|DragDrop[]} id In POINT mode, the element
20514      * id this was hovering over.  In INTERSECT mode, an array of dd items
20515      * that the mouse is no longer over.
20516      */
20517     onDragOut: function(e, id) { /* override this */ },
20518
20519     /**
20520      * Code that executes immediately before the onDragDrop event
20521      * @method b4DragDrop
20522      * @private
20523      */
20524     b4DragDrop: function(e) { },
20525
20526     /**
20527      * Abstract method called when this item is dropped on another DragDrop
20528      * obj
20529      * @method onDragDrop
20530      * @param {Event} e the mouseup event
20531      * @param {String|DragDrop[]} id In POINT mode, the element
20532      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20533      * was dropped on.
20534      */
20535     onDragDrop: function(e, id) { /* override this */ },
20536
20537     /**
20538      * Abstract method called when this item is dropped on an area with no
20539      * drop target
20540      * @method onInvalidDrop
20541      * @param {Event} e the mouseup event
20542      */
20543     onInvalidDrop: function(e) { /* override this */ },
20544
20545     /**
20546      * Code that executes immediately before the endDrag event
20547      * @method b4EndDrag
20548      * @private
20549      */
20550     b4EndDrag: function(e) { },
20551
20552     /**
20553      * Fired when we are done dragging the object
20554      * @method endDrag
20555      * @param {Event} e the mouseup event
20556      */
20557     endDrag: function(e) { /* override this */ },
20558
20559     /**
20560      * Code executed immediately before the onMouseDown event
20561      * @method b4MouseDown
20562      * @param {Event} e the mousedown event
20563      * @private
20564      */
20565     b4MouseDown: function(e) {  },
20566
20567     /**
20568      * Event handler that fires when a drag/drop obj gets a mousedown
20569      * @method onMouseDown
20570      * @param {Event} e the mousedown event
20571      */
20572     onMouseDown: function(e) { /* override this */ },
20573
20574     /**
20575      * Event handler that fires when a drag/drop obj gets a mouseup
20576      * @method onMouseUp
20577      * @param {Event} e the mouseup event
20578      */
20579     onMouseUp: function(e) { /* override this */ },
20580
20581     /**
20582      * Override the onAvailable method to do what is needed after the initial
20583      * position was determined.
20584      * @method onAvailable
20585      */
20586     onAvailable: function () {
20587     },
20588
20589     /*
20590      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20591      * @type Object
20592      */
20593     defaultPadding : {left:0, right:0, top:0, bottom:0},
20594
20595     /*
20596      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20597  *
20598  * Usage:
20599  <pre><code>
20600  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20601                 { dragElId: "existingProxyDiv" });
20602  dd.startDrag = function(){
20603      this.constrainTo("parent-id");
20604  };
20605  </code></pre>
20606  * Or you can initalize it using the {@link Roo.Element} object:
20607  <pre><code>
20608  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20609      startDrag : function(){
20610          this.constrainTo("parent-id");
20611      }
20612  });
20613  </code></pre>
20614      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20615      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20616      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20617      * an object containing the sides to pad. For example: {right:10, bottom:10}
20618      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20619      */
20620     constrainTo : function(constrainTo, pad, inContent){
20621         if(typeof pad == "number"){
20622             pad = {left: pad, right:pad, top:pad, bottom:pad};
20623         }
20624         pad = pad || this.defaultPadding;
20625         var b = Roo.get(this.getEl()).getBox();
20626         var ce = Roo.get(constrainTo);
20627         var s = ce.getScroll();
20628         var c, cd = ce.dom;
20629         if(cd == document.body){
20630             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20631         }else{
20632             xy = ce.getXY();
20633             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20634         }
20635
20636
20637         var topSpace = b.y - c.y;
20638         var leftSpace = b.x - c.x;
20639
20640         this.resetConstraints();
20641         this.setXConstraint(leftSpace - (pad.left||0), // left
20642                 c.width - leftSpace - b.width - (pad.right||0) //right
20643         );
20644         this.setYConstraint(topSpace - (pad.top||0), //top
20645                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20646         );
20647     },
20648
20649     /**
20650      * Returns a reference to the linked element
20651      * @method getEl
20652      * @return {HTMLElement} the html element
20653      */
20654     getEl: function() {
20655         if (!this._domRef) {
20656             this._domRef = Roo.getDom(this.id);
20657         }
20658
20659         return this._domRef;
20660     },
20661
20662     /**
20663      * Returns a reference to the actual element to drag.  By default this is
20664      * the same as the html element, but it can be assigned to another
20665      * element. An example of this can be found in Roo.dd.DDProxy
20666      * @method getDragEl
20667      * @return {HTMLElement} the html element
20668      */
20669     getDragEl: function() {
20670         return Roo.getDom(this.dragElId);
20671     },
20672
20673     /**
20674      * Sets up the DragDrop object.  Must be called in the constructor of any
20675      * Roo.dd.DragDrop subclass
20676      * @method init
20677      * @param id the id of the linked element
20678      * @param {String} sGroup the group of related items
20679      * @param {object} config configuration attributes
20680      */
20681     init: function(id, sGroup, config) {
20682         this.initTarget(id, sGroup, config);
20683         if (!Roo.isTouch) {
20684             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20685         }
20686         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20687         // Event.on(this.id, "selectstart", Event.preventDefault);
20688     },
20689
20690     /**
20691      * Initializes Targeting functionality only... the object does not
20692      * get a mousedown handler.
20693      * @method initTarget
20694      * @param id the id of the linked element
20695      * @param {String} sGroup the group of related items
20696      * @param {object} config configuration attributes
20697      */
20698     initTarget: function(id, sGroup, config) {
20699
20700         // configuration attributes
20701         this.config = config || {};
20702
20703         // create a local reference to the drag and drop manager
20704         this.DDM = Roo.dd.DDM;
20705         // initialize the groups array
20706         this.groups = {};
20707
20708         // assume that we have an element reference instead of an id if the
20709         // parameter is not a string
20710         if (typeof id !== "string") {
20711             id = Roo.id(id);
20712         }
20713
20714         // set the id
20715         this.id = id;
20716
20717         // add to an interaction group
20718         this.addToGroup((sGroup) ? sGroup : "default");
20719
20720         // We don't want to register this as the handle with the manager
20721         // so we just set the id rather than calling the setter.
20722         this.handleElId = id;
20723
20724         // the linked element is the element that gets dragged by default
20725         this.setDragElId(id);
20726
20727         // by default, clicked anchors will not start drag operations.
20728         this.invalidHandleTypes = { A: "A" };
20729         this.invalidHandleIds = {};
20730         this.invalidHandleClasses = [];
20731
20732         this.applyConfig();
20733
20734         this.handleOnAvailable();
20735     },
20736
20737     /**
20738      * Applies the configuration parameters that were passed into the constructor.
20739      * This is supposed to happen at each level through the inheritance chain.  So
20740      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20741      * DragDrop in order to get all of the parameters that are available in
20742      * each object.
20743      * @method applyConfig
20744      */
20745     applyConfig: function() {
20746
20747         // configurable properties:
20748         //    padding, isTarget, maintainOffset, primaryButtonOnly
20749         this.padding           = this.config.padding || [0, 0, 0, 0];
20750         this.isTarget          = (this.config.isTarget !== false);
20751         this.maintainOffset    = (this.config.maintainOffset);
20752         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20753
20754     },
20755
20756     /**
20757      * Executed when the linked element is available
20758      * @method handleOnAvailable
20759      * @private
20760      */
20761     handleOnAvailable: function() {
20762         this.available = true;
20763         this.resetConstraints();
20764         this.onAvailable();
20765     },
20766
20767      /**
20768      * Configures the padding for the target zone in px.  Effectively expands
20769      * (or reduces) the virtual object size for targeting calculations.
20770      * Supports css-style shorthand; if only one parameter is passed, all sides
20771      * will have that padding, and if only two are passed, the top and bottom
20772      * will have the first param, the left and right the second.
20773      * @method setPadding
20774      * @param {int} iTop    Top pad
20775      * @param {int} iRight  Right pad
20776      * @param {int} iBot    Bot pad
20777      * @param {int} iLeft   Left pad
20778      */
20779     setPadding: function(iTop, iRight, iBot, iLeft) {
20780         // this.padding = [iLeft, iRight, iTop, iBot];
20781         if (!iRight && 0 !== iRight) {
20782             this.padding = [iTop, iTop, iTop, iTop];
20783         } else if (!iBot && 0 !== iBot) {
20784             this.padding = [iTop, iRight, iTop, iRight];
20785         } else {
20786             this.padding = [iTop, iRight, iBot, iLeft];
20787         }
20788     },
20789
20790     /**
20791      * Stores the initial placement of the linked element.
20792      * @method setInitialPosition
20793      * @param {int} diffX   the X offset, default 0
20794      * @param {int} diffY   the Y offset, default 0
20795      */
20796     setInitPosition: function(diffX, diffY) {
20797         var el = this.getEl();
20798
20799         if (!this.DDM.verifyEl(el)) {
20800             return;
20801         }
20802
20803         var dx = diffX || 0;
20804         var dy = diffY || 0;
20805
20806         var p = Dom.getXY( el );
20807
20808         this.initPageX = p[0] - dx;
20809         this.initPageY = p[1] - dy;
20810
20811         this.lastPageX = p[0];
20812         this.lastPageY = p[1];
20813
20814
20815         this.setStartPosition(p);
20816     },
20817
20818     /**
20819      * Sets the start position of the element.  This is set when the obj
20820      * is initialized, the reset when a drag is started.
20821      * @method setStartPosition
20822      * @param pos current position (from previous lookup)
20823      * @private
20824      */
20825     setStartPosition: function(pos) {
20826         var p = pos || Dom.getXY( this.getEl() );
20827         this.deltaSetXY = null;
20828
20829         this.startPageX = p[0];
20830         this.startPageY = p[1];
20831     },
20832
20833     /**
20834      * Add this instance to a group of related drag/drop objects.  All
20835      * instances belong to at least one group, and can belong to as many
20836      * groups as needed.
20837      * @method addToGroup
20838      * @param sGroup {string} the name of the group
20839      */
20840     addToGroup: function(sGroup) {
20841         this.groups[sGroup] = true;
20842         this.DDM.regDragDrop(this, sGroup);
20843     },
20844
20845     /**
20846      * Remove's this instance from the supplied interaction group
20847      * @method removeFromGroup
20848      * @param {string}  sGroup  The group to drop
20849      */
20850     removeFromGroup: function(sGroup) {
20851         if (this.groups[sGroup]) {
20852             delete this.groups[sGroup];
20853         }
20854
20855         this.DDM.removeDDFromGroup(this, sGroup);
20856     },
20857
20858     /**
20859      * Allows you to specify that an element other than the linked element
20860      * will be moved with the cursor during a drag
20861      * @method setDragElId
20862      * @param id {string} the id of the element that will be used to initiate the drag
20863      */
20864     setDragElId: function(id) {
20865         this.dragElId = id;
20866     },
20867
20868     /**
20869      * Allows you to specify a child of the linked element that should be
20870      * used to initiate the drag operation.  An example of this would be if
20871      * you have a content div with text and links.  Clicking anywhere in the
20872      * content area would normally start the drag operation.  Use this method
20873      * to specify that an element inside of the content div is the element
20874      * that starts the drag operation.
20875      * @method setHandleElId
20876      * @param id {string} the id of the element that will be used to
20877      * initiate the drag.
20878      */
20879     setHandleElId: function(id) {
20880         if (typeof id !== "string") {
20881             id = Roo.id(id);
20882         }
20883         this.handleElId = id;
20884         this.DDM.regHandle(this.id, id);
20885     },
20886
20887     /**
20888      * Allows you to set an element outside of the linked element as a drag
20889      * handle
20890      * @method setOuterHandleElId
20891      * @param id the id of the element that will be used to initiate the drag
20892      */
20893     setOuterHandleElId: function(id) {
20894         if (typeof id !== "string") {
20895             id = Roo.id(id);
20896         }
20897         Event.on(id, "mousedown",
20898                 this.handleMouseDown, this);
20899         this.setHandleElId(id);
20900
20901         this.hasOuterHandles = true;
20902     },
20903
20904     /**
20905      * Remove all drag and drop hooks for this element
20906      * @method unreg
20907      */
20908     unreg: function() {
20909         Event.un(this.id, "mousedown",
20910                 this.handleMouseDown);
20911         Event.un(this.id, "touchstart",
20912                 this.handleMouseDown);
20913         this._domRef = null;
20914         this.DDM._remove(this);
20915     },
20916
20917     destroy : function(){
20918         this.unreg();
20919     },
20920
20921     /**
20922      * Returns true if this instance is locked, or the drag drop mgr is locked
20923      * (meaning that all drag/drop is disabled on the page.)
20924      * @method isLocked
20925      * @return {boolean} true if this obj or all drag/drop is locked, else
20926      * false
20927      */
20928     isLocked: function() {
20929         return (this.DDM.isLocked() || this.locked);
20930     },
20931
20932     /**
20933      * Fired when this object is clicked
20934      * @method handleMouseDown
20935      * @param {Event} e
20936      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20937      * @private
20938      */
20939     handleMouseDown: function(e, oDD){
20940      
20941         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20942             //Roo.log('not touch/ button !=0');
20943             return;
20944         }
20945         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20946             return; // double touch..
20947         }
20948         
20949
20950         if (this.isLocked()) {
20951             //Roo.log('locked');
20952             return;
20953         }
20954
20955         this.DDM.refreshCache(this.groups);
20956 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20957         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20958         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20959             //Roo.log('no outer handes or not over target');
20960                 // do nothing.
20961         } else {
20962 //            Roo.log('check validator');
20963             if (this.clickValidator(e)) {
20964 //                Roo.log('validate success');
20965                 // set the initial element position
20966                 this.setStartPosition();
20967
20968
20969                 this.b4MouseDown(e);
20970                 this.onMouseDown(e);
20971
20972                 this.DDM.handleMouseDown(e, this);
20973
20974                 this.DDM.stopEvent(e);
20975             } else {
20976
20977
20978             }
20979         }
20980     },
20981
20982     clickValidator: function(e) {
20983         var target = e.getTarget();
20984         return ( this.isValidHandleChild(target) &&
20985                     (this.id == this.handleElId ||
20986                         this.DDM.handleWasClicked(target, this.id)) );
20987     },
20988
20989     /**
20990      * Allows you to specify a tag name that should not start a drag operation
20991      * when clicked.  This is designed to facilitate embedding links within a
20992      * drag handle that do something other than start the drag.
20993      * @method addInvalidHandleType
20994      * @param {string} tagName the type of element to exclude
20995      */
20996     addInvalidHandleType: function(tagName) {
20997         var type = tagName.toUpperCase();
20998         this.invalidHandleTypes[type] = type;
20999     },
21000
21001     /**
21002      * Lets you to specify an element id for a child of a drag handle
21003      * that should not initiate a drag
21004      * @method addInvalidHandleId
21005      * @param {string} id the element id of the element you wish to ignore
21006      */
21007     addInvalidHandleId: function(id) {
21008         if (typeof id !== "string") {
21009             id = Roo.id(id);
21010         }
21011         this.invalidHandleIds[id] = id;
21012     },
21013
21014     /**
21015      * Lets you specify a css class of elements that will not initiate a drag
21016      * @method addInvalidHandleClass
21017      * @param {string} cssClass the class of the elements you wish to ignore
21018      */
21019     addInvalidHandleClass: function(cssClass) {
21020         this.invalidHandleClasses.push(cssClass);
21021     },
21022
21023     /**
21024      * Unsets an excluded tag name set by addInvalidHandleType
21025      * @method removeInvalidHandleType
21026      * @param {string} tagName the type of element to unexclude
21027      */
21028     removeInvalidHandleType: function(tagName) {
21029         var type = tagName.toUpperCase();
21030         // this.invalidHandleTypes[type] = null;
21031         delete this.invalidHandleTypes[type];
21032     },
21033
21034     /**
21035      * Unsets an invalid handle id
21036      * @method removeInvalidHandleId
21037      * @param {string} id the id of the element to re-enable
21038      */
21039     removeInvalidHandleId: function(id) {
21040         if (typeof id !== "string") {
21041             id = Roo.id(id);
21042         }
21043         delete this.invalidHandleIds[id];
21044     },
21045
21046     /**
21047      * Unsets an invalid css class
21048      * @method removeInvalidHandleClass
21049      * @param {string} cssClass the class of the element(s) you wish to
21050      * re-enable
21051      */
21052     removeInvalidHandleClass: function(cssClass) {
21053         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21054             if (this.invalidHandleClasses[i] == cssClass) {
21055                 delete this.invalidHandleClasses[i];
21056             }
21057         }
21058     },
21059
21060     /**
21061      * Checks the tag exclusion list to see if this click should be ignored
21062      * @method isValidHandleChild
21063      * @param {HTMLElement} node the HTMLElement to evaluate
21064      * @return {boolean} true if this is a valid tag type, false if not
21065      */
21066     isValidHandleChild: function(node) {
21067
21068         var valid = true;
21069         // var n = (node.nodeName == "#text") ? node.parentNode : node;
21070         var nodeName;
21071         try {
21072             nodeName = node.nodeName.toUpperCase();
21073         } catch(e) {
21074             nodeName = node.nodeName;
21075         }
21076         valid = valid && !this.invalidHandleTypes[nodeName];
21077         valid = valid && !this.invalidHandleIds[node.id];
21078
21079         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21080             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21081         }
21082
21083
21084         return valid;
21085
21086     },
21087
21088     /**
21089      * Create the array of horizontal tick marks if an interval was specified
21090      * in setXConstraint().
21091      * @method setXTicks
21092      * @private
21093      */
21094     setXTicks: function(iStartX, iTickSize) {
21095         this.xTicks = [];
21096         this.xTickSize = iTickSize;
21097
21098         var tickMap = {};
21099
21100         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21101             if (!tickMap[i]) {
21102                 this.xTicks[this.xTicks.length] = i;
21103                 tickMap[i] = true;
21104             }
21105         }
21106
21107         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21108             if (!tickMap[i]) {
21109                 this.xTicks[this.xTicks.length] = i;
21110                 tickMap[i] = true;
21111             }
21112         }
21113
21114         this.xTicks.sort(this.DDM.numericSort) ;
21115     },
21116
21117     /**
21118      * Create the array of vertical tick marks if an interval was specified in
21119      * setYConstraint().
21120      * @method setYTicks
21121      * @private
21122      */
21123     setYTicks: function(iStartY, iTickSize) {
21124         this.yTicks = [];
21125         this.yTickSize = iTickSize;
21126
21127         var tickMap = {};
21128
21129         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21130             if (!tickMap[i]) {
21131                 this.yTicks[this.yTicks.length] = i;
21132                 tickMap[i] = true;
21133             }
21134         }
21135
21136         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21137             if (!tickMap[i]) {
21138                 this.yTicks[this.yTicks.length] = i;
21139                 tickMap[i] = true;
21140             }
21141         }
21142
21143         this.yTicks.sort(this.DDM.numericSort) ;
21144     },
21145
21146     /**
21147      * By default, the element can be dragged any place on the screen.  Use
21148      * this method to limit the horizontal travel of the element.  Pass in
21149      * 0,0 for the parameters if you want to lock the drag to the y axis.
21150      * @method setXConstraint
21151      * @param {int} iLeft the number of pixels the element can move to the left
21152      * @param {int} iRight the number of pixels the element can move to the
21153      * right
21154      * @param {int} iTickSize optional parameter for specifying that the
21155      * element
21156      * should move iTickSize pixels at a time.
21157      */
21158     setXConstraint: function(iLeft, iRight, iTickSize) {
21159         this.leftConstraint = iLeft;
21160         this.rightConstraint = iRight;
21161
21162         this.minX = this.initPageX - iLeft;
21163         this.maxX = this.initPageX + iRight;
21164         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21165
21166         this.constrainX = true;
21167     },
21168
21169     /**
21170      * Clears any constraints applied to this instance.  Also clears ticks
21171      * since they can't exist independent of a constraint at this time.
21172      * @method clearConstraints
21173      */
21174     clearConstraints: function() {
21175         this.constrainX = false;
21176         this.constrainY = false;
21177         this.clearTicks();
21178     },
21179
21180     /**
21181      * Clears any tick interval defined for this instance
21182      * @method clearTicks
21183      */
21184     clearTicks: function() {
21185         this.xTicks = null;
21186         this.yTicks = null;
21187         this.xTickSize = 0;
21188         this.yTickSize = 0;
21189     },
21190
21191     /**
21192      * By default, the element can be dragged any place on the screen.  Set
21193      * this to limit the vertical travel of the element.  Pass in 0,0 for the
21194      * parameters if you want to lock the drag to the x axis.
21195      * @method setYConstraint
21196      * @param {int} iUp the number of pixels the element can move up
21197      * @param {int} iDown the number of pixels the element can move down
21198      * @param {int} iTickSize optional parameter for specifying that the
21199      * element should move iTickSize pixels at a time.
21200      */
21201     setYConstraint: function(iUp, iDown, iTickSize) {
21202         this.topConstraint = iUp;
21203         this.bottomConstraint = iDown;
21204
21205         this.minY = this.initPageY - iUp;
21206         this.maxY = this.initPageY + iDown;
21207         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21208
21209         this.constrainY = true;
21210
21211     },
21212
21213     /**
21214      * resetConstraints must be called if you manually reposition a dd element.
21215      * @method resetConstraints
21216      * @param {boolean} maintainOffset
21217      */
21218     resetConstraints: function() {
21219
21220
21221         // Maintain offsets if necessary
21222         if (this.initPageX || this.initPageX === 0) {
21223             // figure out how much this thing has moved
21224             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21225             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21226
21227             this.setInitPosition(dx, dy);
21228
21229         // This is the first time we have detected the element's position
21230         } else {
21231             this.setInitPosition();
21232         }
21233
21234         if (this.constrainX) {
21235             this.setXConstraint( this.leftConstraint,
21236                                  this.rightConstraint,
21237                                  this.xTickSize        );
21238         }
21239
21240         if (this.constrainY) {
21241             this.setYConstraint( this.topConstraint,
21242                                  this.bottomConstraint,
21243                                  this.yTickSize         );
21244         }
21245     },
21246
21247     /**
21248      * Normally the drag element is moved pixel by pixel, but we can specify
21249      * that it move a number of pixels at a time.  This method resolves the
21250      * location when we have it set up like this.
21251      * @method getTick
21252      * @param {int} val where we want to place the object
21253      * @param {int[]} tickArray sorted array of valid points
21254      * @return {int} the closest tick
21255      * @private
21256      */
21257     getTick: function(val, tickArray) {
21258
21259         if (!tickArray) {
21260             // If tick interval is not defined, it is effectively 1 pixel,
21261             // so we return the value passed to us.
21262             return val;
21263         } else if (tickArray[0] >= val) {
21264             // The value is lower than the first tick, so we return the first
21265             // tick.
21266             return tickArray[0];
21267         } else {
21268             for (var i=0, len=tickArray.length; i<len; ++i) {
21269                 var next = i + 1;
21270                 if (tickArray[next] && tickArray[next] >= val) {
21271                     var diff1 = val - tickArray[i];
21272                     var diff2 = tickArray[next] - val;
21273                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21274                 }
21275             }
21276
21277             // The value is larger than the last tick, so we return the last
21278             // tick.
21279             return tickArray[tickArray.length - 1];
21280         }
21281     },
21282
21283     /**
21284      * toString method
21285      * @method toString
21286      * @return {string} string representation of the dd obj
21287      */
21288     toString: function() {
21289         return ("DragDrop " + this.id);
21290     }
21291
21292 });
21293
21294 })();
21295 /*
21296  * Based on:
21297  * Ext JS Library 1.1.1
21298  * Copyright(c) 2006-2007, Ext JS, LLC.
21299  *
21300  * Originally Released Under LGPL - original licence link has changed is not relivant.
21301  *
21302  * Fork - LGPL
21303  * <script type="text/javascript">
21304  */
21305
21306
21307 /**
21308  * The drag and drop utility provides a framework for building drag and drop
21309  * applications.  In addition to enabling drag and drop for specific elements,
21310  * the drag and drop elements are tracked by the manager class, and the
21311  * interactions between the various elements are tracked during the drag and
21312  * the implementing code is notified about these important moments.
21313  */
21314
21315 // Only load the library once.  Rewriting the manager class would orphan
21316 // existing drag and drop instances.
21317 if (!Roo.dd.DragDropMgr) {
21318
21319 /**
21320  * @class Roo.dd.DragDropMgr
21321  * DragDropMgr is a singleton that tracks the element interaction for
21322  * all DragDrop items in the window.  Generally, you will not call
21323  * this class directly, but it does have helper methods that could
21324  * be useful in your DragDrop implementations.
21325  * @static
21326  */
21327 Roo.dd.DragDropMgr = function() {
21328
21329     var Event = Roo.EventManager;
21330
21331     return {
21332
21333         /**
21334          * Two dimensional Array of registered DragDrop objects.  The first
21335          * dimension is the DragDrop item group, the second the DragDrop
21336          * object.
21337          * @property ids
21338          * @type {string: string}
21339          * @private
21340          * @static
21341          */
21342         ids: {},
21343
21344         /**
21345          * Array of element ids defined as drag handles.  Used to determine
21346          * if the element that generated the mousedown event is actually the
21347          * handle and not the html element itself.
21348          * @property handleIds
21349          * @type {string: string}
21350          * @private
21351          * @static
21352          */
21353         handleIds: {},
21354
21355         /**
21356          * the DragDrop object that is currently being dragged
21357          * @property dragCurrent
21358          * @type DragDrop
21359          * @private
21360          * @static
21361          **/
21362         dragCurrent: null,
21363
21364         /**
21365          * the DragDrop object(s) that are being hovered over
21366          * @property dragOvers
21367          * @type Array
21368          * @private
21369          * @static
21370          */
21371         dragOvers: {},
21372
21373         /**
21374          * the X distance between the cursor and the object being dragged
21375          * @property deltaX
21376          * @type int
21377          * @private
21378          * @static
21379          */
21380         deltaX: 0,
21381
21382         /**
21383          * the Y distance between the cursor and the object being dragged
21384          * @property deltaY
21385          * @type int
21386          * @private
21387          * @static
21388          */
21389         deltaY: 0,
21390
21391         /**
21392          * Flag to determine if we should prevent the default behavior of the
21393          * events we define. By default this is true, but this can be set to
21394          * false if you need the default behavior (not recommended)
21395          * @property preventDefault
21396          * @type boolean
21397          * @static
21398          */
21399         preventDefault: true,
21400
21401         /**
21402          * Flag to determine if we should stop the propagation of the events
21403          * we generate. This is true by default but you may want to set it to
21404          * false if the html element contains other features that require the
21405          * mouse click.
21406          * @property stopPropagation
21407          * @type boolean
21408          * @static
21409          */
21410         stopPropagation: true,
21411
21412         /**
21413          * Internal flag that is set to true when drag and drop has been
21414          * intialized
21415          * @property initialized
21416          * @private
21417          * @static
21418          */
21419         initalized: false,
21420
21421         /**
21422          * All drag and drop can be disabled.
21423          * @property locked
21424          * @private
21425          * @static
21426          */
21427         locked: false,
21428
21429         /**
21430          * Called the first time an element is registered.
21431          * @method init
21432          * @private
21433          * @static
21434          */
21435         init: function() {
21436             this.initialized = true;
21437         },
21438
21439         /**
21440          * In point mode, drag and drop interaction is defined by the
21441          * location of the cursor during the drag/drop
21442          * @property POINT
21443          * @type int
21444          * @static
21445          */
21446         POINT: 0,
21447
21448         /**
21449          * In intersect mode, drag and drop interactio nis defined by the
21450          * overlap of two or more drag and drop objects.
21451          * @property INTERSECT
21452          * @type int
21453          * @static
21454          */
21455         INTERSECT: 1,
21456
21457         /**
21458          * The current drag and drop mode.  Default: POINT
21459          * @property mode
21460          * @type int
21461          * @static
21462          */
21463         mode: 0,
21464
21465         /**
21466          * Runs method on all drag and drop objects
21467          * @method _execOnAll
21468          * @private
21469          * @static
21470          */
21471         _execOnAll: function(sMethod, args) {
21472             for (var i in this.ids) {
21473                 for (var j in this.ids[i]) {
21474                     var oDD = this.ids[i][j];
21475                     if (! this.isTypeOfDD(oDD)) {
21476                         continue;
21477                     }
21478                     oDD[sMethod].apply(oDD, args);
21479                 }
21480             }
21481         },
21482
21483         /**
21484          * Drag and drop initialization.  Sets up the global event handlers
21485          * @method _onLoad
21486          * @private
21487          * @static
21488          */
21489         _onLoad: function() {
21490
21491             this.init();
21492
21493             if (!Roo.isTouch) {
21494                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21495                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21496             }
21497             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21498             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21499             
21500             Event.on(window,   "unload",    this._onUnload, this, true);
21501             Event.on(window,   "resize",    this._onResize, this, true);
21502             // Event.on(window,   "mouseout",    this._test);
21503
21504         },
21505
21506         /**
21507          * Reset constraints on all drag and drop objs
21508          * @method _onResize
21509          * @private
21510          * @static
21511          */
21512         _onResize: function(e) {
21513             this._execOnAll("resetConstraints", []);
21514         },
21515
21516         /**
21517          * Lock all drag and drop functionality
21518          * @method lock
21519          * @static
21520          */
21521         lock: function() { this.locked = true; },
21522
21523         /**
21524          * Unlock all drag and drop functionality
21525          * @method unlock
21526          * @static
21527          */
21528         unlock: function() { this.locked = false; },
21529
21530         /**
21531          * Is drag and drop locked?
21532          * @method isLocked
21533          * @return {boolean} True if drag and drop is locked, false otherwise.
21534          * @static
21535          */
21536         isLocked: function() { return this.locked; },
21537
21538         /**
21539          * Location cache that is set for all drag drop objects when a drag is
21540          * initiated, cleared when the drag is finished.
21541          * @property locationCache
21542          * @private
21543          * @static
21544          */
21545         locationCache: {},
21546
21547         /**
21548          * Set useCache to false if you want to force object the lookup of each
21549          * drag and drop linked element constantly during a drag.
21550          * @property useCache
21551          * @type boolean
21552          * @static
21553          */
21554         useCache: true,
21555
21556         /**
21557          * The number of pixels that the mouse needs to move after the
21558          * mousedown before the drag is initiated.  Default=3;
21559          * @property clickPixelThresh
21560          * @type int
21561          * @static
21562          */
21563         clickPixelThresh: 3,
21564
21565         /**
21566          * The number of milliseconds after the mousedown event to initiate the
21567          * drag if we don't get a mouseup event. Default=1000
21568          * @property clickTimeThresh
21569          * @type int
21570          * @static
21571          */
21572         clickTimeThresh: 350,
21573
21574         /**
21575          * Flag that indicates that either the drag pixel threshold or the
21576          * mousdown time threshold has been met
21577          * @property dragThreshMet
21578          * @type boolean
21579          * @private
21580          * @static
21581          */
21582         dragThreshMet: false,
21583
21584         /**
21585          * Timeout used for the click time threshold
21586          * @property clickTimeout
21587          * @type Object
21588          * @private
21589          * @static
21590          */
21591         clickTimeout: null,
21592
21593         /**
21594          * The X position of the mousedown event stored for later use when a
21595          * drag threshold is met.
21596          * @property startX
21597          * @type int
21598          * @private
21599          * @static
21600          */
21601         startX: 0,
21602
21603         /**
21604          * The Y position of the mousedown event stored for later use when a
21605          * drag threshold is met.
21606          * @property startY
21607          * @type int
21608          * @private
21609          * @static
21610          */
21611         startY: 0,
21612
21613         /**
21614          * Each DragDrop instance must be registered with the DragDropMgr.
21615          * This is executed in DragDrop.init()
21616          * @method regDragDrop
21617          * @param {DragDrop} oDD the DragDrop object to register
21618          * @param {String} sGroup the name of the group this element belongs to
21619          * @static
21620          */
21621         regDragDrop: function(oDD, sGroup) {
21622             if (!this.initialized) { this.init(); }
21623
21624             if (!this.ids[sGroup]) {
21625                 this.ids[sGroup] = {};
21626             }
21627             this.ids[sGroup][oDD.id] = oDD;
21628         },
21629
21630         /**
21631          * Removes the supplied dd instance from the supplied group. Executed
21632          * by DragDrop.removeFromGroup, so don't call this function directly.
21633          * @method removeDDFromGroup
21634          * @private
21635          * @static
21636          */
21637         removeDDFromGroup: function(oDD, sGroup) {
21638             if (!this.ids[sGroup]) {
21639                 this.ids[sGroup] = {};
21640             }
21641
21642             var obj = this.ids[sGroup];
21643             if (obj && obj[oDD.id]) {
21644                 delete obj[oDD.id];
21645             }
21646         },
21647
21648         /**
21649          * Unregisters a drag and drop item.  This is executed in
21650          * DragDrop.unreg, use that method instead of calling this directly.
21651          * @method _remove
21652          * @private
21653          * @static
21654          */
21655         _remove: function(oDD) {
21656             for (var g in oDD.groups) {
21657                 if (g && this.ids[g][oDD.id]) {
21658                     delete this.ids[g][oDD.id];
21659                 }
21660             }
21661             delete this.handleIds[oDD.id];
21662         },
21663
21664         /**
21665          * Each DragDrop handle element must be registered.  This is done
21666          * automatically when executing DragDrop.setHandleElId()
21667          * @method regHandle
21668          * @param {String} sDDId the DragDrop id this element is a handle for
21669          * @param {String} sHandleId the id of the element that is the drag
21670          * handle
21671          * @static
21672          */
21673         regHandle: function(sDDId, sHandleId) {
21674             if (!this.handleIds[sDDId]) {
21675                 this.handleIds[sDDId] = {};
21676             }
21677             this.handleIds[sDDId][sHandleId] = sHandleId;
21678         },
21679
21680         /**
21681          * Utility function to determine if a given element has been
21682          * registered as a drag drop item.
21683          * @method isDragDrop
21684          * @param {String} id the element id to check
21685          * @return {boolean} true if this element is a DragDrop item,
21686          * false otherwise
21687          * @static
21688          */
21689         isDragDrop: function(id) {
21690             return ( this.getDDById(id) ) ? true : false;
21691         },
21692
21693         /**
21694          * Returns the drag and drop instances that are in all groups the
21695          * passed in instance belongs to.
21696          * @method getRelated
21697          * @param {DragDrop} p_oDD the obj to get related data for
21698          * @param {boolean} bTargetsOnly if true, only return targetable objs
21699          * @return {DragDrop[]} the related instances
21700          * @static
21701          */
21702         getRelated: function(p_oDD, bTargetsOnly) {
21703             var oDDs = [];
21704             for (var i in p_oDD.groups) {
21705                 for (j in this.ids[i]) {
21706                     var dd = this.ids[i][j];
21707                     if (! this.isTypeOfDD(dd)) {
21708                         continue;
21709                     }
21710                     if (!bTargetsOnly || dd.isTarget) {
21711                         oDDs[oDDs.length] = dd;
21712                     }
21713                 }
21714             }
21715
21716             return oDDs;
21717         },
21718
21719         /**
21720          * Returns true if the specified dd target is a legal target for
21721          * the specifice drag obj
21722          * @method isLegalTarget
21723          * @param {DragDrop} the drag obj
21724          * @param {DragDrop} the target
21725          * @return {boolean} true if the target is a legal target for the
21726          * dd obj
21727          * @static
21728          */
21729         isLegalTarget: function (oDD, oTargetDD) {
21730             var targets = this.getRelated(oDD, true);
21731             for (var i=0, len=targets.length;i<len;++i) {
21732                 if (targets[i].id == oTargetDD.id) {
21733                     return true;
21734                 }
21735             }
21736
21737             return false;
21738         },
21739
21740         /**
21741          * My goal is to be able to transparently determine if an object is
21742          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21743          * returns "object", oDD.constructor.toString() always returns
21744          * "DragDrop" and not the name of the subclass.  So for now it just
21745          * evaluates a well-known variable in DragDrop.
21746          * @method isTypeOfDD
21747          * @param {Object} the object to evaluate
21748          * @return {boolean} true if typeof oDD = DragDrop
21749          * @static
21750          */
21751         isTypeOfDD: function (oDD) {
21752             return (oDD && oDD.__ygDragDrop);
21753         },
21754
21755         /**
21756          * Utility function to determine if a given element has been
21757          * registered as a drag drop handle for the given Drag Drop object.
21758          * @method isHandle
21759          * @param {String} id the element id to check
21760          * @return {boolean} true if this element is a DragDrop handle, false
21761          * otherwise
21762          * @static
21763          */
21764         isHandle: function(sDDId, sHandleId) {
21765             return ( this.handleIds[sDDId] &&
21766                             this.handleIds[sDDId][sHandleId] );
21767         },
21768
21769         /**
21770          * Returns the DragDrop instance for a given id
21771          * @method getDDById
21772          * @param {String} id the id of the DragDrop object
21773          * @return {DragDrop} the drag drop object, null if it is not found
21774          * @static
21775          */
21776         getDDById: function(id) {
21777             for (var i in this.ids) {
21778                 if (this.ids[i][id]) {
21779                     return this.ids[i][id];
21780                 }
21781             }
21782             return null;
21783         },
21784
21785         /**
21786          * Fired after a registered DragDrop object gets the mousedown event.
21787          * Sets up the events required to track the object being dragged
21788          * @method handleMouseDown
21789          * @param {Event} e the event
21790          * @param oDD the DragDrop object being dragged
21791          * @private
21792          * @static
21793          */
21794         handleMouseDown: function(e, oDD) {
21795             if(Roo.QuickTips){
21796                 Roo.QuickTips.disable();
21797             }
21798             this.currentTarget = e.getTarget();
21799
21800             this.dragCurrent = oDD;
21801
21802             var el = oDD.getEl();
21803
21804             // track start position
21805             this.startX = e.getPageX();
21806             this.startY = e.getPageY();
21807
21808             this.deltaX = this.startX - el.offsetLeft;
21809             this.deltaY = this.startY - el.offsetTop;
21810
21811             this.dragThreshMet = false;
21812
21813             this.clickTimeout = setTimeout(
21814                     function() {
21815                         var DDM = Roo.dd.DDM;
21816                         DDM.startDrag(DDM.startX, DDM.startY);
21817                     },
21818                     this.clickTimeThresh );
21819         },
21820
21821         /**
21822          * Fired when either the drag pixel threshol or the mousedown hold
21823          * time threshold has been met.
21824          * @method startDrag
21825          * @param x {int} the X position of the original mousedown
21826          * @param y {int} the Y position of the original mousedown
21827          * @static
21828          */
21829         startDrag: function(x, y) {
21830             clearTimeout(this.clickTimeout);
21831             if (this.dragCurrent) {
21832                 this.dragCurrent.b4StartDrag(x, y);
21833                 this.dragCurrent.startDrag(x, y);
21834             }
21835             this.dragThreshMet = true;
21836         },
21837
21838         /**
21839          * Internal function to handle the mouseup event.  Will be invoked
21840          * from the context of the document.
21841          * @method handleMouseUp
21842          * @param {Event} e the event
21843          * @private
21844          * @static
21845          */
21846         handleMouseUp: function(e) {
21847
21848             if(Roo.QuickTips){
21849                 Roo.QuickTips.enable();
21850             }
21851             if (! this.dragCurrent) {
21852                 return;
21853             }
21854
21855             clearTimeout(this.clickTimeout);
21856
21857             if (this.dragThreshMet) {
21858                 this.fireEvents(e, true);
21859             } else {
21860             }
21861
21862             this.stopDrag(e);
21863
21864             this.stopEvent(e);
21865         },
21866
21867         /**
21868          * Utility to stop event propagation and event default, if these
21869          * features are turned on.
21870          * @method stopEvent
21871          * @param {Event} e the event as returned by this.getEvent()
21872          * @static
21873          */
21874         stopEvent: function(e){
21875             if(this.stopPropagation) {
21876                 e.stopPropagation();
21877             }
21878
21879             if (this.preventDefault) {
21880                 e.preventDefault();
21881             }
21882         },
21883
21884         /**
21885          * Internal function to clean up event handlers after the drag
21886          * operation is complete
21887          * @method stopDrag
21888          * @param {Event} e the event
21889          * @private
21890          * @static
21891          */
21892         stopDrag: function(e) {
21893             // Fire the drag end event for the item that was dragged
21894             if (this.dragCurrent) {
21895                 if (this.dragThreshMet) {
21896                     this.dragCurrent.b4EndDrag(e);
21897                     this.dragCurrent.endDrag(e);
21898                 }
21899
21900                 this.dragCurrent.onMouseUp(e);
21901             }
21902
21903             this.dragCurrent = null;
21904             this.dragOvers = {};
21905         },
21906
21907         /**
21908          * Internal function to handle the mousemove event.  Will be invoked
21909          * from the context of the html element.
21910          *
21911          * @TODO figure out what we can do about mouse events lost when the
21912          * user drags objects beyond the window boundary.  Currently we can
21913          * detect this in internet explorer by verifying that the mouse is
21914          * down during the mousemove event.  Firefox doesn't give us the
21915          * button state on the mousemove event.
21916          * @method handleMouseMove
21917          * @param {Event} e the event
21918          * @private
21919          * @static
21920          */
21921         handleMouseMove: function(e) {
21922             if (! this.dragCurrent) {
21923                 return true;
21924             }
21925
21926             // var button = e.which || e.button;
21927
21928             // check for IE mouseup outside of page boundary
21929             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21930                 this.stopEvent(e);
21931                 return this.handleMouseUp(e);
21932             }
21933
21934             if (!this.dragThreshMet) {
21935                 var diffX = Math.abs(this.startX - e.getPageX());
21936                 var diffY = Math.abs(this.startY - e.getPageY());
21937                 if (diffX > this.clickPixelThresh ||
21938                             diffY > this.clickPixelThresh) {
21939                     this.startDrag(this.startX, this.startY);
21940                 }
21941             }
21942
21943             if (this.dragThreshMet) {
21944                 this.dragCurrent.b4Drag(e);
21945                 this.dragCurrent.onDrag(e);
21946                 if(!this.dragCurrent.moveOnly){
21947                     this.fireEvents(e, false);
21948                 }
21949             }
21950
21951             this.stopEvent(e);
21952
21953             return true;
21954         },
21955
21956         /**
21957          * Iterates over all of the DragDrop elements to find ones we are
21958          * hovering over or dropping on
21959          * @method fireEvents
21960          * @param {Event} e the event
21961          * @param {boolean} isDrop is this a drop op or a mouseover op?
21962          * @private
21963          * @static
21964          */
21965         fireEvents: function(e, isDrop) {
21966             var dc = this.dragCurrent;
21967
21968             // If the user did the mouse up outside of the window, we could
21969             // get here even though we have ended the drag.
21970             if (!dc || dc.isLocked()) {
21971                 return;
21972             }
21973
21974             var pt = e.getPoint();
21975
21976             // cache the previous dragOver array
21977             var oldOvers = [];
21978
21979             var outEvts   = [];
21980             var overEvts  = [];
21981             var dropEvts  = [];
21982             var enterEvts = [];
21983
21984             // Check to see if the object(s) we were hovering over is no longer
21985             // being hovered over so we can fire the onDragOut event
21986             for (var i in this.dragOvers) {
21987
21988                 var ddo = this.dragOvers[i];
21989
21990                 if (! this.isTypeOfDD(ddo)) {
21991                     continue;
21992                 }
21993
21994                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21995                     outEvts.push( ddo );
21996                 }
21997
21998                 oldOvers[i] = true;
21999                 delete this.dragOvers[i];
22000             }
22001
22002             for (var sGroup in dc.groups) {
22003
22004                 if ("string" != typeof sGroup) {
22005                     continue;
22006                 }
22007
22008                 for (i in this.ids[sGroup]) {
22009                     var oDD = this.ids[sGroup][i];
22010                     if (! this.isTypeOfDD(oDD)) {
22011                         continue;
22012                     }
22013
22014                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
22015                         if (this.isOverTarget(pt, oDD, this.mode)) {
22016                             // look for drop interactions
22017                             if (isDrop) {
22018                                 dropEvts.push( oDD );
22019                             // look for drag enter and drag over interactions
22020                             } else {
22021
22022                                 // initial drag over: dragEnter fires
22023                                 if (!oldOvers[oDD.id]) {
22024                                     enterEvts.push( oDD );
22025                                 // subsequent drag overs: dragOver fires
22026                                 } else {
22027                                     overEvts.push( oDD );
22028                                 }
22029
22030                                 this.dragOvers[oDD.id] = oDD;
22031                             }
22032                         }
22033                     }
22034                 }
22035             }
22036
22037             if (this.mode) {
22038                 if (outEvts.length) {
22039                     dc.b4DragOut(e, outEvts);
22040                     dc.onDragOut(e, outEvts);
22041                 }
22042
22043                 if (enterEvts.length) {
22044                     dc.onDragEnter(e, enterEvts);
22045                 }
22046
22047                 if (overEvts.length) {
22048                     dc.b4DragOver(e, overEvts);
22049                     dc.onDragOver(e, overEvts);
22050                 }
22051
22052                 if (dropEvts.length) {
22053                     dc.b4DragDrop(e, dropEvts);
22054                     dc.onDragDrop(e, dropEvts);
22055                 }
22056
22057             } else {
22058                 // fire dragout events
22059                 var len = 0;
22060                 for (i=0, len=outEvts.length; i<len; ++i) {
22061                     dc.b4DragOut(e, outEvts[i].id);
22062                     dc.onDragOut(e, outEvts[i].id);
22063                 }
22064
22065                 // fire enter events
22066                 for (i=0,len=enterEvts.length; i<len; ++i) {
22067                     // dc.b4DragEnter(e, oDD.id);
22068                     dc.onDragEnter(e, enterEvts[i].id);
22069                 }
22070
22071                 // fire over events
22072                 for (i=0,len=overEvts.length; i<len; ++i) {
22073                     dc.b4DragOver(e, overEvts[i].id);
22074                     dc.onDragOver(e, overEvts[i].id);
22075                 }
22076
22077                 // fire drop events
22078                 for (i=0, len=dropEvts.length; i<len; ++i) {
22079                     dc.b4DragDrop(e, dropEvts[i].id);
22080                     dc.onDragDrop(e, dropEvts[i].id);
22081                 }
22082
22083             }
22084
22085             // notify about a drop that did not find a target
22086             if (isDrop && !dropEvts.length) {
22087                 dc.onInvalidDrop(e);
22088             }
22089
22090         },
22091
22092         /**
22093          * Helper function for getting the best match from the list of drag
22094          * and drop objects returned by the drag and drop events when we are
22095          * in INTERSECT mode.  It returns either the first object that the
22096          * cursor is over, or the object that has the greatest overlap with
22097          * the dragged element.
22098          * @method getBestMatch
22099          * @param  {DragDrop[]} dds The array of drag and drop objects
22100          * targeted
22101          * @return {DragDrop}       The best single match
22102          * @static
22103          */
22104         getBestMatch: function(dds) {
22105             var winner = null;
22106             // Return null if the input is not what we expect
22107             //if (!dds || !dds.length || dds.length == 0) {
22108                // winner = null;
22109             // If there is only one item, it wins
22110             //} else if (dds.length == 1) {
22111
22112             var len = dds.length;
22113
22114             if (len == 1) {
22115                 winner = dds[0];
22116             } else {
22117                 // Loop through the targeted items
22118                 for (var i=0; i<len; ++i) {
22119                     var dd = dds[i];
22120                     // If the cursor is over the object, it wins.  If the
22121                     // cursor is over multiple matches, the first one we come
22122                     // to wins.
22123                     if (dd.cursorIsOver) {
22124                         winner = dd;
22125                         break;
22126                     // Otherwise the object with the most overlap wins
22127                     } else {
22128                         if (!winner ||
22129                             winner.overlap.getArea() < dd.overlap.getArea()) {
22130                             winner = dd;
22131                         }
22132                     }
22133                 }
22134             }
22135
22136             return winner;
22137         },
22138
22139         /**
22140          * Refreshes the cache of the top-left and bottom-right points of the
22141          * drag and drop objects in the specified group(s).  This is in the
22142          * format that is stored in the drag and drop instance, so typical
22143          * usage is:
22144          * <code>
22145          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22146          * </code>
22147          * Alternatively:
22148          * <code>
22149          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22150          * </code>
22151          * @TODO this really should be an indexed array.  Alternatively this
22152          * method could accept both.
22153          * @method refreshCache
22154          * @param {Object} groups an associative array of groups to refresh
22155          * @static
22156          */
22157         refreshCache: function(groups) {
22158             for (var sGroup in groups) {
22159                 if ("string" != typeof sGroup) {
22160                     continue;
22161                 }
22162                 for (var i in this.ids[sGroup]) {
22163                     var oDD = this.ids[sGroup][i];
22164
22165                     if (this.isTypeOfDD(oDD)) {
22166                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22167                         var loc = this.getLocation(oDD);
22168                         if (loc) {
22169                             this.locationCache[oDD.id] = loc;
22170                         } else {
22171                             delete this.locationCache[oDD.id];
22172                             // this will unregister the drag and drop object if
22173                             // the element is not in a usable state
22174                             // oDD.unreg();
22175                         }
22176                     }
22177                 }
22178             }
22179         },
22180
22181         /**
22182          * This checks to make sure an element exists and is in the DOM.  The
22183          * main purpose is to handle cases where innerHTML is used to remove
22184          * drag and drop objects from the DOM.  IE provides an 'unspecified
22185          * error' when trying to access the offsetParent of such an element
22186          * @method verifyEl
22187          * @param {HTMLElement} el the element to check
22188          * @return {boolean} true if the element looks usable
22189          * @static
22190          */
22191         verifyEl: function(el) {
22192             if (el) {
22193                 var parent;
22194                 if(Roo.isIE){
22195                     try{
22196                         parent = el.offsetParent;
22197                     }catch(e){}
22198                 }else{
22199                     parent = el.offsetParent;
22200                 }
22201                 if (parent) {
22202                     return true;
22203                 }
22204             }
22205
22206             return false;
22207         },
22208
22209         /**
22210          * Returns a Region object containing the drag and drop element's position
22211          * and size, including the padding configured for it
22212          * @method getLocation
22213          * @param {DragDrop} oDD the drag and drop object to get the
22214          *                       location for
22215          * @return {Roo.lib.Region} a Region object representing the total area
22216          *                             the element occupies, including any padding
22217          *                             the instance is configured for.
22218          * @static
22219          */
22220         getLocation: function(oDD) {
22221             if (! this.isTypeOfDD(oDD)) {
22222                 return null;
22223             }
22224
22225             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22226
22227             try {
22228                 pos= Roo.lib.Dom.getXY(el);
22229             } catch (e) { }
22230
22231             if (!pos) {
22232                 return null;
22233             }
22234
22235             x1 = pos[0];
22236             x2 = x1 + el.offsetWidth;
22237             y1 = pos[1];
22238             y2 = y1 + el.offsetHeight;
22239
22240             t = y1 - oDD.padding[0];
22241             r = x2 + oDD.padding[1];
22242             b = y2 + oDD.padding[2];
22243             l = x1 - oDD.padding[3];
22244
22245             return new Roo.lib.Region( t, r, b, l );
22246         },
22247
22248         /**
22249          * Checks the cursor location to see if it over the target
22250          * @method isOverTarget
22251          * @param {Roo.lib.Point} pt The point to evaluate
22252          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22253          * @return {boolean} true if the mouse is over the target
22254          * @private
22255          * @static
22256          */
22257         isOverTarget: function(pt, oTarget, intersect) {
22258             // use cache if available
22259             var loc = this.locationCache[oTarget.id];
22260             if (!loc || !this.useCache) {
22261                 loc = this.getLocation(oTarget);
22262                 this.locationCache[oTarget.id] = loc;
22263
22264             }
22265
22266             if (!loc) {
22267                 return false;
22268             }
22269
22270             oTarget.cursorIsOver = loc.contains( pt );
22271
22272             // DragDrop is using this as a sanity check for the initial mousedown
22273             // in this case we are done.  In POINT mode, if the drag obj has no
22274             // contraints, we are also done. Otherwise we need to evaluate the
22275             // location of the target as related to the actual location of the
22276             // dragged element.
22277             var dc = this.dragCurrent;
22278             if (!dc || !dc.getTargetCoord ||
22279                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22280                 return oTarget.cursorIsOver;
22281             }
22282
22283             oTarget.overlap = null;
22284
22285             // Get the current location of the drag element, this is the
22286             // location of the mouse event less the delta that represents
22287             // where the original mousedown happened on the element.  We
22288             // need to consider constraints and ticks as well.
22289             var pos = dc.getTargetCoord(pt.x, pt.y);
22290
22291             var el = dc.getDragEl();
22292             var curRegion = new Roo.lib.Region( pos.y,
22293                                                    pos.x + el.offsetWidth,
22294                                                    pos.y + el.offsetHeight,
22295                                                    pos.x );
22296
22297             var overlap = curRegion.intersect(loc);
22298
22299             if (overlap) {
22300                 oTarget.overlap = overlap;
22301                 return (intersect) ? true : oTarget.cursorIsOver;
22302             } else {
22303                 return false;
22304             }
22305         },
22306
22307         /**
22308          * unload event handler
22309          * @method _onUnload
22310          * @private
22311          * @static
22312          */
22313         _onUnload: function(e, me) {
22314             Roo.dd.DragDropMgr.unregAll();
22315         },
22316
22317         /**
22318          * Cleans up the drag and drop events and objects.
22319          * @method unregAll
22320          * @private
22321          * @static
22322          */
22323         unregAll: function() {
22324
22325             if (this.dragCurrent) {
22326                 this.stopDrag();
22327                 this.dragCurrent = null;
22328             }
22329
22330             this._execOnAll("unreg", []);
22331
22332             for (i in this.elementCache) {
22333                 delete this.elementCache[i];
22334             }
22335
22336             this.elementCache = {};
22337             this.ids = {};
22338         },
22339
22340         /**
22341          * A cache of DOM elements
22342          * @property elementCache
22343          * @private
22344          * @static
22345          */
22346         elementCache: {},
22347
22348         /**
22349          * Get the wrapper for the DOM element specified
22350          * @method getElWrapper
22351          * @param {String} id the id of the element to get
22352          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22353          * @private
22354          * @deprecated This wrapper isn't that useful
22355          * @static
22356          */
22357         getElWrapper: function(id) {
22358             var oWrapper = this.elementCache[id];
22359             if (!oWrapper || !oWrapper.el) {
22360                 oWrapper = this.elementCache[id] =
22361                     new this.ElementWrapper(Roo.getDom(id));
22362             }
22363             return oWrapper;
22364         },
22365
22366         /**
22367          * Returns the actual DOM element
22368          * @method getElement
22369          * @param {String} id the id of the elment to get
22370          * @return {Object} The element
22371          * @deprecated use Roo.getDom instead
22372          * @static
22373          */
22374         getElement: function(id) {
22375             return Roo.getDom(id);
22376         },
22377
22378         /**
22379          * Returns the style property for the DOM element (i.e.,
22380          * document.getElById(id).style)
22381          * @method getCss
22382          * @param {String} id the id of the elment to get
22383          * @return {Object} The style property of the element
22384          * @deprecated use Roo.getDom instead
22385          * @static
22386          */
22387         getCss: function(id) {
22388             var el = Roo.getDom(id);
22389             return (el) ? el.style : null;
22390         },
22391
22392         /**
22393          * Inner class for cached elements
22394          * @class DragDropMgr.ElementWrapper
22395          * @for DragDropMgr
22396          * @private
22397          * @deprecated
22398          */
22399         ElementWrapper: function(el) {
22400                 /**
22401                  * The element
22402                  * @property el
22403                  */
22404                 this.el = el || null;
22405                 /**
22406                  * The element id
22407                  * @property id
22408                  */
22409                 this.id = this.el && el.id;
22410                 /**
22411                  * A reference to the style property
22412                  * @property css
22413                  */
22414                 this.css = this.el && el.style;
22415             },
22416
22417         /**
22418          * Returns the X position of an html element
22419          * @method getPosX
22420          * @param el the element for which to get the position
22421          * @return {int} the X coordinate
22422          * @for DragDropMgr
22423          * @deprecated use Roo.lib.Dom.getX instead
22424          * @static
22425          */
22426         getPosX: function(el) {
22427             return Roo.lib.Dom.getX(el);
22428         },
22429
22430         /**
22431          * Returns the Y position of an html element
22432          * @method getPosY
22433          * @param el the element for which to get the position
22434          * @return {int} the Y coordinate
22435          * @deprecated use Roo.lib.Dom.getY instead
22436          * @static
22437          */
22438         getPosY: function(el) {
22439             return Roo.lib.Dom.getY(el);
22440         },
22441
22442         /**
22443          * Swap two nodes.  In IE, we use the native method, for others we
22444          * emulate the IE behavior
22445          * @method swapNode
22446          * @param n1 the first node to swap
22447          * @param n2 the other node to swap
22448          * @static
22449          */
22450         swapNode: function(n1, n2) {
22451             if (n1.swapNode) {
22452                 n1.swapNode(n2);
22453             } else {
22454                 var p = n2.parentNode;
22455                 var s = n2.nextSibling;
22456
22457                 if (s == n1) {
22458                     p.insertBefore(n1, n2);
22459                 } else if (n2 == n1.nextSibling) {
22460                     p.insertBefore(n2, n1);
22461                 } else {
22462                     n1.parentNode.replaceChild(n2, n1);
22463                     p.insertBefore(n1, s);
22464                 }
22465             }
22466         },
22467
22468         /**
22469          * Returns the current scroll position
22470          * @method getScroll
22471          * @private
22472          * @static
22473          */
22474         getScroll: function () {
22475             var t, l, dde=document.documentElement, db=document.body;
22476             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22477                 t = dde.scrollTop;
22478                 l = dde.scrollLeft;
22479             } else if (db) {
22480                 t = db.scrollTop;
22481                 l = db.scrollLeft;
22482             } else {
22483
22484             }
22485             return { top: t, left: l };
22486         },
22487
22488         /**
22489          * Returns the specified element style property
22490          * @method getStyle
22491          * @param {HTMLElement} el          the element
22492          * @param {string}      styleProp   the style property
22493          * @return {string} The value of the style property
22494          * @deprecated use Roo.lib.Dom.getStyle
22495          * @static
22496          */
22497         getStyle: function(el, styleProp) {
22498             return Roo.fly(el).getStyle(styleProp);
22499         },
22500
22501         /**
22502          * Gets the scrollTop
22503          * @method getScrollTop
22504          * @return {int} the document's scrollTop
22505          * @static
22506          */
22507         getScrollTop: function () { return this.getScroll().top; },
22508
22509         /**
22510          * Gets the scrollLeft
22511          * @method getScrollLeft
22512          * @return {int} the document's scrollTop
22513          * @static
22514          */
22515         getScrollLeft: function () { return this.getScroll().left; },
22516
22517         /**
22518          * Sets the x/y position of an element to the location of the
22519          * target element.
22520          * @method moveToEl
22521          * @param {HTMLElement} moveEl      The element to move
22522          * @param {HTMLElement} targetEl    The position reference element
22523          * @static
22524          */
22525         moveToEl: function (moveEl, targetEl) {
22526             var aCoord = Roo.lib.Dom.getXY(targetEl);
22527             Roo.lib.Dom.setXY(moveEl, aCoord);
22528         },
22529
22530         /**
22531          * Numeric array sort function
22532          * @method numericSort
22533          * @static
22534          */
22535         numericSort: function(a, b) { return (a - b); },
22536
22537         /**
22538          * Internal counter
22539          * @property _timeoutCount
22540          * @private
22541          * @static
22542          */
22543         _timeoutCount: 0,
22544
22545         /**
22546          * Trying to make the load order less important.  Without this we get
22547          * an error if this file is loaded before the Event Utility.
22548          * @method _addListeners
22549          * @private
22550          * @static
22551          */
22552         _addListeners: function() {
22553             var DDM = Roo.dd.DDM;
22554             if ( Roo.lib.Event && document ) {
22555                 DDM._onLoad();
22556             } else {
22557                 if (DDM._timeoutCount > 2000) {
22558                 } else {
22559                     setTimeout(DDM._addListeners, 10);
22560                     if (document && document.body) {
22561                         DDM._timeoutCount += 1;
22562                     }
22563                 }
22564             }
22565         },
22566
22567         /**
22568          * Recursively searches the immediate parent and all child nodes for
22569          * the handle element in order to determine wheter or not it was
22570          * clicked.
22571          * @method handleWasClicked
22572          * @param node the html element to inspect
22573          * @static
22574          */
22575         handleWasClicked: function(node, id) {
22576             if (this.isHandle(id, node.id)) {
22577                 return true;
22578             } else {
22579                 // check to see if this is a text node child of the one we want
22580                 var p = node.parentNode;
22581
22582                 while (p) {
22583                     if (this.isHandle(id, p.id)) {
22584                         return true;
22585                     } else {
22586                         p = p.parentNode;
22587                     }
22588                 }
22589             }
22590
22591             return false;
22592         }
22593
22594     };
22595
22596 }();
22597
22598 // shorter alias, save a few bytes
22599 Roo.dd.DDM = Roo.dd.DragDropMgr;
22600 Roo.dd.DDM._addListeners();
22601
22602 }/*
22603  * Based on:
22604  * Ext JS Library 1.1.1
22605  * Copyright(c) 2006-2007, Ext JS, LLC.
22606  *
22607  * Originally Released Under LGPL - original licence link has changed is not relivant.
22608  *
22609  * Fork - LGPL
22610  * <script type="text/javascript">
22611  */
22612
22613 /**
22614  * @class Roo.dd.DD
22615  * A DragDrop implementation where the linked element follows the
22616  * mouse cursor during a drag.
22617  * @extends Roo.dd.DragDrop
22618  * @constructor
22619  * @param {String} id the id of the linked element
22620  * @param {String} sGroup the group of related DragDrop items
22621  * @param {object} config an object containing configurable attributes
22622  *                Valid properties for DD:
22623  *                    scroll
22624  */
22625 Roo.dd.DD = function(id, sGroup, config) {
22626     if (id) {
22627         this.init(id, sGroup, config);
22628     }
22629 };
22630
22631 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22632
22633     /**
22634      * When set to true, the utility automatically tries to scroll the browser
22635      * window wehn a drag and drop element is dragged near the viewport boundary.
22636      * Defaults to true.
22637      * @property scroll
22638      * @type boolean
22639      */
22640     scroll: true,
22641
22642     /**
22643      * Sets the pointer offset to the distance between the linked element's top
22644      * left corner and the location the element was clicked
22645      * @method autoOffset
22646      * @param {int} iPageX the X coordinate of the click
22647      * @param {int} iPageY the Y coordinate of the click
22648      */
22649     autoOffset: function(iPageX, iPageY) {
22650         var x = iPageX - this.startPageX;
22651         var y = iPageY - this.startPageY;
22652         this.setDelta(x, y);
22653     },
22654
22655     /**
22656      * Sets the pointer offset.  You can call this directly to force the
22657      * offset to be in a particular location (e.g., pass in 0,0 to set it
22658      * to the center of the object)
22659      * @method setDelta
22660      * @param {int} iDeltaX the distance from the left
22661      * @param {int} iDeltaY the distance from the top
22662      */
22663     setDelta: function(iDeltaX, iDeltaY) {
22664         this.deltaX = iDeltaX;
22665         this.deltaY = iDeltaY;
22666     },
22667
22668     /**
22669      * Sets the drag element to the location of the mousedown or click event,
22670      * maintaining the cursor location relative to the location on the element
22671      * that was clicked.  Override this if you want to place the element in a
22672      * location other than where the cursor is.
22673      * @method setDragElPos
22674      * @param {int} iPageX the X coordinate of the mousedown or drag event
22675      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22676      */
22677     setDragElPos: function(iPageX, iPageY) {
22678         // the first time we do this, we are going to check to make sure
22679         // the element has css positioning
22680
22681         var el = this.getDragEl();
22682         this.alignElWithMouse(el, iPageX, iPageY);
22683     },
22684
22685     /**
22686      * Sets the element to the location of the mousedown or click event,
22687      * maintaining the cursor location relative to the location on the element
22688      * that was clicked.  Override this if you want to place the element in a
22689      * location other than where the cursor is.
22690      * @method alignElWithMouse
22691      * @param {HTMLElement} el the element to move
22692      * @param {int} iPageX the X coordinate of the mousedown or drag event
22693      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22694      */
22695     alignElWithMouse: function(el, iPageX, iPageY) {
22696         var oCoord = this.getTargetCoord(iPageX, iPageY);
22697         var fly = el.dom ? el : Roo.fly(el);
22698         if (!this.deltaSetXY) {
22699             var aCoord = [oCoord.x, oCoord.y];
22700             fly.setXY(aCoord);
22701             var newLeft = fly.getLeft(true);
22702             var newTop  = fly.getTop(true);
22703             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22704         } else {
22705             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22706         }
22707
22708         this.cachePosition(oCoord.x, oCoord.y);
22709         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22710         return oCoord;
22711     },
22712
22713     /**
22714      * Saves the most recent position so that we can reset the constraints and
22715      * tick marks on-demand.  We need to know this so that we can calculate the
22716      * number of pixels the element is offset from its original position.
22717      * @method cachePosition
22718      * @param iPageX the current x position (optional, this just makes it so we
22719      * don't have to look it up again)
22720      * @param iPageY the current y position (optional, this just makes it so we
22721      * don't have to look it up again)
22722      */
22723     cachePosition: function(iPageX, iPageY) {
22724         if (iPageX) {
22725             this.lastPageX = iPageX;
22726             this.lastPageY = iPageY;
22727         } else {
22728             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22729             this.lastPageX = aCoord[0];
22730             this.lastPageY = aCoord[1];
22731         }
22732     },
22733
22734     /**
22735      * Auto-scroll the window if the dragged object has been moved beyond the
22736      * visible window boundary.
22737      * @method autoScroll
22738      * @param {int} x the drag element's x position
22739      * @param {int} y the drag element's y position
22740      * @param {int} h the height of the drag element
22741      * @param {int} w the width of the drag element
22742      * @private
22743      */
22744     autoScroll: function(x, y, h, w) {
22745
22746         if (this.scroll) {
22747             // The client height
22748             var clientH = Roo.lib.Dom.getViewWidth();
22749
22750             // The client width
22751             var clientW = Roo.lib.Dom.getViewHeight();
22752
22753             // The amt scrolled down
22754             var st = this.DDM.getScrollTop();
22755
22756             // The amt scrolled right
22757             var sl = this.DDM.getScrollLeft();
22758
22759             // Location of the bottom of the element
22760             var bot = h + y;
22761
22762             // Location of the right of the element
22763             var right = w + x;
22764
22765             // The distance from the cursor to the bottom of the visible area,
22766             // adjusted so that we don't scroll if the cursor is beyond the
22767             // element drag constraints
22768             var toBot = (clientH + st - y - this.deltaY);
22769
22770             // The distance from the cursor to the right of the visible area
22771             var toRight = (clientW + sl - x - this.deltaX);
22772
22773
22774             // How close to the edge the cursor must be before we scroll
22775             // var thresh = (document.all) ? 100 : 40;
22776             var thresh = 40;
22777
22778             // How many pixels to scroll per autoscroll op.  This helps to reduce
22779             // clunky scrolling. IE is more sensitive about this ... it needs this
22780             // value to be higher.
22781             var scrAmt = (document.all) ? 80 : 30;
22782
22783             // Scroll down if we are near the bottom of the visible page and the
22784             // obj extends below the crease
22785             if ( bot > clientH && toBot < thresh ) {
22786                 window.scrollTo(sl, st + scrAmt);
22787             }
22788
22789             // Scroll up if the window is scrolled down and the top of the object
22790             // goes above the top border
22791             if ( y < st && st > 0 && y - st < thresh ) {
22792                 window.scrollTo(sl, st - scrAmt);
22793             }
22794
22795             // Scroll right if the obj is beyond the right border and the cursor is
22796             // near the border.
22797             if ( right > clientW && toRight < thresh ) {
22798                 window.scrollTo(sl + scrAmt, st);
22799             }
22800
22801             // Scroll left if the window has been scrolled to the right and the obj
22802             // extends past the left border
22803             if ( x < sl && sl > 0 && x - sl < thresh ) {
22804                 window.scrollTo(sl - scrAmt, st);
22805             }
22806         }
22807     },
22808
22809     /**
22810      * Finds the location the element should be placed if we want to move
22811      * it to where the mouse location less the click offset would place us.
22812      * @method getTargetCoord
22813      * @param {int} iPageX the X coordinate of the click
22814      * @param {int} iPageY the Y coordinate of the click
22815      * @return an object that contains the coordinates (Object.x and Object.y)
22816      * @private
22817      */
22818     getTargetCoord: function(iPageX, iPageY) {
22819
22820
22821         var x = iPageX - this.deltaX;
22822         var y = iPageY - this.deltaY;
22823
22824         if (this.constrainX) {
22825             if (x < this.minX) { x = this.minX; }
22826             if (x > this.maxX) { x = this.maxX; }
22827         }
22828
22829         if (this.constrainY) {
22830             if (y < this.minY) { y = this.minY; }
22831             if (y > this.maxY) { y = this.maxY; }
22832         }
22833
22834         x = this.getTick(x, this.xTicks);
22835         y = this.getTick(y, this.yTicks);
22836
22837
22838         return {x:x, y:y};
22839     },
22840
22841     /*
22842      * Sets up config options specific to this class. Overrides
22843      * Roo.dd.DragDrop, but all versions of this method through the
22844      * inheritance chain are called
22845      */
22846     applyConfig: function() {
22847         Roo.dd.DD.superclass.applyConfig.call(this);
22848         this.scroll = (this.config.scroll !== false);
22849     },
22850
22851     /*
22852      * Event that fires prior to the onMouseDown event.  Overrides
22853      * Roo.dd.DragDrop.
22854      */
22855     b4MouseDown: function(e) {
22856         // this.resetConstraints();
22857         this.autoOffset(e.getPageX(),
22858                             e.getPageY());
22859     },
22860
22861     /*
22862      * Event that fires prior to the onDrag event.  Overrides
22863      * Roo.dd.DragDrop.
22864      */
22865     b4Drag: function(e) {
22866         this.setDragElPos(e.getPageX(),
22867                             e.getPageY());
22868     },
22869
22870     toString: function() {
22871         return ("DD " + this.id);
22872     }
22873
22874     //////////////////////////////////////////////////////////////////////////
22875     // Debugging ygDragDrop events that can be overridden
22876     //////////////////////////////////////////////////////////////////////////
22877     /*
22878     startDrag: function(x, y) {
22879     },
22880
22881     onDrag: function(e) {
22882     },
22883
22884     onDragEnter: function(e, id) {
22885     },
22886
22887     onDragOver: function(e, id) {
22888     },
22889
22890     onDragOut: function(e, id) {
22891     },
22892
22893     onDragDrop: function(e, id) {
22894     },
22895
22896     endDrag: function(e) {
22897     }
22898
22899     */
22900
22901 });/*
22902  * Based on:
22903  * Ext JS Library 1.1.1
22904  * Copyright(c) 2006-2007, Ext JS, LLC.
22905  *
22906  * Originally Released Under LGPL - original licence link has changed is not relivant.
22907  *
22908  * Fork - LGPL
22909  * <script type="text/javascript">
22910  */
22911
22912 /**
22913  * @class Roo.dd.DDProxy
22914  * A DragDrop implementation that inserts an empty, bordered div into
22915  * the document that follows the cursor during drag operations.  At the time of
22916  * the click, the frame div is resized to the dimensions of the linked html
22917  * element, and moved to the exact location of the linked element.
22918  *
22919  * References to the "frame" element refer to the single proxy element that
22920  * was created to be dragged in place of all DDProxy elements on the
22921  * page.
22922  *
22923  * @extends Roo.dd.DD
22924  * @constructor
22925  * @param {String} id the id of the linked html element
22926  * @param {String} sGroup the group of related DragDrop objects
22927  * @param {object} config an object containing configurable attributes
22928  *                Valid properties for DDProxy in addition to those in DragDrop:
22929  *                   resizeFrame, centerFrame, dragElId
22930  */
22931 Roo.dd.DDProxy = function(id, sGroup, config) {
22932     if (id) {
22933         this.init(id, sGroup, config);
22934         this.initFrame();
22935     }
22936 };
22937
22938 /**
22939  * The default drag frame div id
22940  * @property Roo.dd.DDProxy.dragElId
22941  * @type String
22942  * @static
22943  */
22944 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22945
22946 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22947
22948     /**
22949      * By default we resize the drag frame to be the same size as the element
22950      * we want to drag (this is to get the frame effect).  We can turn it off
22951      * if we want a different behavior.
22952      * @property resizeFrame
22953      * @type boolean
22954      */
22955     resizeFrame: true,
22956
22957     /**
22958      * By default the frame is positioned exactly where the drag element is, so
22959      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22960      * you do not have constraints on the obj is to have the drag frame centered
22961      * around the cursor.  Set centerFrame to true for this effect.
22962      * @property centerFrame
22963      * @type boolean
22964      */
22965     centerFrame: false,
22966
22967     /**
22968      * Creates the proxy element if it does not yet exist
22969      * @method createFrame
22970      */
22971     createFrame: function() {
22972         var self = this;
22973         var body = document.body;
22974
22975         if (!body || !body.firstChild) {
22976             setTimeout( function() { self.createFrame(); }, 50 );
22977             return;
22978         }
22979
22980         var div = this.getDragEl();
22981
22982         if (!div) {
22983             div    = document.createElement("div");
22984             div.id = this.dragElId;
22985             var s  = div.style;
22986
22987             s.position   = "absolute";
22988             s.visibility = "hidden";
22989             s.cursor     = "move";
22990             s.border     = "2px solid #aaa";
22991             s.zIndex     = 999;
22992
22993             // appendChild can blow up IE if invoked prior to the window load event
22994             // while rendering a table.  It is possible there are other scenarios
22995             // that would cause this to happen as well.
22996             body.insertBefore(div, body.firstChild);
22997         }
22998     },
22999
23000     /**
23001      * Initialization for the drag frame element.  Must be called in the
23002      * constructor of all subclasses
23003      * @method initFrame
23004      */
23005     initFrame: function() {
23006         this.createFrame();
23007     },
23008
23009     applyConfig: function() {
23010         Roo.dd.DDProxy.superclass.applyConfig.call(this);
23011
23012         this.resizeFrame = (this.config.resizeFrame !== false);
23013         this.centerFrame = (this.config.centerFrame);
23014         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
23015     },
23016
23017     /**
23018      * Resizes the drag frame to the dimensions of the clicked object, positions
23019      * it over the object, and finally displays it
23020      * @method showFrame
23021      * @param {int} iPageX X click position
23022      * @param {int} iPageY Y click position
23023      * @private
23024      */
23025     showFrame: function(iPageX, iPageY) {
23026         var el = this.getEl();
23027         var dragEl = this.getDragEl();
23028         var s = dragEl.style;
23029
23030         this._resizeProxy();
23031
23032         if (this.centerFrame) {
23033             this.setDelta( Math.round(parseInt(s.width,  10)/2),
23034                            Math.round(parseInt(s.height, 10)/2) );
23035         }
23036
23037         this.setDragElPos(iPageX, iPageY);
23038
23039         Roo.fly(dragEl).show();
23040     },
23041
23042     /**
23043      * The proxy is automatically resized to the dimensions of the linked
23044      * element when a drag is initiated, unless resizeFrame is set to false
23045      * @method _resizeProxy
23046      * @private
23047      */
23048     _resizeProxy: function() {
23049         if (this.resizeFrame) {
23050             var el = this.getEl();
23051             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23052         }
23053     },
23054
23055     // overrides Roo.dd.DragDrop
23056     b4MouseDown: function(e) {
23057         var x = e.getPageX();
23058         var y = e.getPageY();
23059         this.autoOffset(x, y);
23060         this.setDragElPos(x, y);
23061     },
23062
23063     // overrides Roo.dd.DragDrop
23064     b4StartDrag: function(x, y) {
23065         // show the drag frame
23066         this.showFrame(x, y);
23067     },
23068
23069     // overrides Roo.dd.DragDrop
23070     b4EndDrag: function(e) {
23071         Roo.fly(this.getDragEl()).hide();
23072     },
23073
23074     // overrides Roo.dd.DragDrop
23075     // By default we try to move the element to the last location of the frame.
23076     // This is so that the default behavior mirrors that of Roo.dd.DD.
23077     endDrag: function(e) {
23078
23079         var lel = this.getEl();
23080         var del = this.getDragEl();
23081
23082         // Show the drag frame briefly so we can get its position
23083         del.style.visibility = "";
23084
23085         this.beforeMove();
23086         // Hide the linked element before the move to get around a Safari
23087         // rendering bug.
23088         lel.style.visibility = "hidden";
23089         Roo.dd.DDM.moveToEl(lel, del);
23090         del.style.visibility = "hidden";
23091         lel.style.visibility = "";
23092
23093         this.afterDrag();
23094     },
23095
23096     beforeMove : function(){
23097
23098     },
23099
23100     afterDrag : function(){
23101
23102     },
23103
23104     toString: function() {
23105         return ("DDProxy " + this.id);
23106     }
23107
23108 });
23109 /*
23110  * Based on:
23111  * Ext JS Library 1.1.1
23112  * Copyright(c) 2006-2007, Ext JS, LLC.
23113  *
23114  * Originally Released Under LGPL - original licence link has changed is not relivant.
23115  *
23116  * Fork - LGPL
23117  * <script type="text/javascript">
23118  */
23119
23120  /**
23121  * @class Roo.dd.DDTarget
23122  * A DragDrop implementation that does not move, but can be a drop
23123  * target.  You would get the same result by simply omitting implementation
23124  * for the event callbacks, but this way we reduce the processing cost of the
23125  * event listener and the callbacks.
23126  * @extends Roo.dd.DragDrop
23127  * @constructor
23128  * @param {String} id the id of the element that is a drop target
23129  * @param {String} sGroup the group of related DragDrop objects
23130  * @param {object} config an object containing configurable attributes
23131  *                 Valid properties for DDTarget in addition to those in
23132  *                 DragDrop:
23133  *                    none
23134  */
23135 Roo.dd.DDTarget = function(id, sGroup, config) {
23136     if (id) {
23137         this.initTarget(id, sGroup, config);
23138     }
23139     if (config && (config.listeners || config.events)) { 
23140         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
23141             listeners : config.listeners || {}, 
23142             events : config.events || {} 
23143         });    
23144     }
23145 };
23146
23147 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23148 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23149     toString: function() {
23150         return ("DDTarget " + this.id);
23151     }
23152 });
23153 /*
23154  * Based on:
23155  * Ext JS Library 1.1.1
23156  * Copyright(c) 2006-2007, Ext JS, LLC.
23157  *
23158  * Originally Released Under LGPL - original licence link has changed is not relivant.
23159  *
23160  * Fork - LGPL
23161  * <script type="text/javascript">
23162  */
23163  
23164
23165 /**
23166  * @class Roo.dd.ScrollManager
23167  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23168  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23169  * @static
23170  */
23171 Roo.dd.ScrollManager = function(){
23172     var ddm = Roo.dd.DragDropMgr;
23173     var els = {};
23174     var dragEl = null;
23175     var proc = {};
23176     
23177     
23178     
23179     var onStop = function(e){
23180         dragEl = null;
23181         clearProc();
23182     };
23183     
23184     var triggerRefresh = function(){
23185         if(ddm.dragCurrent){
23186              ddm.refreshCache(ddm.dragCurrent.groups);
23187         }
23188     };
23189     
23190     var doScroll = function(){
23191         if(ddm.dragCurrent){
23192             var dds = Roo.dd.ScrollManager;
23193             if(!dds.animate){
23194                 if(proc.el.scroll(proc.dir, dds.increment)){
23195                     triggerRefresh();
23196                 }
23197             }else{
23198                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23199             }
23200         }
23201     };
23202     
23203     var clearProc = function(){
23204         if(proc.id){
23205             clearInterval(proc.id);
23206         }
23207         proc.id = 0;
23208         proc.el = null;
23209         proc.dir = "";
23210     };
23211     
23212     var startProc = function(el, dir){
23213          Roo.log('scroll startproc');
23214         clearProc();
23215         proc.el = el;
23216         proc.dir = dir;
23217         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23218     };
23219     
23220     var onFire = function(e, isDrop){
23221        
23222         if(isDrop || !ddm.dragCurrent){ return; }
23223         var dds = Roo.dd.ScrollManager;
23224         if(!dragEl || dragEl != ddm.dragCurrent){
23225             dragEl = ddm.dragCurrent;
23226             // refresh regions on drag start
23227             dds.refreshCache();
23228         }
23229         
23230         var xy = Roo.lib.Event.getXY(e);
23231         var pt = new Roo.lib.Point(xy[0], xy[1]);
23232         for(var id in els){
23233             var el = els[id], r = el._region;
23234             if(r && r.contains(pt) && el.isScrollable()){
23235                 if(r.bottom - pt.y <= dds.thresh){
23236                     if(proc.el != el){
23237                         startProc(el, "down");
23238                     }
23239                     return;
23240                 }else if(r.right - pt.x <= dds.thresh){
23241                     if(proc.el != el){
23242                         startProc(el, "left");
23243                     }
23244                     return;
23245                 }else if(pt.y - r.top <= dds.thresh){
23246                     if(proc.el != el){
23247                         startProc(el, "up");
23248                     }
23249                     return;
23250                 }else if(pt.x - r.left <= dds.thresh){
23251                     if(proc.el != el){
23252                         startProc(el, "right");
23253                     }
23254                     return;
23255                 }
23256             }
23257         }
23258         clearProc();
23259     };
23260     
23261     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23262     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23263     
23264     return {
23265         /**
23266          * Registers new overflow element(s) to auto scroll
23267          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23268          */
23269         register : function(el){
23270             if(el instanceof Array){
23271                 for(var i = 0, len = el.length; i < len; i++) {
23272                         this.register(el[i]);
23273                 }
23274             }else{
23275                 el = Roo.get(el);
23276                 els[el.id] = el;
23277             }
23278             Roo.dd.ScrollManager.els = els;
23279         },
23280         
23281         /**
23282          * Unregisters overflow element(s) so they are no longer scrolled
23283          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23284          */
23285         unregister : function(el){
23286             if(el instanceof Array){
23287                 for(var i = 0, len = el.length; i < len; i++) {
23288                         this.unregister(el[i]);
23289                 }
23290             }else{
23291                 el = Roo.get(el);
23292                 delete els[el.id];
23293             }
23294         },
23295         
23296         /**
23297          * The number of pixels from the edge of a container the pointer needs to be to 
23298          * trigger scrolling (defaults to 25)
23299          * @type Number
23300          */
23301         thresh : 25,
23302         
23303         /**
23304          * The number of pixels to scroll in each scroll increment (defaults to 50)
23305          * @type Number
23306          */
23307         increment : 100,
23308         
23309         /**
23310          * The frequency of scrolls in milliseconds (defaults to 500)
23311          * @type Number
23312          */
23313         frequency : 500,
23314         
23315         /**
23316          * True to animate the scroll (defaults to true)
23317          * @type Boolean
23318          */
23319         animate: true,
23320         
23321         /**
23322          * The animation duration in seconds - 
23323          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23324          * @type Number
23325          */
23326         animDuration: .4,
23327         
23328         /**
23329          * Manually trigger a cache refresh.
23330          */
23331         refreshCache : function(){
23332             for(var id in els){
23333                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23334                     els[id]._region = els[id].getRegion();
23335                 }
23336             }
23337         }
23338     };
23339 }();/*
23340  * Based on:
23341  * Ext JS Library 1.1.1
23342  * Copyright(c) 2006-2007, Ext JS, LLC.
23343  *
23344  * Originally Released Under LGPL - original licence link has changed is not relivant.
23345  *
23346  * Fork - LGPL
23347  * <script type="text/javascript">
23348  */
23349  
23350
23351 /**
23352  * @class Roo.dd.Registry
23353  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23354  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23355  * @static
23356  */
23357 Roo.dd.Registry = function(){
23358     var elements = {}; 
23359     var handles = {}; 
23360     var autoIdSeed = 0;
23361
23362     var getId = function(el, autogen){
23363         if(typeof el == "string"){
23364             return el;
23365         }
23366         var id = el.id;
23367         if(!id && autogen !== false){
23368             id = "roodd-" + (++autoIdSeed);
23369             el.id = id;
23370         }
23371         return id;
23372     };
23373     
23374     return {
23375     /**
23376      * Register a drag drop element
23377      * @param {String|HTMLElement} element The id or DOM node to register
23378      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23379      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23380      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23381      * populated in the data object (if applicable):
23382      * <pre>
23383 Value      Description<br />
23384 ---------  ------------------------------------------<br />
23385 handles    Array of DOM nodes that trigger dragging<br />
23386            for the element being registered<br />
23387 isHandle   True if the element passed in triggers<br />
23388            dragging itself, else false
23389 </pre>
23390      */
23391         register : function(el, data){
23392             data = data || {};
23393             if(typeof el == "string"){
23394                 el = document.getElementById(el);
23395             }
23396             data.ddel = el;
23397             elements[getId(el)] = data;
23398             if(data.isHandle !== false){
23399                 handles[data.ddel.id] = data;
23400             }
23401             if(data.handles){
23402                 var hs = data.handles;
23403                 for(var i = 0, len = hs.length; i < len; i++){
23404                         handles[getId(hs[i])] = data;
23405                 }
23406             }
23407         },
23408
23409     /**
23410      * Unregister a drag drop element
23411      * @param {String|HTMLElement}  element The id or DOM node to unregister
23412      */
23413         unregister : function(el){
23414             var id = getId(el, false);
23415             var data = elements[id];
23416             if(data){
23417                 delete elements[id];
23418                 if(data.handles){
23419                     var hs = data.handles;
23420                     for(var i = 0, len = hs.length; i < len; i++){
23421                         delete handles[getId(hs[i], false)];
23422                     }
23423                 }
23424             }
23425         },
23426
23427     /**
23428      * Returns the handle registered for a DOM Node by id
23429      * @param {String|HTMLElement} id The DOM node or id to look up
23430      * @return {Object} handle The custom handle data
23431      */
23432         getHandle : function(id){
23433             if(typeof id != "string"){ // must be element?
23434                 id = id.id;
23435             }
23436             return handles[id];
23437         },
23438
23439     /**
23440      * Returns the handle that is registered for the DOM node that is the target of the event
23441      * @param {Event} e The event
23442      * @return {Object} handle The custom handle data
23443      */
23444         getHandleFromEvent : function(e){
23445             var t = Roo.lib.Event.getTarget(e);
23446             return t ? handles[t.id] : null;
23447         },
23448
23449     /**
23450      * Returns a custom data object that is registered for a DOM node by id
23451      * @param {String|HTMLElement} id The DOM node or id to look up
23452      * @return {Object} data The custom data
23453      */
23454         getTarget : function(id){
23455             if(typeof id != "string"){ // must be element?
23456                 id = id.id;
23457             }
23458             return elements[id];
23459         },
23460
23461     /**
23462      * Returns a custom data object that is registered for the DOM node that is the target of the event
23463      * @param {Event} e The event
23464      * @return {Object} data The custom data
23465      */
23466         getTargetFromEvent : function(e){
23467             var t = Roo.lib.Event.getTarget(e);
23468             return t ? elements[t.id] || handles[t.id] : null;
23469         }
23470     };
23471 }();/*
23472  * Based on:
23473  * Ext JS Library 1.1.1
23474  * Copyright(c) 2006-2007, Ext JS, LLC.
23475  *
23476  * Originally Released Under LGPL - original licence link has changed is not relivant.
23477  *
23478  * Fork - LGPL
23479  * <script type="text/javascript">
23480  */
23481  
23482
23483 /**
23484  * @class Roo.dd.StatusProxy
23485  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23486  * default drag proxy used by all Roo.dd components.
23487  * @constructor
23488  * @param {Object} config
23489  */
23490 Roo.dd.StatusProxy = function(config){
23491     Roo.apply(this, config);
23492     this.id = this.id || Roo.id();
23493     this.el = new Roo.Layer({
23494         dh: {
23495             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23496                 {tag: "div", cls: "x-dd-drop-icon"},
23497                 {tag: "div", cls: "x-dd-drag-ghost"}
23498             ]
23499         }, 
23500         shadow: !config || config.shadow !== false
23501     });
23502     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23503     this.dropStatus = this.dropNotAllowed;
23504 };
23505
23506 Roo.dd.StatusProxy.prototype = {
23507     /**
23508      * @cfg {String} dropAllowed
23509      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23510      */
23511     dropAllowed : "x-dd-drop-ok",
23512     /**
23513      * @cfg {String} dropNotAllowed
23514      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23515      */
23516     dropNotAllowed : "x-dd-drop-nodrop",
23517
23518     /**
23519      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23520      * over the current target element.
23521      * @param {String} cssClass The css class for the new drop status indicator image
23522      */
23523     setStatus : function(cssClass){
23524         cssClass = cssClass || this.dropNotAllowed;
23525         if(this.dropStatus != cssClass){
23526             this.el.replaceClass(this.dropStatus, cssClass);
23527             this.dropStatus = cssClass;
23528         }
23529     },
23530
23531     /**
23532      * Resets the status indicator to the default dropNotAllowed value
23533      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23534      */
23535     reset : function(clearGhost){
23536         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23537         this.dropStatus = this.dropNotAllowed;
23538         if(clearGhost){
23539             this.ghost.update("");
23540         }
23541     },
23542
23543     /**
23544      * Updates the contents of the ghost element
23545      * @param {String} html The html that will replace the current innerHTML of the ghost element
23546      */
23547     update : function(html){
23548         if(typeof html == "string"){
23549             this.ghost.update(html);
23550         }else{
23551             this.ghost.update("");
23552             html.style.margin = "0";
23553             this.ghost.dom.appendChild(html);
23554         }
23555         // ensure float = none set?? cant remember why though.
23556         var el = this.ghost.dom.firstChild;
23557                 if(el){
23558                         Roo.fly(el).setStyle('float', 'none');
23559                 }
23560     },
23561     
23562     /**
23563      * Returns the underlying proxy {@link Roo.Layer}
23564      * @return {Roo.Layer} el
23565     */
23566     getEl : function(){
23567         return this.el;
23568     },
23569
23570     /**
23571      * Returns the ghost element
23572      * @return {Roo.Element} el
23573      */
23574     getGhost : function(){
23575         return this.ghost;
23576     },
23577
23578     /**
23579      * Hides the proxy
23580      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23581      */
23582     hide : function(clear){
23583         this.el.hide();
23584         if(clear){
23585             this.reset(true);
23586         }
23587     },
23588
23589     /**
23590      * Stops the repair animation if it's currently running
23591      */
23592     stop : function(){
23593         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23594             this.anim.stop();
23595         }
23596     },
23597
23598     /**
23599      * Displays this proxy
23600      */
23601     show : function(){
23602         this.el.show();
23603     },
23604
23605     /**
23606      * Force the Layer to sync its shadow and shim positions to the element
23607      */
23608     sync : function(){
23609         this.el.sync();
23610     },
23611
23612     /**
23613      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23614      * invalid drop operation by the item being dragged.
23615      * @param {Array} xy The XY position of the element ([x, y])
23616      * @param {Function} callback The function to call after the repair is complete
23617      * @param {Object} scope The scope in which to execute the callback
23618      */
23619     repair : function(xy, callback, scope){
23620         this.callback = callback;
23621         this.scope = scope;
23622         if(xy && this.animRepair !== false){
23623             this.el.addClass("x-dd-drag-repair");
23624             this.el.hideUnders(true);
23625             this.anim = this.el.shift({
23626                 duration: this.repairDuration || .5,
23627                 easing: 'easeOut',
23628                 xy: xy,
23629                 stopFx: true,
23630                 callback: this.afterRepair,
23631                 scope: this
23632             });
23633         }else{
23634             this.afterRepair();
23635         }
23636     },
23637
23638     // private
23639     afterRepair : function(){
23640         this.hide(true);
23641         if(typeof this.callback == "function"){
23642             this.callback.call(this.scope || this);
23643         }
23644         this.callback = null;
23645         this.scope = null;
23646     }
23647 };/*
23648  * Based on:
23649  * Ext JS Library 1.1.1
23650  * Copyright(c) 2006-2007, Ext JS, LLC.
23651  *
23652  * Originally Released Under LGPL - original licence link has changed is not relivant.
23653  *
23654  * Fork - LGPL
23655  * <script type="text/javascript">
23656  */
23657
23658 /**
23659  * @class Roo.dd.DragSource
23660  * @extends Roo.dd.DDProxy
23661  * A simple class that provides the basic implementation needed to make any element draggable.
23662  * @constructor
23663  * @param {String/HTMLElement/Element} el The container element
23664  * @param {Object} config
23665  */
23666 Roo.dd.DragSource = function(el, config){
23667     this.el = Roo.get(el);
23668     this.dragData = {};
23669     
23670     Roo.apply(this, config);
23671     
23672     if(!this.proxy){
23673         this.proxy = new Roo.dd.StatusProxy();
23674     }
23675
23676     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23677           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23678     
23679     this.dragging = false;
23680 };
23681
23682 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23683     /**
23684      * @cfg {String} dropAllowed
23685      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23686      */
23687     dropAllowed : "x-dd-drop-ok",
23688     /**
23689      * @cfg {String} dropNotAllowed
23690      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23691      */
23692     dropNotAllowed : "x-dd-drop-nodrop",
23693
23694     /**
23695      * Returns the data object associated with this drag source
23696      * @return {Object} data An object containing arbitrary data
23697      */
23698     getDragData : function(e){
23699         return this.dragData;
23700     },
23701
23702     // private
23703     onDragEnter : function(e, id){
23704         var target = Roo.dd.DragDropMgr.getDDById(id);
23705         this.cachedTarget = target;
23706         if(this.beforeDragEnter(target, e, id) !== false){
23707             if(target.isNotifyTarget){
23708                 var status = target.notifyEnter(this, e, this.dragData);
23709                 this.proxy.setStatus(status);
23710             }else{
23711                 this.proxy.setStatus(this.dropAllowed);
23712             }
23713             
23714             if(this.afterDragEnter){
23715                 /**
23716                  * An empty function by default, but provided so that you can perform a custom action
23717                  * when the dragged item enters the drop target by providing an implementation.
23718                  * @param {Roo.dd.DragDrop} target The drop target
23719                  * @param {Event} e The event object
23720                  * @param {String} id The id of the dragged element
23721                  * @method afterDragEnter
23722                  */
23723                 this.afterDragEnter(target, e, id);
23724             }
23725         }
23726     },
23727
23728     /**
23729      * An empty function by default, but provided so that you can perform a custom action
23730      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23731      * @param {Roo.dd.DragDrop} target The drop target
23732      * @param {Event} e The event object
23733      * @param {String} id The id of the dragged element
23734      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23735      */
23736     beforeDragEnter : function(target, e, id){
23737         return true;
23738     },
23739
23740     // private
23741     alignElWithMouse: function() {
23742         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23743         this.proxy.sync();
23744     },
23745
23746     // private
23747     onDragOver : function(e, id){
23748         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23749         if(this.beforeDragOver(target, e, id) !== false){
23750             if(target.isNotifyTarget){
23751                 var status = target.notifyOver(this, e, this.dragData);
23752                 this.proxy.setStatus(status);
23753             }
23754
23755             if(this.afterDragOver){
23756                 /**
23757                  * An empty function by default, but provided so that you can perform a custom action
23758                  * while the dragged item is over the drop target by providing an implementation.
23759                  * @param {Roo.dd.DragDrop} target The drop target
23760                  * @param {Event} e The event object
23761                  * @param {String} id The id of the dragged element
23762                  * @method afterDragOver
23763                  */
23764                 this.afterDragOver(target, e, id);
23765             }
23766         }
23767     },
23768
23769     /**
23770      * An empty function by default, but provided so that you can perform a custom action
23771      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23772      * @param {Roo.dd.DragDrop} target The drop target
23773      * @param {Event} e The event object
23774      * @param {String} id The id of the dragged element
23775      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23776      */
23777     beforeDragOver : function(target, e, id){
23778         return true;
23779     },
23780
23781     // private
23782     onDragOut : function(e, id){
23783         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23784         if(this.beforeDragOut(target, e, id) !== false){
23785             if(target.isNotifyTarget){
23786                 target.notifyOut(this, e, this.dragData);
23787             }
23788             this.proxy.reset();
23789             if(this.afterDragOut){
23790                 /**
23791                  * An empty function by default, but provided so that you can perform a custom action
23792                  * after the dragged item is dragged out of the target without dropping.
23793                  * @param {Roo.dd.DragDrop} target The drop target
23794                  * @param {Event} e The event object
23795                  * @param {String} id The id of the dragged element
23796                  * @method afterDragOut
23797                  */
23798                 this.afterDragOut(target, e, id);
23799             }
23800         }
23801         this.cachedTarget = null;
23802     },
23803
23804     /**
23805      * An empty function by default, but provided so that you can perform a custom action before the dragged
23806      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23807      * @param {Roo.dd.DragDrop} target The drop target
23808      * @param {Event} e The event object
23809      * @param {String} id The id of the dragged element
23810      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23811      */
23812     beforeDragOut : function(target, e, id){
23813         return true;
23814     },
23815     
23816     // private
23817     onDragDrop : function(e, id){
23818         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23819         if(this.beforeDragDrop(target, e, id) !== false){
23820             if(target.isNotifyTarget){
23821                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23822                     this.onValidDrop(target, e, id);
23823                 }else{
23824                     this.onInvalidDrop(target, e, id);
23825                 }
23826             }else{
23827                 this.onValidDrop(target, e, id);
23828             }
23829             
23830             if(this.afterDragDrop){
23831                 /**
23832                  * An empty function by default, but provided so that you can perform a custom action
23833                  * after a valid drag drop has occurred by providing an implementation.
23834                  * @param {Roo.dd.DragDrop} target The drop target
23835                  * @param {Event} e The event object
23836                  * @param {String} id The id of the dropped element
23837                  * @method afterDragDrop
23838                  */
23839                 this.afterDragDrop(target, e, id);
23840             }
23841         }
23842         delete this.cachedTarget;
23843     },
23844
23845     /**
23846      * An empty function by default, but provided so that you can perform a custom action before the dragged
23847      * item is dropped onto the target and optionally cancel the onDragDrop.
23848      * @param {Roo.dd.DragDrop} target The drop target
23849      * @param {Event} e The event object
23850      * @param {String} id The id of the dragged element
23851      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23852      */
23853     beforeDragDrop : function(target, e, id){
23854         return true;
23855     },
23856
23857     // private
23858     onValidDrop : function(target, e, id){
23859         this.hideProxy();
23860         if(this.afterValidDrop){
23861             /**
23862              * An empty function by default, but provided so that you can perform a custom action
23863              * after a valid drop has occurred by providing an implementation.
23864              * @param {Object} target The target DD 
23865              * @param {Event} e The event object
23866              * @param {String} id The id of the dropped element
23867              * @method afterInvalidDrop
23868              */
23869             this.afterValidDrop(target, e, id);
23870         }
23871     },
23872
23873     // private
23874     getRepairXY : function(e, data){
23875         return this.el.getXY();  
23876     },
23877
23878     // private
23879     onInvalidDrop : function(target, e, id){
23880         this.beforeInvalidDrop(target, e, id);
23881         if(this.cachedTarget){
23882             if(this.cachedTarget.isNotifyTarget){
23883                 this.cachedTarget.notifyOut(this, e, this.dragData);
23884             }
23885             this.cacheTarget = null;
23886         }
23887         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23888
23889         if(this.afterInvalidDrop){
23890             /**
23891              * An empty function by default, but provided so that you can perform a custom action
23892              * after an invalid drop has occurred by providing an implementation.
23893              * @param {Event} e The event object
23894              * @param {String} id The id of the dropped element
23895              * @method afterInvalidDrop
23896              */
23897             this.afterInvalidDrop(e, id);
23898         }
23899     },
23900
23901     // private
23902     afterRepair : function(){
23903         if(Roo.enableFx){
23904             this.el.highlight(this.hlColor || "c3daf9");
23905         }
23906         this.dragging = false;
23907     },
23908
23909     /**
23910      * An empty function by default, but provided so that you can perform a custom action after an invalid
23911      * drop has occurred.
23912      * @param {Roo.dd.DragDrop} target The drop target
23913      * @param {Event} e The event object
23914      * @param {String} id The id of the dragged element
23915      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23916      */
23917     beforeInvalidDrop : function(target, e, id){
23918         return true;
23919     },
23920
23921     // private
23922     handleMouseDown : function(e){
23923         if(this.dragging) {
23924             return;
23925         }
23926         var data = this.getDragData(e);
23927         if(data && this.onBeforeDrag(data, e) !== false){
23928             this.dragData = data;
23929             this.proxy.stop();
23930             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23931         } 
23932     },
23933
23934     /**
23935      * An empty function by default, but provided so that you can perform a custom action before the initial
23936      * drag event begins and optionally cancel it.
23937      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23938      * @param {Event} e The event object
23939      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23940      */
23941     onBeforeDrag : function(data, e){
23942         return true;
23943     },
23944
23945     /**
23946      * An empty function by default, but provided so that you can perform a custom action once the initial
23947      * drag event has begun.  The drag cannot be canceled from this function.
23948      * @param {Number} x The x position of the click on the dragged object
23949      * @param {Number} y The y position of the click on the dragged object
23950      */
23951     onStartDrag : Roo.emptyFn,
23952
23953     // private - YUI override
23954     startDrag : function(x, y){
23955         this.proxy.reset();
23956         this.dragging = true;
23957         this.proxy.update("");
23958         this.onInitDrag(x, y);
23959         this.proxy.show();
23960     },
23961
23962     // private
23963     onInitDrag : function(x, y){
23964         var clone = this.el.dom.cloneNode(true);
23965         clone.id = Roo.id(); // prevent duplicate ids
23966         this.proxy.update(clone);
23967         this.onStartDrag(x, y);
23968         return true;
23969     },
23970
23971     /**
23972      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23973      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23974      */
23975     getProxy : function(){
23976         return this.proxy;  
23977     },
23978
23979     /**
23980      * Hides the drag source's {@link Roo.dd.StatusProxy}
23981      */
23982     hideProxy : function(){
23983         this.proxy.hide();  
23984         this.proxy.reset(true);
23985         this.dragging = false;
23986     },
23987
23988     // private
23989     triggerCacheRefresh : function(){
23990         Roo.dd.DDM.refreshCache(this.groups);
23991     },
23992
23993     // private - override to prevent hiding
23994     b4EndDrag: function(e) {
23995     },
23996
23997     // private - override to prevent moving
23998     endDrag : function(e){
23999         this.onEndDrag(this.dragData, e);
24000     },
24001
24002     // private
24003     onEndDrag : function(data, e){
24004     },
24005     
24006     // private - pin to cursor
24007     autoOffset : function(x, y) {
24008         this.setDelta(-12, -20);
24009     }    
24010 });/*
24011  * Based on:
24012  * Ext JS Library 1.1.1
24013  * Copyright(c) 2006-2007, Ext JS, LLC.
24014  *
24015  * Originally Released Under LGPL - original licence link has changed is not relivant.
24016  *
24017  * Fork - LGPL
24018  * <script type="text/javascript">
24019  */
24020
24021
24022 /**
24023  * @class Roo.dd.DropTarget
24024  * @extends Roo.dd.DDTarget
24025  * A simple class that provides the basic implementation needed to make any element a drop target that can have
24026  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
24027  * @constructor
24028  * @param {String/HTMLElement/Element} el The container element
24029  * @param {Object} config
24030  */
24031 Roo.dd.DropTarget = function(el, config){
24032     this.el = Roo.get(el);
24033     
24034     var listeners = false; ;
24035     if (config && config.listeners) {
24036         listeners= config.listeners;
24037         delete config.listeners;
24038     }
24039     Roo.apply(this, config);
24040     
24041     if(this.containerScroll){
24042         Roo.dd.ScrollManager.register(this.el);
24043     }
24044     this.addEvents( {
24045          /**
24046          * @scope Roo.dd.DropTarget
24047          */
24048          
24049          /**
24050          * @event enter
24051          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24052          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
24053          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
24054          * 
24055          * IMPORTANT : it should set  this.valid to true|false
24056          * 
24057          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24058          * @param {Event} e The event
24059          * @param {Object} data An object containing arbitrary data supplied by the drag source
24060          */
24061         "enter" : true,
24062         
24063          /**
24064          * @event over
24065          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24066          * This method will be called on every mouse movement while the drag source is over the drop target.
24067          * This default implementation simply returns the dropAllowed config value.
24068          * 
24069          * IMPORTANT : it should set  this.valid to true|false
24070          * 
24071          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24072          * @param {Event} e The event
24073          * @param {Object} data An object containing arbitrary data supplied by the drag source
24074          
24075          */
24076         "over" : true,
24077         /**
24078          * @event out
24079          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24080          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
24081          * overClass (if any) from the drop element.
24082          * 
24083          * 
24084          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24085          * @param {Event} e The event
24086          * @param {Object} data An object containing arbitrary data supplied by the drag source
24087          */
24088          "out" : true,
24089          
24090         /**
24091          * @event drop
24092          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24093          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
24094          * implementation that does something to process the drop event and returns true so that the drag source's
24095          * repair action does not run.
24096          * 
24097          * IMPORTANT : it should set this.success
24098          * 
24099          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24100          * @param {Event} e The event
24101          * @param {Object} data An object containing arbitrary data supplied by the drag source
24102         */
24103          "drop" : true
24104     });
24105             
24106      
24107     Roo.dd.DropTarget.superclass.constructor.call(  this, 
24108         this.el.dom, 
24109         this.ddGroup || this.group,
24110         {
24111             isTarget: true,
24112             listeners : listeners || {} 
24113            
24114         
24115         }
24116     );
24117
24118 };
24119
24120 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24121     /**
24122      * @cfg {String} overClass
24123      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24124      */
24125      /**
24126      * @cfg {String} ddGroup
24127      * The drag drop group to handle drop events for
24128      */
24129      
24130     /**
24131      * @cfg {String} dropAllowed
24132      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24133      */
24134     dropAllowed : "x-dd-drop-ok",
24135     /**
24136      * @cfg {String} dropNotAllowed
24137      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24138      */
24139     dropNotAllowed : "x-dd-drop-nodrop",
24140     /**
24141      * @cfg {boolean} success
24142      * set this after drop listener.. 
24143      */
24144     success : false,
24145     /**
24146      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24147      * if the drop point is valid for over/enter..
24148      */
24149     valid : false,
24150     // private
24151     isTarget : true,
24152
24153     // private
24154     isNotifyTarget : true,
24155     
24156     /**
24157      * @hide
24158      */
24159     notifyEnter : function(dd, e, data)
24160     {
24161         this.valid = true;
24162         this.fireEvent('enter', dd, e, data);
24163         if(this.overClass){
24164             this.el.addClass(this.overClass);
24165         }
24166         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24167             this.valid ? this.dropAllowed : this.dropNotAllowed
24168         );
24169     },
24170
24171     /**
24172      * @hide
24173      */
24174     notifyOver : function(dd, e, data)
24175     {
24176         this.valid = true;
24177         this.fireEvent('over', dd, e, data);
24178         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24179             this.valid ? this.dropAllowed : this.dropNotAllowed
24180         );
24181     },
24182
24183     /**
24184      * @hide
24185      */
24186     notifyOut : function(dd, e, data)
24187     {
24188         this.fireEvent('out', dd, e, data);
24189         if(this.overClass){
24190             this.el.removeClass(this.overClass);
24191         }
24192     },
24193
24194     /**
24195      * @hide
24196      */
24197     notifyDrop : function(dd, e, data)
24198     {
24199         this.success = false;
24200         this.fireEvent('drop', dd, e, data);
24201         return this.success;
24202     }
24203 });/*
24204  * Based on:
24205  * Ext JS Library 1.1.1
24206  * Copyright(c) 2006-2007, Ext JS, LLC.
24207  *
24208  * Originally Released Under LGPL - original licence link has changed is not relivant.
24209  *
24210  * Fork - LGPL
24211  * <script type="text/javascript">
24212  */
24213
24214
24215 /**
24216  * @class Roo.dd.DragZone
24217  * @extends Roo.dd.DragSource
24218  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24219  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24220  * @constructor
24221  * @param {String/HTMLElement/Element} el The container element
24222  * @param {Object} config
24223  */
24224 Roo.dd.DragZone = function(el, config){
24225     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24226     if(this.containerScroll){
24227         Roo.dd.ScrollManager.register(this.el);
24228     }
24229 };
24230
24231 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24232     /**
24233      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24234      * for auto scrolling during drag operations.
24235      */
24236     /**
24237      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24238      * method after a failed drop (defaults to "c3daf9" - light blue)
24239      */
24240
24241     /**
24242      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24243      * for a valid target to drag based on the mouse down. Override this method
24244      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24245      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24246      * @param {EventObject} e The mouse down event
24247      * @return {Object} The dragData
24248      */
24249     getDragData : function(e){
24250         return Roo.dd.Registry.getHandleFromEvent(e);
24251     },
24252     
24253     /**
24254      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24255      * this.dragData.ddel
24256      * @param {Number} x The x position of the click on the dragged object
24257      * @param {Number} y The y position of the click on the dragged object
24258      * @return {Boolean} true to continue the drag, false to cancel
24259      */
24260     onInitDrag : function(x, y){
24261         this.proxy.update(this.dragData.ddel.cloneNode(true));
24262         this.onStartDrag(x, y);
24263         return true;
24264     },
24265     
24266     /**
24267      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24268      */
24269     afterRepair : function(){
24270         if(Roo.enableFx){
24271             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24272         }
24273         this.dragging = false;
24274     },
24275
24276     /**
24277      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24278      * the XY of this.dragData.ddel
24279      * @param {EventObject} e The mouse up event
24280      * @return {Array} The xy location (e.g. [100, 200])
24281      */
24282     getRepairXY : function(e){
24283         return Roo.Element.fly(this.dragData.ddel).getXY();  
24284     }
24285 });/*
24286  * Based on:
24287  * Ext JS Library 1.1.1
24288  * Copyright(c) 2006-2007, Ext JS, LLC.
24289  *
24290  * Originally Released Under LGPL - original licence link has changed is not relivant.
24291  *
24292  * Fork - LGPL
24293  * <script type="text/javascript">
24294  */
24295 /**
24296  * @class Roo.dd.DropZone
24297  * @extends Roo.dd.DropTarget
24298  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24299  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24300  * @constructor
24301  * @param {String/HTMLElement/Element} el The container element
24302  * @param {Object} config
24303  */
24304 Roo.dd.DropZone = function(el, config){
24305     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24306 };
24307
24308 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24309     /**
24310      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24311      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24312      * provide your own custom lookup.
24313      * @param {Event} e The event
24314      * @return {Object} data The custom data
24315      */
24316     getTargetFromEvent : function(e){
24317         return Roo.dd.Registry.getTargetFromEvent(e);
24318     },
24319
24320     /**
24321      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24322      * that it has registered.  This method has no default implementation and should be overridden to provide
24323      * node-specific processing if necessary.
24324      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24325      * {@link #getTargetFromEvent} for this node)
24326      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24327      * @param {Event} e The event
24328      * @param {Object} data An object containing arbitrary data supplied by the drag source
24329      */
24330     onNodeEnter : function(n, dd, e, data){
24331         
24332     },
24333
24334     /**
24335      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24336      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24337      * overridden to provide the proper feedback.
24338      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24339      * {@link #getTargetFromEvent} for this node)
24340      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24341      * @param {Event} e The event
24342      * @param {Object} data An object containing arbitrary data supplied by the drag source
24343      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24344      * underlying {@link Roo.dd.StatusProxy} can be updated
24345      */
24346     onNodeOver : function(n, dd, e, data){
24347         return this.dropAllowed;
24348     },
24349
24350     /**
24351      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24352      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24353      * node-specific processing if necessary.
24354      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24355      * {@link #getTargetFromEvent} for this node)
24356      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24357      * @param {Event} e The event
24358      * @param {Object} data An object containing arbitrary data supplied by the drag source
24359      */
24360     onNodeOut : function(n, dd, e, data){
24361         
24362     },
24363
24364     /**
24365      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24366      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24367      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24368      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24369      * {@link #getTargetFromEvent} for this node)
24370      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24371      * @param {Event} e The event
24372      * @param {Object} data An object containing arbitrary data supplied by the drag source
24373      * @return {Boolean} True if the drop was valid, else false
24374      */
24375     onNodeDrop : function(n, dd, e, data){
24376         return false;
24377     },
24378
24379     /**
24380      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24381      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24382      * it should be overridden to provide the proper feedback if necessary.
24383      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24384      * @param {Event} e The event
24385      * @param {Object} data An object containing arbitrary data supplied by the drag source
24386      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24387      * underlying {@link Roo.dd.StatusProxy} can be updated
24388      */
24389     onContainerOver : function(dd, e, data){
24390         return this.dropNotAllowed;
24391     },
24392
24393     /**
24394      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24395      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24396      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24397      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24398      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24399      * @param {Event} e The event
24400      * @param {Object} data An object containing arbitrary data supplied by the drag source
24401      * @return {Boolean} True if the drop was valid, else false
24402      */
24403     onContainerDrop : function(dd, e, data){
24404         return false;
24405     },
24406
24407     /**
24408      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24409      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24410      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24411      * you should override this method and provide a custom implementation.
24412      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24413      * @param {Event} e The event
24414      * @param {Object} data An object containing arbitrary data supplied by the drag source
24415      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24416      * underlying {@link Roo.dd.StatusProxy} can be updated
24417      */
24418     notifyEnter : function(dd, e, data){
24419         return this.dropNotAllowed;
24420     },
24421
24422     /**
24423      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24424      * This method will be called on every mouse movement while the drag source is over the drop zone.
24425      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24426      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24427      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24428      * registered node, it will call {@link #onContainerOver}.
24429      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24430      * @param {Event} e The event
24431      * @param {Object} data An object containing arbitrary data supplied by the drag source
24432      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24433      * underlying {@link Roo.dd.StatusProxy} can be updated
24434      */
24435     notifyOver : function(dd, e, data){
24436         var n = this.getTargetFromEvent(e);
24437         if(!n){ // not over valid drop target
24438             if(this.lastOverNode){
24439                 this.onNodeOut(this.lastOverNode, dd, e, data);
24440                 this.lastOverNode = null;
24441             }
24442             return this.onContainerOver(dd, e, data);
24443         }
24444         if(this.lastOverNode != n){
24445             if(this.lastOverNode){
24446                 this.onNodeOut(this.lastOverNode, dd, e, data);
24447             }
24448             this.onNodeEnter(n, dd, e, data);
24449             this.lastOverNode = n;
24450         }
24451         return this.onNodeOver(n, dd, e, data);
24452     },
24453
24454     /**
24455      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24456      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24457      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24458      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24459      * @param {Event} e The event
24460      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24461      */
24462     notifyOut : function(dd, e, data){
24463         if(this.lastOverNode){
24464             this.onNodeOut(this.lastOverNode, dd, e, data);
24465             this.lastOverNode = null;
24466         }
24467     },
24468
24469     /**
24470      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24471      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24472      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24473      * otherwise it will call {@link #onContainerDrop}.
24474      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24475      * @param {Event} e The event
24476      * @param {Object} data An object containing arbitrary data supplied by the drag source
24477      * @return {Boolean} True if the drop was valid, else false
24478      */
24479     notifyDrop : function(dd, e, data){
24480         if(this.lastOverNode){
24481             this.onNodeOut(this.lastOverNode, dd, e, data);
24482             this.lastOverNode = null;
24483         }
24484         var n = this.getTargetFromEvent(e);
24485         return n ?
24486             this.onNodeDrop(n, dd, e, data) :
24487             this.onContainerDrop(dd, e, data);
24488     },
24489
24490     // private
24491     triggerCacheRefresh : function(){
24492         Roo.dd.DDM.refreshCache(this.groups);
24493     }  
24494 });/*
24495  * Based on:
24496  * Ext JS Library 1.1.1
24497  * Copyright(c) 2006-2007, Ext JS, LLC.
24498  *
24499  * Originally Released Under LGPL - original licence link has changed is not relivant.
24500  *
24501  * Fork - LGPL
24502  * <script type="text/javascript">
24503  */
24504
24505
24506 /**
24507  * @class Roo.data.SortTypes
24508  * @static
24509  * Defines the default sorting (casting?) comparison functions used when sorting data.
24510  */
24511 Roo.data.SortTypes = {
24512     /**
24513      * Default sort that does nothing
24514      * @param {Mixed} s The value being converted
24515      * @return {Mixed} The comparison value
24516      */
24517     none : function(s){
24518         return s;
24519     },
24520     
24521     /**
24522      * The regular expression used to strip tags
24523      * @type {RegExp}
24524      * @property
24525      */
24526     stripTagsRE : /<\/?[^>]+>/gi,
24527     
24528     /**
24529      * Strips all HTML tags to sort on text only
24530      * @param {Mixed} s The value being converted
24531      * @return {String} The comparison value
24532      */
24533     asText : function(s){
24534         return String(s).replace(this.stripTagsRE, "");
24535     },
24536     
24537     /**
24538      * Strips all HTML tags to sort on text only - Case insensitive
24539      * @param {Mixed} s The value being converted
24540      * @return {String} The comparison value
24541      */
24542     asUCText : function(s){
24543         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24544     },
24545     
24546     /**
24547      * Case insensitive string
24548      * @param {Mixed} s The value being converted
24549      * @return {String} The comparison value
24550      */
24551     asUCString : function(s) {
24552         return String(s).toUpperCase();
24553     },
24554     
24555     /**
24556      * Date sorting
24557      * @param {Mixed} s The value being converted
24558      * @return {Number} The comparison value
24559      */
24560     asDate : function(s) {
24561         if(!s){
24562             return 0;
24563         }
24564         if(s instanceof Date){
24565             return s.getTime();
24566         }
24567         return Date.parse(String(s));
24568     },
24569     
24570     /**
24571      * Float sorting
24572      * @param {Mixed} s The value being converted
24573      * @return {Float} The comparison value
24574      */
24575     asFloat : function(s) {
24576         var val = parseFloat(String(s).replace(/,/g, ""));
24577         if(isNaN(val)) {
24578             val = 0;
24579         }
24580         return val;
24581     },
24582     
24583     /**
24584      * Integer sorting
24585      * @param {Mixed} s The value being converted
24586      * @return {Number} The comparison value
24587      */
24588     asInt : function(s) {
24589         var val = parseInt(String(s).replace(/,/g, ""));
24590         if(isNaN(val)) {
24591             val = 0;
24592         }
24593         return val;
24594     }
24595 };/*
24596  * Based on:
24597  * Ext JS Library 1.1.1
24598  * Copyright(c) 2006-2007, Ext JS, LLC.
24599  *
24600  * Originally Released Under LGPL - original licence link has changed is not relivant.
24601  *
24602  * Fork - LGPL
24603  * <script type="text/javascript">
24604  */
24605
24606 /**
24607 * @class Roo.data.Record
24608  * Instances of this class encapsulate both record <em>definition</em> information, and record
24609  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24610  * to access Records cached in an {@link Roo.data.Store} object.<br>
24611  * <p>
24612  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24613  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24614  * objects.<br>
24615  * <p>
24616  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24617  * @constructor
24618  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24619  * {@link #create}. The parameters are the same.
24620  * @param {Array} data An associative Array of data values keyed by the field name.
24621  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24622  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24623  * not specified an integer id is generated.
24624  */
24625 Roo.data.Record = function(data, id){
24626     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24627     this.data = data;
24628 };
24629
24630 /**
24631  * Generate a constructor for a specific record layout.
24632  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24633  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24634  * Each field definition object may contain the following properties: <ul>
24635  * <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,
24636  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24637  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24638  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24639  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24640  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24641  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24642  * this may be omitted.</p></li>
24643  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24644  * <ul><li>auto (Default, implies no conversion)</li>
24645  * <li>string</li>
24646  * <li>int</li>
24647  * <li>float</li>
24648  * <li>boolean</li>
24649  * <li>date</li></ul></p></li>
24650  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24651  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24652  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24653  * by the Reader into an object that will be stored in the Record. It is passed the
24654  * following parameters:<ul>
24655  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24656  * </ul></p></li>
24657  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24658  * </ul>
24659  * <br>usage:<br><pre><code>
24660 var TopicRecord = Roo.data.Record.create(
24661     {name: 'title', mapping: 'topic_title'},
24662     {name: 'author', mapping: 'username'},
24663     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24664     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24665     {name: 'lastPoster', mapping: 'user2'},
24666     {name: 'excerpt', mapping: 'post_text'}
24667 );
24668
24669 var myNewRecord = new TopicRecord({
24670     title: 'Do my job please',
24671     author: 'noobie',
24672     totalPosts: 1,
24673     lastPost: new Date(),
24674     lastPoster: 'Animal',
24675     excerpt: 'No way dude!'
24676 });
24677 myStore.add(myNewRecord);
24678 </code></pre>
24679  * @method create
24680  * @static
24681  */
24682 Roo.data.Record.create = function(o){
24683     var f = function(){
24684         f.superclass.constructor.apply(this, arguments);
24685     };
24686     Roo.extend(f, Roo.data.Record);
24687     var p = f.prototype;
24688     p.fields = new Roo.util.MixedCollection(false, function(field){
24689         return field.name;
24690     });
24691     for(var i = 0, len = o.length; i < len; i++){
24692         p.fields.add(new Roo.data.Field(o[i]));
24693     }
24694     f.getField = function(name){
24695         return p.fields.get(name);  
24696     };
24697     return f;
24698 };
24699
24700 Roo.data.Record.AUTO_ID = 1000;
24701 Roo.data.Record.EDIT = 'edit';
24702 Roo.data.Record.REJECT = 'reject';
24703 Roo.data.Record.COMMIT = 'commit';
24704
24705 Roo.data.Record.prototype = {
24706     /**
24707      * Readonly flag - true if this record has been modified.
24708      * @type Boolean
24709      */
24710     dirty : false,
24711     editing : false,
24712     error: null,
24713     modified: null,
24714
24715     // private
24716     join : function(store){
24717         this.store = store;
24718     },
24719
24720     /**
24721      * Set the named field to the specified value.
24722      * @param {String} name The name of the field to set.
24723      * @param {Object} value The value to set the field to.
24724      */
24725     set : function(name, value){
24726         if(this.data[name] == value){
24727             return;
24728         }
24729         this.dirty = true;
24730         if(!this.modified){
24731             this.modified = {};
24732         }
24733         if(typeof this.modified[name] == 'undefined'){
24734             this.modified[name] = this.data[name];
24735         }
24736         this.data[name] = value;
24737         if(!this.editing && this.store){
24738             this.store.afterEdit(this);
24739         }       
24740     },
24741
24742     /**
24743      * Get the value of the named field.
24744      * @param {String} name The name of the field to get the value of.
24745      * @return {Object} The value of the field.
24746      */
24747     get : function(name){
24748         return this.data[name]; 
24749     },
24750
24751     // private
24752     beginEdit : function(){
24753         this.editing = true;
24754         this.modified = {}; 
24755     },
24756
24757     // private
24758     cancelEdit : function(){
24759         this.editing = false;
24760         delete this.modified;
24761     },
24762
24763     // private
24764     endEdit : function(){
24765         this.editing = false;
24766         if(this.dirty && this.store){
24767             this.store.afterEdit(this);
24768         }
24769     },
24770
24771     /**
24772      * Usually called by the {@link Roo.data.Store} which owns the Record.
24773      * Rejects all changes made to the Record since either creation, or the last commit operation.
24774      * Modified fields are reverted to their original values.
24775      * <p>
24776      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24777      * of reject operations.
24778      */
24779     reject : function(){
24780         var m = this.modified;
24781         for(var n in m){
24782             if(typeof m[n] != "function"){
24783                 this.data[n] = m[n];
24784             }
24785         }
24786         this.dirty = false;
24787         delete this.modified;
24788         this.editing = false;
24789         if(this.store){
24790             this.store.afterReject(this);
24791         }
24792     },
24793
24794     /**
24795      * Usually called by the {@link Roo.data.Store} which owns the Record.
24796      * Commits all changes made to the Record since either creation, or the last commit operation.
24797      * <p>
24798      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24799      * of commit operations.
24800      */
24801     commit : function(){
24802         this.dirty = false;
24803         delete this.modified;
24804         this.editing = false;
24805         if(this.store){
24806             this.store.afterCommit(this);
24807         }
24808     },
24809
24810     // private
24811     hasError : function(){
24812         return this.error != null;
24813     },
24814
24815     // private
24816     clearError : function(){
24817         this.error = null;
24818     },
24819
24820     /**
24821      * Creates a copy of this record.
24822      * @param {String} id (optional) A new record id if you don't want to use this record's id
24823      * @return {Record}
24824      */
24825     copy : function(newId) {
24826         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24827     }
24828 };/*
24829  * Based on:
24830  * Ext JS Library 1.1.1
24831  * Copyright(c) 2006-2007, Ext JS, LLC.
24832  *
24833  * Originally Released Under LGPL - original licence link has changed is not relivant.
24834  *
24835  * Fork - LGPL
24836  * <script type="text/javascript">
24837  */
24838
24839
24840
24841 /**
24842  * @class Roo.data.Store
24843  * @extends Roo.util.Observable
24844  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24845  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24846  * <p>
24847  * 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
24848  * has no knowledge of the format of the data returned by the Proxy.<br>
24849  * <p>
24850  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24851  * instances from the data object. These records are cached and made available through accessor functions.
24852  * @constructor
24853  * Creates a new Store.
24854  * @param {Object} config A config object containing the objects needed for the Store to access data,
24855  * and read the data into Records.
24856  */
24857 Roo.data.Store = function(config){
24858     this.data = new Roo.util.MixedCollection(false);
24859     this.data.getKey = function(o){
24860         return o.id;
24861     };
24862     this.baseParams = {};
24863     // private
24864     this.paramNames = {
24865         "start" : "start",
24866         "limit" : "limit",
24867         "sort" : "sort",
24868         "dir" : "dir",
24869         "multisort" : "_multisort"
24870     };
24871
24872     if(config && config.data){
24873         this.inlineData = config.data;
24874         delete config.data;
24875     }
24876
24877     Roo.apply(this, config);
24878     
24879     if(this.reader){ // reader passed
24880         this.reader = Roo.factory(this.reader, Roo.data);
24881         this.reader.xmodule = this.xmodule || false;
24882         if(!this.recordType){
24883             this.recordType = this.reader.recordType;
24884         }
24885         if(this.reader.onMetaChange){
24886             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24887         }
24888     }
24889
24890     if(this.recordType){
24891         this.fields = this.recordType.prototype.fields;
24892     }
24893     this.modified = [];
24894
24895     this.addEvents({
24896         /**
24897          * @event datachanged
24898          * Fires when the data cache has changed, and a widget which is using this Store
24899          * as a Record cache should refresh its view.
24900          * @param {Store} this
24901          */
24902         datachanged : true,
24903         /**
24904          * @event metachange
24905          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24906          * @param {Store} this
24907          * @param {Object} meta The JSON metadata
24908          */
24909         metachange : true,
24910         /**
24911          * @event add
24912          * Fires when Records have been added to the Store
24913          * @param {Store} this
24914          * @param {Roo.data.Record[]} records The array of Records added
24915          * @param {Number} index The index at which the record(s) were added
24916          */
24917         add : true,
24918         /**
24919          * @event remove
24920          * Fires when a Record has been removed from the Store
24921          * @param {Store} this
24922          * @param {Roo.data.Record} record The Record that was removed
24923          * @param {Number} index The index at which the record was removed
24924          */
24925         remove : true,
24926         /**
24927          * @event update
24928          * Fires when a Record has been updated
24929          * @param {Store} this
24930          * @param {Roo.data.Record} record The Record that was updated
24931          * @param {String} operation The update operation being performed.  Value may be one of:
24932          * <pre><code>
24933  Roo.data.Record.EDIT
24934  Roo.data.Record.REJECT
24935  Roo.data.Record.COMMIT
24936          * </code></pre>
24937          */
24938         update : true,
24939         /**
24940          * @event clear
24941          * Fires when the data cache has been cleared.
24942          * @param {Store} this
24943          */
24944         clear : true,
24945         /**
24946          * @event beforeload
24947          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24948          * the load action will be canceled.
24949          * @param {Store} this
24950          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24951          */
24952         beforeload : true,
24953         /**
24954          * @event beforeloadadd
24955          * Fires after a new set of Records has been loaded.
24956          * @param {Store} this
24957          * @param {Roo.data.Record[]} records The Records that were loaded
24958          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24959          */
24960         beforeloadadd : true,
24961         /**
24962          * @event load
24963          * Fires after a new set of Records has been loaded, before they are added to the store.
24964          * @param {Store} this
24965          * @param {Roo.data.Record[]} records The Records that were loaded
24966          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24967          * @params {Object} return from reader
24968          */
24969         load : true,
24970         /**
24971          * @event loadexception
24972          * Fires if an exception occurs in the Proxy during loading.
24973          * Called with the signature of the Proxy's "loadexception" event.
24974          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24975          * 
24976          * @param {Proxy} 
24977          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24978          * @param {Object} load options 
24979          * @param {Object} jsonData from your request (normally this contains the Exception)
24980          */
24981         loadexception : true
24982     });
24983     
24984     if(this.proxy){
24985         this.proxy = Roo.factory(this.proxy, Roo.data);
24986         this.proxy.xmodule = this.xmodule || false;
24987         this.relayEvents(this.proxy,  ["loadexception"]);
24988     }
24989     this.sortToggle = {};
24990     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24991
24992     Roo.data.Store.superclass.constructor.call(this);
24993
24994     if(this.inlineData){
24995         this.loadData(this.inlineData);
24996         delete this.inlineData;
24997     }
24998 };
24999
25000 Roo.extend(Roo.data.Store, Roo.util.Observable, {
25001      /**
25002     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
25003     * without a remote query - used by combo/forms at present.
25004     */
25005     
25006     /**
25007     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
25008     */
25009     /**
25010     * @cfg {Array} data Inline data to be loaded when the store is initialized.
25011     */
25012     /**
25013     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
25014     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
25015     */
25016     /**
25017     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
25018     * on any HTTP request
25019     */
25020     /**
25021     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
25022     */
25023     /**
25024     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25025     */
25026     multiSort: false,
25027     /**
25028     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25029     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25030     */
25031     remoteSort : false,
25032
25033     /**
25034     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25035      * loaded or when a record is removed. (defaults to false).
25036     */
25037     pruneModifiedRecords : false,
25038
25039     // private
25040     lastOptions : null,
25041
25042     /**
25043      * Add Records to the Store and fires the add event.
25044      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25045      */
25046     add : function(records){
25047         records = [].concat(records);
25048         for(var i = 0, len = records.length; i < len; i++){
25049             records[i].join(this);
25050         }
25051         var index = this.data.length;
25052         this.data.addAll(records);
25053         this.fireEvent("add", this, records, index);
25054     },
25055
25056     /**
25057      * Remove a Record from the Store and fires the remove event.
25058      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25059      */
25060     remove : function(record){
25061         var index = this.data.indexOf(record);
25062         this.data.removeAt(index);
25063  
25064         if(this.pruneModifiedRecords){
25065             this.modified.remove(record);
25066         }
25067         this.fireEvent("remove", this, record, index);
25068     },
25069
25070     /**
25071      * Remove all Records from the Store and fires the clear event.
25072      */
25073     removeAll : function(){
25074         this.data.clear();
25075         if(this.pruneModifiedRecords){
25076             this.modified = [];
25077         }
25078         this.fireEvent("clear", this);
25079     },
25080
25081     /**
25082      * Inserts Records to the Store at the given index and fires the add event.
25083      * @param {Number} index The start index at which to insert the passed Records.
25084      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25085      */
25086     insert : function(index, records){
25087         records = [].concat(records);
25088         for(var i = 0, len = records.length; i < len; i++){
25089             this.data.insert(index, records[i]);
25090             records[i].join(this);
25091         }
25092         this.fireEvent("add", this, records, index);
25093     },
25094
25095     /**
25096      * Get the index within the cache of the passed Record.
25097      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25098      * @return {Number} The index of the passed Record. Returns -1 if not found.
25099      */
25100     indexOf : function(record){
25101         return this.data.indexOf(record);
25102     },
25103
25104     /**
25105      * Get the index within the cache of the Record with the passed id.
25106      * @param {String} id The id of the Record to find.
25107      * @return {Number} The index of the Record. Returns -1 if not found.
25108      */
25109     indexOfId : function(id){
25110         return this.data.indexOfKey(id);
25111     },
25112
25113     /**
25114      * Get the Record with the specified id.
25115      * @param {String} id The id of the Record to find.
25116      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25117      */
25118     getById : function(id){
25119         return this.data.key(id);
25120     },
25121
25122     /**
25123      * Get the Record at the specified index.
25124      * @param {Number} index The index of the Record to find.
25125      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25126      */
25127     getAt : function(index){
25128         return this.data.itemAt(index);
25129     },
25130
25131     /**
25132      * Returns a range of Records between specified indices.
25133      * @param {Number} startIndex (optional) The starting index (defaults to 0)
25134      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25135      * @return {Roo.data.Record[]} An array of Records
25136      */
25137     getRange : function(start, end){
25138         return this.data.getRange(start, end);
25139     },
25140
25141     // private
25142     storeOptions : function(o){
25143         o = Roo.apply({}, o);
25144         delete o.callback;
25145         delete o.scope;
25146         this.lastOptions = o;
25147     },
25148
25149     /**
25150      * Loads the Record cache from the configured Proxy using the configured Reader.
25151      * <p>
25152      * If using remote paging, then the first load call must specify the <em>start</em>
25153      * and <em>limit</em> properties in the options.params property to establish the initial
25154      * position within the dataset, and the number of Records to cache on each read from the Proxy.
25155      * <p>
25156      * <strong>It is important to note that for remote data sources, loading is asynchronous,
25157      * and this call will return before the new data has been loaded. Perform any post-processing
25158      * in a callback function, or in a "load" event handler.</strong>
25159      * <p>
25160      * @param {Object} options An object containing properties which control loading options:<ul>
25161      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25162      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25163      * <pre>
25164                 {
25165                     data : data,  // array of key=>value data like JsonReader
25166                     total : data.length,
25167                     success : true
25168                     
25169                 }
25170         </pre>
25171             }.</li>
25172      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25173      * passed the following arguments:<ul>
25174      * <li>r : Roo.data.Record[]</li>
25175      * <li>options: Options object from the load call</li>
25176      * <li>success: Boolean success indicator</li></ul></li>
25177      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25178      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25179      * </ul>
25180      */
25181     load : function(options){
25182         options = options || {};
25183         if(this.fireEvent("beforeload", this, options) !== false){
25184             this.storeOptions(options);
25185             var p = Roo.apply(options.params || {}, this.baseParams);
25186             // if meta was not loaded from remote source.. try requesting it.
25187             if (!this.reader.metaFromRemote) {
25188                 p._requestMeta = 1;
25189             }
25190             if(this.sortInfo && this.remoteSort){
25191                 var pn = this.paramNames;
25192                 p[pn["sort"]] = this.sortInfo.field;
25193                 p[pn["dir"]] = this.sortInfo.direction;
25194             }
25195             if (this.multiSort) {
25196                 var pn = this.paramNames;
25197                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25198             }
25199             
25200             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25201         }
25202     },
25203
25204     /**
25205      * Reloads the Record cache from the configured Proxy using the configured Reader and
25206      * the options from the last load operation performed.
25207      * @param {Object} options (optional) An object containing properties which may override the options
25208      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25209      * the most recently used options are reused).
25210      */
25211     reload : function(options){
25212         this.load(Roo.applyIf(options||{}, this.lastOptions));
25213     },
25214
25215     // private
25216     // Called as a callback by the Reader during a load operation.
25217     loadRecords : function(o, options, success){
25218          
25219         if(!o){
25220             if(success !== false){
25221                 this.fireEvent("load", this, [], options, o);
25222             }
25223             if(options.callback){
25224                 options.callback.call(options.scope || this, [], options, false);
25225             }
25226             return;
25227         }
25228         // if data returned failure - throw an exception.
25229         if (o.success === false) {
25230             // show a message if no listener is registered.
25231             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25232                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25233             }
25234             // loadmask wil be hooked into this..
25235             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25236             return;
25237         }
25238         var r = o.records, t = o.totalRecords || r.length;
25239         
25240         this.fireEvent("beforeloadadd", this, r, options, o);
25241         
25242         if(!options || options.add !== true){
25243             if(this.pruneModifiedRecords){
25244                 this.modified = [];
25245             }
25246             for(var i = 0, len = r.length; i < len; i++){
25247                 r[i].join(this);
25248             }
25249             if(this.snapshot){
25250                 this.data = this.snapshot;
25251                 delete this.snapshot;
25252             }
25253             this.data.clear();
25254             this.data.addAll(r);
25255             this.totalLength = t;
25256             this.applySort();
25257             this.fireEvent("datachanged", this);
25258         }else{
25259             this.totalLength = Math.max(t, this.data.length+r.length);
25260             this.add(r);
25261         }
25262         
25263         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25264                 
25265             var e = new Roo.data.Record({});
25266
25267             e.set(this.parent.displayField, this.parent.emptyTitle);
25268             e.set(this.parent.valueField, '');
25269
25270             this.insert(0, e);
25271         }
25272             
25273         this.fireEvent("load", this, r, options, o);
25274         if(options.callback){
25275             options.callback.call(options.scope || this, r, options, true);
25276         }
25277     },
25278
25279
25280     /**
25281      * Loads data from a passed data block. A Reader which understands the format of the data
25282      * must have been configured in the constructor.
25283      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25284      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25285      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25286      */
25287     loadData : function(o, append){
25288         var r = this.reader.readRecords(o);
25289         this.loadRecords(r, {add: append}, true);
25290     },
25291     
25292      /**
25293      * using 'cn' the nested child reader read the child array into it's child stores.
25294      * @param {Object} rec The record with a 'children array
25295      */
25296     loadDataFromChildren : function(rec)
25297     {
25298         this.loadData(this.reader.toLoadData(rec));
25299     },
25300     
25301
25302     /**
25303      * Gets the number of cached records.
25304      * <p>
25305      * <em>If using paging, this may not be the total size of the dataset. If the data object
25306      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25307      * the data set size</em>
25308      */
25309     getCount : function(){
25310         return this.data.length || 0;
25311     },
25312
25313     /**
25314      * Gets the total number of records in the dataset as returned by the server.
25315      * <p>
25316      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25317      * the dataset size</em>
25318      */
25319     getTotalCount : function(){
25320         return this.totalLength || 0;
25321     },
25322
25323     /**
25324      * Returns the sort state of the Store as an object with two properties:
25325      * <pre><code>
25326  field {String} The name of the field by which the Records are sorted
25327  direction {String} The sort order, "ASC" or "DESC"
25328      * </code></pre>
25329      */
25330     getSortState : function(){
25331         return this.sortInfo;
25332     },
25333
25334     // private
25335     applySort : function(){
25336         if(this.sortInfo && !this.remoteSort){
25337             var s = this.sortInfo, f = s.field;
25338             var st = this.fields.get(f).sortType;
25339             var fn = function(r1, r2){
25340                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25341                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25342             };
25343             this.data.sort(s.direction, fn);
25344             if(this.snapshot && this.snapshot != this.data){
25345                 this.snapshot.sort(s.direction, fn);
25346             }
25347         }
25348     },
25349
25350     /**
25351      * Sets the default sort column and order to be used by the next load operation.
25352      * @param {String} fieldName The name of the field to sort by.
25353      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25354      */
25355     setDefaultSort : function(field, dir){
25356         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25357     },
25358
25359     /**
25360      * Sort the Records.
25361      * If remote sorting is used, the sort is performed on the server, and the cache is
25362      * reloaded. If local sorting is used, the cache is sorted internally.
25363      * @param {String} fieldName The name of the field to sort by.
25364      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25365      */
25366     sort : function(fieldName, dir){
25367         var f = this.fields.get(fieldName);
25368         if(!dir){
25369             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25370             
25371             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25372                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25373             }else{
25374                 dir = f.sortDir;
25375             }
25376         }
25377         this.sortToggle[f.name] = dir;
25378         this.sortInfo = {field: f.name, direction: dir};
25379         if(!this.remoteSort){
25380             this.applySort();
25381             this.fireEvent("datachanged", this);
25382         }else{
25383             this.load(this.lastOptions);
25384         }
25385     },
25386
25387     /**
25388      * Calls the specified function for each of the Records in the cache.
25389      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25390      * Returning <em>false</em> aborts and exits the iteration.
25391      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25392      */
25393     each : function(fn, scope){
25394         this.data.each(fn, scope);
25395     },
25396
25397     /**
25398      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25399      * (e.g., during paging).
25400      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25401      */
25402     getModifiedRecords : function(){
25403         return this.modified;
25404     },
25405
25406     // private
25407     createFilterFn : function(property, value, anyMatch){
25408         if(!value.exec){ // not a regex
25409             value = String(value);
25410             if(value.length == 0){
25411                 return false;
25412             }
25413             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25414         }
25415         return function(r){
25416             return value.test(r.data[property]);
25417         };
25418     },
25419
25420     /**
25421      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25422      * @param {String} property A field on your records
25423      * @param {Number} start The record index to start at (defaults to 0)
25424      * @param {Number} end The last record index to include (defaults to length - 1)
25425      * @return {Number} The sum
25426      */
25427     sum : function(property, start, end){
25428         var rs = this.data.items, v = 0;
25429         start = start || 0;
25430         end = (end || end === 0) ? end : rs.length-1;
25431
25432         for(var i = start; i <= end; i++){
25433             v += (rs[i].data[property] || 0);
25434         }
25435         return v;
25436     },
25437
25438     /**
25439      * Filter the records by a specified property.
25440      * @param {String} field A field on your records
25441      * @param {String/RegExp} value Either a string that the field
25442      * should start with or a RegExp to test against the field
25443      * @param {Boolean} anyMatch True to match any part not just the beginning
25444      */
25445     filter : function(property, value, anyMatch){
25446         var fn = this.createFilterFn(property, value, anyMatch);
25447         return fn ? this.filterBy(fn) : this.clearFilter();
25448     },
25449
25450     /**
25451      * Filter by a function. The specified function will be called with each
25452      * record in this data source. If the function returns true the record is included,
25453      * otherwise it is filtered.
25454      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25455      * @param {Object} scope (optional) The scope of the function (defaults to this)
25456      */
25457     filterBy : function(fn, scope){
25458         this.snapshot = this.snapshot || this.data;
25459         this.data = this.queryBy(fn, scope||this);
25460         this.fireEvent("datachanged", this);
25461     },
25462
25463     /**
25464      * Query the records by a specified property.
25465      * @param {String} field A field on your records
25466      * @param {String/RegExp} value Either a string that the field
25467      * should start with or a RegExp to test against the field
25468      * @param {Boolean} anyMatch True to match any part not just the beginning
25469      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25470      */
25471     query : function(property, value, anyMatch){
25472         var fn = this.createFilterFn(property, value, anyMatch);
25473         return fn ? this.queryBy(fn) : this.data.clone();
25474     },
25475
25476     /**
25477      * Query by a function. The specified function will be called with each
25478      * record in this data source. If the function returns true the record is included
25479      * in the results.
25480      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25481      * @param {Object} scope (optional) The scope of the function (defaults to this)
25482       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25483      **/
25484     queryBy : function(fn, scope){
25485         var data = this.snapshot || this.data;
25486         return data.filterBy(fn, scope||this);
25487     },
25488
25489     /**
25490      * Collects unique values for a particular dataIndex from this store.
25491      * @param {String} dataIndex The property to collect
25492      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25493      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25494      * @return {Array} An array of the unique values
25495      **/
25496     collect : function(dataIndex, allowNull, bypassFilter){
25497         var d = (bypassFilter === true && this.snapshot) ?
25498                 this.snapshot.items : this.data.items;
25499         var v, sv, r = [], l = {};
25500         for(var i = 0, len = d.length; i < len; i++){
25501             v = d[i].data[dataIndex];
25502             sv = String(v);
25503             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25504                 l[sv] = true;
25505                 r[r.length] = v;
25506             }
25507         }
25508         return r;
25509     },
25510
25511     /**
25512      * Revert to a view of the Record cache with no filtering applied.
25513      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25514      */
25515     clearFilter : function(suppressEvent){
25516         if(this.snapshot && this.snapshot != this.data){
25517             this.data = this.snapshot;
25518             delete this.snapshot;
25519             if(suppressEvent !== true){
25520                 this.fireEvent("datachanged", this);
25521             }
25522         }
25523     },
25524
25525     // private
25526     afterEdit : function(record){
25527         if(this.modified.indexOf(record) == -1){
25528             this.modified.push(record);
25529         }
25530         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25531     },
25532     
25533     // private
25534     afterReject : function(record){
25535         this.modified.remove(record);
25536         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25537     },
25538
25539     // private
25540     afterCommit : function(record){
25541         this.modified.remove(record);
25542         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25543     },
25544
25545     /**
25546      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25547      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25548      */
25549     commitChanges : function(){
25550         var m = this.modified.slice(0);
25551         this.modified = [];
25552         for(var i = 0, len = m.length; i < len; i++){
25553             m[i].commit();
25554         }
25555     },
25556
25557     /**
25558      * Cancel outstanding changes on all changed records.
25559      */
25560     rejectChanges : function(){
25561         var m = this.modified.slice(0);
25562         this.modified = [];
25563         for(var i = 0, len = m.length; i < len; i++){
25564             m[i].reject();
25565         }
25566     },
25567
25568     onMetaChange : function(meta, rtype, o){
25569         this.recordType = rtype;
25570         this.fields = rtype.prototype.fields;
25571         delete this.snapshot;
25572         this.sortInfo = meta.sortInfo || this.sortInfo;
25573         this.modified = [];
25574         this.fireEvent('metachange', this, this.reader.meta);
25575     },
25576     
25577     moveIndex : function(data, type)
25578     {
25579         var index = this.indexOf(data);
25580         
25581         var newIndex = index + type;
25582         
25583         this.remove(data);
25584         
25585         this.insert(newIndex, data);
25586         
25587     }
25588 });/*
25589  * Based on:
25590  * Ext JS Library 1.1.1
25591  * Copyright(c) 2006-2007, Ext JS, LLC.
25592  *
25593  * Originally Released Under LGPL - original licence link has changed is not relivant.
25594  *
25595  * Fork - LGPL
25596  * <script type="text/javascript">
25597  */
25598
25599 /**
25600  * @class Roo.data.SimpleStore
25601  * @extends Roo.data.Store
25602  * Small helper class to make creating Stores from Array data easier.
25603  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25604  * @cfg {Array} fields An array of field definition objects, or field name strings.
25605  * @cfg {Object} an existing reader (eg. copied from another store)
25606  * @cfg {Array} data The multi-dimensional array of data
25607  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25608  * @cfg {Roo.data.Reader} reader  [not-required] 
25609  * @constructor
25610  * @param {Object} config
25611  */
25612 Roo.data.SimpleStore = function(config)
25613 {
25614     Roo.data.SimpleStore.superclass.constructor.call(this, {
25615         isLocal : true,
25616         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25617                 id: config.id
25618             },
25619             Roo.data.Record.create(config.fields)
25620         ),
25621         proxy : new Roo.data.MemoryProxy(config.data)
25622     });
25623     this.load();
25624 };
25625 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25626  * Based on:
25627  * Ext JS Library 1.1.1
25628  * Copyright(c) 2006-2007, Ext JS, LLC.
25629  *
25630  * Originally Released Under LGPL - original licence link has changed is not relivant.
25631  *
25632  * Fork - LGPL
25633  * <script type="text/javascript">
25634  */
25635
25636 /**
25637 /**
25638  * @extends Roo.data.Store
25639  * @class Roo.data.JsonStore
25640  * Small helper class to make creating Stores for JSON data easier. <br/>
25641 <pre><code>
25642 var store = new Roo.data.JsonStore({
25643     url: 'get-images.php',
25644     root: 'images',
25645     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25646 });
25647 </code></pre>
25648  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25649  * JsonReader and HttpProxy (unless inline data is provided).</b>
25650  * @cfg {Array} fields An array of field definition objects, or field name strings.
25651  * @constructor
25652  * @param {Object} config
25653  */
25654 Roo.data.JsonStore = function(c){
25655     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25656         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25657         reader: new Roo.data.JsonReader(c, c.fields)
25658     }));
25659 };
25660 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25661  * Based on:
25662  * Ext JS Library 1.1.1
25663  * Copyright(c) 2006-2007, Ext JS, LLC.
25664  *
25665  * Originally Released Under LGPL - original licence link has changed is not relivant.
25666  *
25667  * Fork - LGPL
25668  * <script type="text/javascript">
25669  */
25670
25671  
25672 Roo.data.Field = function(config){
25673     if(typeof config == "string"){
25674         config = {name: config};
25675     }
25676     Roo.apply(this, config);
25677     
25678     if(!this.type){
25679         this.type = "auto";
25680     }
25681     
25682     var st = Roo.data.SortTypes;
25683     // named sortTypes are supported, here we look them up
25684     if(typeof this.sortType == "string"){
25685         this.sortType = st[this.sortType];
25686     }
25687     
25688     // set default sortType for strings and dates
25689     if(!this.sortType){
25690         switch(this.type){
25691             case "string":
25692                 this.sortType = st.asUCString;
25693                 break;
25694             case "date":
25695                 this.sortType = st.asDate;
25696                 break;
25697             default:
25698                 this.sortType = st.none;
25699         }
25700     }
25701
25702     // define once
25703     var stripRe = /[\$,%]/g;
25704
25705     // prebuilt conversion function for this field, instead of
25706     // switching every time we're reading a value
25707     if(!this.convert){
25708         var cv, dateFormat = this.dateFormat;
25709         switch(this.type){
25710             case "":
25711             case "auto":
25712             case undefined:
25713                 cv = function(v){ return v; };
25714                 break;
25715             case "string":
25716                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25717                 break;
25718             case "int":
25719                 cv = function(v){
25720                     return v !== undefined && v !== null && v !== '' ?
25721                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25722                     };
25723                 break;
25724             case "float":
25725                 cv = function(v){
25726                     return v !== undefined && v !== null && v !== '' ?
25727                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25728                     };
25729                 break;
25730             case "bool":
25731             case "boolean":
25732                 cv = function(v){ return v === true || v === "true" || v == 1; };
25733                 break;
25734             case "date":
25735                 cv = function(v){
25736                     if(!v){
25737                         return '';
25738                     }
25739                     if(v instanceof Date){
25740                         return v;
25741                     }
25742                     if(dateFormat){
25743                         if(dateFormat == "timestamp"){
25744                             return new Date(v*1000);
25745                         }
25746                         return Date.parseDate(v, dateFormat);
25747                     }
25748                     var parsed = Date.parse(v);
25749                     return parsed ? new Date(parsed) : null;
25750                 };
25751              break;
25752             
25753         }
25754         this.convert = cv;
25755     }
25756 };
25757
25758 Roo.data.Field.prototype = {
25759     dateFormat: null,
25760     defaultValue: "",
25761     mapping: null,
25762     sortType : null,
25763     sortDir : "ASC"
25764 };/*
25765  * Based on:
25766  * Ext JS Library 1.1.1
25767  * Copyright(c) 2006-2007, Ext JS, LLC.
25768  *
25769  * Originally Released Under LGPL - original licence link has changed is not relivant.
25770  *
25771  * Fork - LGPL
25772  * <script type="text/javascript">
25773  */
25774  
25775 // Base class for reading structured data from a data source.  This class is intended to be
25776 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25777
25778 /**
25779  * @class Roo.data.DataReader
25780  * @abstract
25781  * Base class for reading structured data from a data source.  This class is intended to be
25782  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25783  */
25784
25785 Roo.data.DataReader = function(meta, recordType){
25786     
25787     this.meta = meta;
25788     
25789     this.recordType = recordType instanceof Array ? 
25790         Roo.data.Record.create(recordType) : recordType;
25791 };
25792
25793 Roo.data.DataReader.prototype = {
25794     
25795     
25796     readerType : 'Data',
25797      /**
25798      * Create an empty record
25799      * @param {Object} data (optional) - overlay some values
25800      * @return {Roo.data.Record} record created.
25801      */
25802     newRow :  function(d) {
25803         var da =  {};
25804         this.recordType.prototype.fields.each(function(c) {
25805             switch( c.type) {
25806                 case 'int' : da[c.name] = 0; break;
25807                 case 'date' : da[c.name] = new Date(); break;
25808                 case 'float' : da[c.name] = 0.0; break;
25809                 case 'boolean' : da[c.name] = false; break;
25810                 default : da[c.name] = ""; break;
25811             }
25812             
25813         });
25814         return new this.recordType(Roo.apply(da, d));
25815     }
25816     
25817     
25818 };/*
25819  * Based on:
25820  * Ext JS Library 1.1.1
25821  * Copyright(c) 2006-2007, Ext JS, LLC.
25822  *
25823  * Originally Released Under LGPL - original licence link has changed is not relivant.
25824  *
25825  * Fork - LGPL
25826  * <script type="text/javascript">
25827  */
25828
25829 /**
25830  * @class Roo.data.DataProxy
25831  * @extends Roo.util.Observable
25832  * @abstract
25833  * This class is an abstract base class for implementations which provide retrieval of
25834  * unformatted data objects.<br>
25835  * <p>
25836  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25837  * (of the appropriate type which knows how to parse the data object) to provide a block of
25838  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25839  * <p>
25840  * Custom implementations must implement the load method as described in
25841  * {@link Roo.data.HttpProxy#load}.
25842  */
25843 Roo.data.DataProxy = function(){
25844     this.addEvents({
25845         /**
25846          * @event beforeload
25847          * Fires before a network request is made to retrieve a data object.
25848          * @param {Object} This DataProxy object.
25849          * @param {Object} params The params parameter to the load function.
25850          */
25851         beforeload : true,
25852         /**
25853          * @event load
25854          * Fires before the load method's callback is called.
25855          * @param {Object} This DataProxy object.
25856          * @param {Object} o The data object.
25857          * @param {Object} arg The callback argument object passed to the load function.
25858          */
25859         load : true,
25860         /**
25861          * @event loadexception
25862          * Fires if an Exception occurs during data retrieval.
25863          * @param {Object} This DataProxy object.
25864          * @param {Object} o The data object.
25865          * @param {Object} arg The callback argument object passed to the load function.
25866          * @param {Object} e The Exception.
25867          */
25868         loadexception : true
25869     });
25870     Roo.data.DataProxy.superclass.constructor.call(this);
25871 };
25872
25873 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25874
25875     /**
25876      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25877      */
25878 /*
25879  * Based on:
25880  * Ext JS Library 1.1.1
25881  * Copyright(c) 2006-2007, Ext JS, LLC.
25882  *
25883  * Originally Released Under LGPL - original licence link has changed is not relivant.
25884  *
25885  * Fork - LGPL
25886  * <script type="text/javascript">
25887  */
25888 /**
25889  * @class Roo.data.MemoryProxy
25890  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25891  * to the Reader when its load method is called.
25892  * @constructor
25893  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25894  */
25895 Roo.data.MemoryProxy = function(data){
25896     if (data.data) {
25897         data = data.data;
25898     }
25899     Roo.data.MemoryProxy.superclass.constructor.call(this);
25900     this.data = data;
25901 };
25902
25903 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25904     
25905     /**
25906      * Load data from the requested source (in this case an in-memory
25907      * data object passed to the constructor), read the data object into
25908      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25909      * process that block using the passed callback.
25910      * @param {Object} params This parameter is not used by the MemoryProxy class.
25911      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25912      * object into a block of Roo.data.Records.
25913      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25914      * The function must be passed <ul>
25915      * <li>The Record block object</li>
25916      * <li>The "arg" argument from the load function</li>
25917      * <li>A boolean success indicator</li>
25918      * </ul>
25919      * @param {Object} scope The scope in which to call the callback
25920      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25921      */
25922     load : function(params, reader, callback, scope, arg){
25923         params = params || {};
25924         var result;
25925         try {
25926             result = reader.readRecords(params.data ? params.data :this.data);
25927         }catch(e){
25928             this.fireEvent("loadexception", this, arg, null, e);
25929             callback.call(scope, null, arg, false);
25930             return;
25931         }
25932         callback.call(scope, result, arg, true);
25933     },
25934     
25935     // private
25936     update : function(params, records){
25937         
25938     }
25939 });/*
25940  * Based on:
25941  * Ext JS Library 1.1.1
25942  * Copyright(c) 2006-2007, Ext JS, LLC.
25943  *
25944  * Originally Released Under LGPL - original licence link has changed is not relivant.
25945  *
25946  * Fork - LGPL
25947  * <script type="text/javascript">
25948  */
25949 /**
25950  * @class Roo.data.HttpProxy
25951  * @extends Roo.data.DataProxy
25952  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25953  * configured to reference a certain URL.<br><br>
25954  * <p>
25955  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25956  * from which the running page was served.<br><br>
25957  * <p>
25958  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25959  * <p>
25960  * Be aware that to enable the browser to parse an XML document, the server must set
25961  * the Content-Type header in the HTTP response to "text/xml".
25962  * @constructor
25963  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25964  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25965  * will be used to make the request.
25966  */
25967 Roo.data.HttpProxy = function(conn){
25968     Roo.data.HttpProxy.superclass.constructor.call(this);
25969     // is conn a conn config or a real conn?
25970     this.conn = conn;
25971     this.useAjax = !conn || !conn.events;
25972   
25973 };
25974
25975 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25976     // thse are take from connection...
25977     
25978     /**
25979      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25980      */
25981     /**
25982      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25983      * extra parameters to each request made by this object. (defaults to undefined)
25984      */
25985     /**
25986      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25987      *  to each request made by this object. (defaults to undefined)
25988      */
25989     /**
25990      * @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)
25991      */
25992     /**
25993      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25994      */
25995      /**
25996      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25997      * @type Boolean
25998      */
25999   
26000
26001     /**
26002      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
26003      * @type Boolean
26004      */
26005     /**
26006      * Return the {@link Roo.data.Connection} object being used by this Proxy.
26007      * @return {Connection} The Connection object. This object may be used to subscribe to events on
26008      * a finer-grained basis than the DataProxy events.
26009      */
26010     getConnection : function(){
26011         return this.useAjax ? Roo.Ajax : this.conn;
26012     },
26013
26014     /**
26015      * Load data from the configured {@link Roo.data.Connection}, read the data object into
26016      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
26017      * process that block using the passed callback.
26018      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26019      * for the request to the remote server.
26020      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26021      * object into a block of Roo.data.Records.
26022      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26023      * The function must be passed <ul>
26024      * <li>The Record block object</li>
26025      * <li>The "arg" argument from the load function</li>
26026      * <li>A boolean success indicator</li>
26027      * </ul>
26028      * @param {Object} scope The scope in which to call the callback
26029      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26030      */
26031     load : function(params, reader, callback, scope, arg){
26032         if(this.fireEvent("beforeload", this, params) !== false){
26033             var  o = {
26034                 params : params || {},
26035                 request: {
26036                     callback : callback,
26037                     scope : scope,
26038                     arg : arg
26039                 },
26040                 reader: reader,
26041                 callback : this.loadResponse,
26042                 scope: this
26043             };
26044             if(this.useAjax){
26045                 Roo.applyIf(o, this.conn);
26046                 if(this.activeRequest){
26047                     Roo.Ajax.abort(this.activeRequest);
26048                 }
26049                 this.activeRequest = Roo.Ajax.request(o);
26050             }else{
26051                 this.conn.request(o);
26052             }
26053         }else{
26054             callback.call(scope||this, null, arg, false);
26055         }
26056     },
26057
26058     // private
26059     loadResponse : function(o, success, response){
26060         delete this.activeRequest;
26061         if(!success){
26062             this.fireEvent("loadexception", this, o, response);
26063             o.request.callback.call(o.request.scope, null, o.request.arg, false);
26064             return;
26065         }
26066         var result;
26067         try {
26068             result = o.reader.read(response);
26069         }catch(e){
26070             o.success = false;
26071             o.raw = { errorMsg : response.responseText };
26072             this.fireEvent("loadexception", this, o, response, e);
26073             o.request.callback.call(o.request.scope, o, o.request.arg, false);
26074             return;
26075         }
26076         
26077         this.fireEvent("load", this, o, o.request.arg);
26078         o.request.callback.call(o.request.scope, result, o.request.arg, true);
26079     },
26080
26081     // private
26082     update : function(dataSet){
26083
26084     },
26085
26086     // private
26087     updateResponse : function(dataSet){
26088
26089     }
26090 });/*
26091  * Based on:
26092  * Ext JS Library 1.1.1
26093  * Copyright(c) 2006-2007, Ext JS, LLC.
26094  *
26095  * Originally Released Under LGPL - original licence link has changed is not relivant.
26096  *
26097  * Fork - LGPL
26098  * <script type="text/javascript">
26099  */
26100
26101 /**
26102  * @class Roo.data.ScriptTagProxy
26103  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26104  * other than the originating domain of the running page.<br><br>
26105  * <p>
26106  * <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
26107  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26108  * <p>
26109  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26110  * source code that is used as the source inside a &lt;script> tag.<br><br>
26111  * <p>
26112  * In order for the browser to process the returned data, the server must wrap the data object
26113  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26114  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26115  * depending on whether the callback name was passed:
26116  * <p>
26117  * <pre><code>
26118 boolean scriptTag = false;
26119 String cb = request.getParameter("callback");
26120 if (cb != null) {
26121     scriptTag = true;
26122     response.setContentType("text/javascript");
26123 } else {
26124     response.setContentType("application/x-json");
26125 }
26126 Writer out = response.getWriter();
26127 if (scriptTag) {
26128     out.write(cb + "(");
26129 }
26130 out.print(dataBlock.toJsonString());
26131 if (scriptTag) {
26132     out.write(");");
26133 }
26134 </pre></code>
26135  *
26136  * @constructor
26137  * @param {Object} config A configuration object.
26138  */
26139 Roo.data.ScriptTagProxy = function(config){
26140     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26141     Roo.apply(this, config);
26142     this.head = document.getElementsByTagName("head")[0];
26143 };
26144
26145 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26146
26147 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26148     /**
26149      * @cfg {String} url The URL from which to request the data object.
26150      */
26151     /**
26152      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26153      */
26154     timeout : 30000,
26155     /**
26156      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26157      * the server the name of the callback function set up by the load call to process the returned data object.
26158      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26159      * javascript output which calls this named function passing the data object as its only parameter.
26160      */
26161     callbackParam : "callback",
26162     /**
26163      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26164      * name to the request.
26165      */
26166     nocache : true,
26167
26168     /**
26169      * Load data from the configured URL, read the data object into
26170      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26171      * process that block using the passed callback.
26172      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26173      * for the request to the remote server.
26174      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26175      * object into a block of Roo.data.Records.
26176      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26177      * The function must be passed <ul>
26178      * <li>The Record block object</li>
26179      * <li>The "arg" argument from the load function</li>
26180      * <li>A boolean success indicator</li>
26181      * </ul>
26182      * @param {Object} scope The scope in which to call the callback
26183      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26184      */
26185     load : function(params, reader, callback, scope, arg){
26186         if(this.fireEvent("beforeload", this, params) !== false){
26187
26188             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26189
26190             var url = this.url;
26191             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26192             if(this.nocache){
26193                 url += "&_dc=" + (new Date().getTime());
26194             }
26195             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26196             var trans = {
26197                 id : transId,
26198                 cb : "stcCallback"+transId,
26199                 scriptId : "stcScript"+transId,
26200                 params : params,
26201                 arg : arg,
26202                 url : url,
26203                 callback : callback,
26204                 scope : scope,
26205                 reader : reader
26206             };
26207             var conn = this;
26208
26209             window[trans.cb] = function(o){
26210                 conn.handleResponse(o, trans);
26211             };
26212
26213             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26214
26215             if(this.autoAbort !== false){
26216                 this.abort();
26217             }
26218
26219             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26220
26221             var script = document.createElement("script");
26222             script.setAttribute("src", url);
26223             script.setAttribute("type", "text/javascript");
26224             script.setAttribute("id", trans.scriptId);
26225             this.head.appendChild(script);
26226
26227             this.trans = trans;
26228         }else{
26229             callback.call(scope||this, null, arg, false);
26230         }
26231     },
26232
26233     // private
26234     isLoading : function(){
26235         return this.trans ? true : false;
26236     },
26237
26238     /**
26239      * Abort the current server request.
26240      */
26241     abort : function(){
26242         if(this.isLoading()){
26243             this.destroyTrans(this.trans);
26244         }
26245     },
26246
26247     // private
26248     destroyTrans : function(trans, isLoaded){
26249         this.head.removeChild(document.getElementById(trans.scriptId));
26250         clearTimeout(trans.timeoutId);
26251         if(isLoaded){
26252             window[trans.cb] = undefined;
26253             try{
26254                 delete window[trans.cb];
26255             }catch(e){}
26256         }else{
26257             // if hasn't been loaded, wait for load to remove it to prevent script error
26258             window[trans.cb] = function(){
26259                 window[trans.cb] = undefined;
26260                 try{
26261                     delete window[trans.cb];
26262                 }catch(e){}
26263             };
26264         }
26265     },
26266
26267     // private
26268     handleResponse : function(o, trans){
26269         this.trans = false;
26270         this.destroyTrans(trans, true);
26271         var result;
26272         try {
26273             result = trans.reader.readRecords(o);
26274         }catch(e){
26275             this.fireEvent("loadexception", this, o, trans.arg, e);
26276             trans.callback.call(trans.scope||window, null, trans.arg, false);
26277             return;
26278         }
26279         this.fireEvent("load", this, o, trans.arg);
26280         trans.callback.call(trans.scope||window, result, trans.arg, true);
26281     },
26282
26283     // private
26284     handleFailure : function(trans){
26285         this.trans = false;
26286         this.destroyTrans(trans, false);
26287         this.fireEvent("loadexception", this, null, trans.arg);
26288         trans.callback.call(trans.scope||window, null, trans.arg, false);
26289     }
26290 });/*
26291  * Based on:
26292  * Ext JS Library 1.1.1
26293  * Copyright(c) 2006-2007, Ext JS, LLC.
26294  *
26295  * Originally Released Under LGPL - original licence link has changed is not relivant.
26296  *
26297  * Fork - LGPL
26298  * <script type="text/javascript">
26299  */
26300
26301 /**
26302  * @class Roo.data.JsonReader
26303  * @extends Roo.data.DataReader
26304  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26305  * based on mappings in a provided Roo.data.Record constructor.
26306  * 
26307  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26308  * in the reply previously. 
26309  * 
26310  * <p>
26311  * Example code:
26312  * <pre><code>
26313 var RecordDef = Roo.data.Record.create([
26314     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26315     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26316 ]);
26317 var myReader = new Roo.data.JsonReader({
26318     totalProperty: "results",    // The property which contains the total dataset size (optional)
26319     root: "rows",                // The property which contains an Array of row objects
26320     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26321 }, RecordDef);
26322 </code></pre>
26323  * <p>
26324  * This would consume a JSON file like this:
26325  * <pre><code>
26326 { 'results': 2, 'rows': [
26327     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26328     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26329 }
26330 </code></pre>
26331  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26332  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26333  * paged from the remote server.
26334  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26335  * @cfg {String} root name of the property which contains the Array of row objects.
26336  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26337  * @cfg {Array} fields Array of field definition objects
26338  * @constructor
26339  * Create a new JsonReader
26340  * @param {Object} meta Metadata configuration options
26341  * @param {Object} recordType Either an Array of field definition objects,
26342  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26343  */
26344 Roo.data.JsonReader = function(meta, recordType){
26345     
26346     meta = meta || {};
26347     // set some defaults:
26348     Roo.applyIf(meta, {
26349         totalProperty: 'total',
26350         successProperty : 'success',
26351         root : 'data',
26352         id : 'id'
26353     });
26354     
26355     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26356 };
26357 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26358     
26359     readerType : 'Json',
26360     
26361     /**
26362      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26363      * Used by Store query builder to append _requestMeta to params.
26364      * 
26365      */
26366     metaFromRemote : false,
26367     /**
26368      * This method is only used by a DataProxy which has retrieved data from a remote server.
26369      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26370      * @return {Object} data A data block which is used by an Roo.data.Store object as
26371      * a cache of Roo.data.Records.
26372      */
26373     read : function(response){
26374         var json = response.responseText;
26375        
26376         var o = /* eval:var:o */ eval("("+json+")");
26377         if(!o) {
26378             throw {message: "JsonReader.read: Json object not found"};
26379         }
26380         
26381         if(o.metaData){
26382             
26383             delete this.ef;
26384             this.metaFromRemote = true;
26385             this.meta = o.metaData;
26386             this.recordType = Roo.data.Record.create(o.metaData.fields);
26387             this.onMetaChange(this.meta, this.recordType, o);
26388         }
26389         return this.readRecords(o);
26390     },
26391
26392     // private function a store will implement
26393     onMetaChange : function(meta, recordType, o){
26394
26395     },
26396
26397     /**
26398          * @ignore
26399          */
26400     simpleAccess: function(obj, subsc) {
26401         return obj[subsc];
26402     },
26403
26404         /**
26405          * @ignore
26406          */
26407     getJsonAccessor: function(){
26408         var re = /[\[\.]/;
26409         return function(expr) {
26410             try {
26411                 return(re.test(expr))
26412                     ? new Function("obj", "return obj." + expr)
26413                     : function(obj){
26414                         return obj[expr];
26415                     };
26416             } catch(e){}
26417             return Roo.emptyFn;
26418         };
26419     }(),
26420
26421     /**
26422      * Create a data block containing Roo.data.Records from an XML document.
26423      * @param {Object} o An object which contains an Array of row objects in the property specified
26424      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26425      * which contains the total size of the dataset.
26426      * @return {Object} data A data block which is used by an Roo.data.Store object as
26427      * a cache of Roo.data.Records.
26428      */
26429     readRecords : function(o){
26430         /**
26431          * After any data loads, the raw JSON data is available for further custom processing.
26432          * @type Object
26433          */
26434         this.o = o;
26435         var s = this.meta, Record = this.recordType,
26436             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26437
26438 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26439         if (!this.ef) {
26440             if(s.totalProperty) {
26441                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26442                 }
26443                 if(s.successProperty) {
26444                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26445                 }
26446                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26447                 if (s.id) {
26448                         var g = this.getJsonAccessor(s.id);
26449                         this.getId = function(rec) {
26450                                 var r = g(rec);  
26451                                 return (r === undefined || r === "") ? null : r;
26452                         };
26453                 } else {
26454                         this.getId = function(){return null;};
26455                 }
26456             this.ef = [];
26457             for(var jj = 0; jj < fl; jj++){
26458                 f = fi[jj];
26459                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26460                 this.ef[jj] = this.getJsonAccessor(map);
26461             }
26462         }
26463
26464         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26465         if(s.totalProperty){
26466             var vt = parseInt(this.getTotal(o), 10);
26467             if(!isNaN(vt)){
26468                 totalRecords = vt;
26469             }
26470         }
26471         if(s.successProperty){
26472             var vs = this.getSuccess(o);
26473             if(vs === false || vs === 'false'){
26474                 success = false;
26475             }
26476         }
26477         var records = [];
26478         for(var i = 0; i < c; i++){
26479             var n = root[i];
26480             var values = {};
26481             var id = this.getId(n);
26482             for(var j = 0; j < fl; j++){
26483                 f = fi[j];
26484                                 var v = this.ef[j](n);
26485                                 if (!f.convert) {
26486                                         Roo.log('missing convert for ' + f.name);
26487                                         Roo.log(f);
26488                                         continue;
26489                                 }
26490                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26491             }
26492                         if (!Record) {
26493                                 return {
26494                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26495                                         success : false,
26496                                         records : [],
26497                                         totalRecords : 0
26498                                 };
26499                         }
26500             var record = new Record(values, id);
26501             record.json = n;
26502             records[i] = record;
26503         }
26504         return {
26505             raw : o,
26506             success : success,
26507             records : records,
26508             totalRecords : totalRecords
26509         };
26510     },
26511     // used when loading children.. @see loadDataFromChildren
26512     toLoadData: function(rec)
26513     {
26514         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26515         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26516         return { data : data, total : data.length };
26517         
26518     }
26519 });/*
26520  * Based on:
26521  * Ext JS Library 1.1.1
26522  * Copyright(c) 2006-2007, Ext JS, LLC.
26523  *
26524  * Originally Released Under LGPL - original licence link has changed is not relivant.
26525  *
26526  * Fork - LGPL
26527  * <script type="text/javascript">
26528  */
26529
26530 /**
26531  * @class Roo.data.XmlReader
26532  * @extends Roo.data.DataReader
26533  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26534  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26535  * <p>
26536  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26537  * header in the HTTP response must be set to "text/xml".</em>
26538  * <p>
26539  * Example code:
26540  * <pre><code>
26541 var RecordDef = Roo.data.Record.create([
26542    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26543    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26544 ]);
26545 var myReader = new Roo.data.XmlReader({
26546    totalRecords: "results", // The element which contains the total dataset size (optional)
26547    record: "row",           // The repeated element which contains row information
26548    id: "id"                 // The element within the row that provides an ID for the record (optional)
26549 }, RecordDef);
26550 </code></pre>
26551  * <p>
26552  * This would consume an XML file like this:
26553  * <pre><code>
26554 &lt;?xml?>
26555 &lt;dataset>
26556  &lt;results>2&lt;/results>
26557  &lt;row>
26558    &lt;id>1&lt;/id>
26559    &lt;name>Bill&lt;/name>
26560    &lt;occupation>Gardener&lt;/occupation>
26561  &lt;/row>
26562  &lt;row>
26563    &lt;id>2&lt;/id>
26564    &lt;name>Ben&lt;/name>
26565    &lt;occupation>Horticulturalist&lt;/occupation>
26566  &lt;/row>
26567 &lt;/dataset>
26568 </code></pre>
26569  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26570  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26571  * paged from the remote server.
26572  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26573  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26574  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26575  * a record identifier value.
26576  * @constructor
26577  * Create a new XmlReader
26578  * @param {Object} meta Metadata configuration options
26579  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26580  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26581  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26582  */
26583 Roo.data.XmlReader = function(meta, recordType){
26584     meta = meta || {};
26585     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26586 };
26587 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26588     
26589     readerType : 'Xml',
26590     
26591     /**
26592      * This method is only used by a DataProxy which has retrieved data from a remote server.
26593          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26594          * to contain a method called 'responseXML' that returns an XML document object.
26595      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26596      * a cache of Roo.data.Records.
26597      */
26598     read : function(response){
26599         var doc = response.responseXML;
26600         if(!doc) {
26601             throw {message: "XmlReader.read: XML Document not available"};
26602         }
26603         return this.readRecords(doc);
26604     },
26605
26606     /**
26607      * Create a data block containing Roo.data.Records from an XML document.
26608          * @param {Object} doc A parsed XML document.
26609      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26610      * a cache of Roo.data.Records.
26611      */
26612     readRecords : function(doc){
26613         /**
26614          * After any data loads/reads, the raw XML Document is available for further custom processing.
26615          * @type XMLDocument
26616          */
26617         this.xmlData = doc;
26618         var root = doc.documentElement || doc;
26619         var q = Roo.DomQuery;
26620         var recordType = this.recordType, fields = recordType.prototype.fields;
26621         var sid = this.meta.id;
26622         var totalRecords = 0, success = true;
26623         if(this.meta.totalRecords){
26624             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26625         }
26626         
26627         if(this.meta.success){
26628             var sv = q.selectValue(this.meta.success, root, true);
26629             success = sv !== false && sv !== 'false';
26630         }
26631         var records = [];
26632         var ns = q.select(this.meta.record, root);
26633         for(var i = 0, len = ns.length; i < len; i++) {
26634                 var n = ns[i];
26635                 var values = {};
26636                 var id = sid ? q.selectValue(sid, n) : undefined;
26637                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26638                     var f = fields.items[j];
26639                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26640                     v = f.convert(v);
26641                     values[f.name] = v;
26642                 }
26643                 var record = new recordType(values, id);
26644                 record.node = n;
26645                 records[records.length] = record;
26646             }
26647
26648             return {
26649                 success : success,
26650                 records : records,
26651                 totalRecords : totalRecords || records.length
26652             };
26653     }
26654 });/*
26655  * Based on:
26656  * Ext JS Library 1.1.1
26657  * Copyright(c) 2006-2007, Ext JS, LLC.
26658  *
26659  * Originally Released Under LGPL - original licence link has changed is not relivant.
26660  *
26661  * Fork - LGPL
26662  * <script type="text/javascript">
26663  */
26664
26665 /**
26666  * @class Roo.data.ArrayReader
26667  * @extends Roo.data.DataReader
26668  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26669  * Each element of that Array represents a row of data fields. The
26670  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26671  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26672  * <p>
26673  * Example code:.
26674  * <pre><code>
26675 var RecordDef = Roo.data.Record.create([
26676     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26677     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26678 ]);
26679 var myReader = new Roo.data.ArrayReader({
26680     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26681 }, RecordDef);
26682 </code></pre>
26683  * <p>
26684  * This would consume an Array like this:
26685  * <pre><code>
26686 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26687   </code></pre>
26688  
26689  * @constructor
26690  * Create a new JsonReader
26691  * @param {Object} meta Metadata configuration options.
26692  * @param {Object|Array} recordType Either an Array of field definition objects
26693  * 
26694  * @cfg {Array} fields Array of field definition objects
26695  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26696  * as specified to {@link Roo.data.Record#create},
26697  * or an {@link Roo.data.Record} object
26698  *
26699  * 
26700  * created using {@link Roo.data.Record#create}.
26701  */
26702 Roo.data.ArrayReader = function(meta, recordType)
26703 {    
26704     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26705 };
26706
26707 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26708     
26709       /**
26710      * Create a data block containing Roo.data.Records from an XML document.
26711      * @param {Object} o An Array of row objects which represents the dataset.
26712      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26713      * a cache of Roo.data.Records.
26714      */
26715     readRecords : function(o)
26716     {
26717         var sid = this.meta ? this.meta.id : null;
26718         var recordType = this.recordType, fields = recordType.prototype.fields;
26719         var records = [];
26720         var root = o;
26721         for(var i = 0; i < root.length; i++){
26722             var n = root[i];
26723             var values = {};
26724             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26725             for(var j = 0, jlen = fields.length; j < jlen; j++){
26726                 var f = fields.items[j];
26727                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26728                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26729                 v = f.convert(v);
26730                 values[f.name] = v;
26731             }
26732             var record = new recordType(values, id);
26733             record.json = n;
26734             records[records.length] = record;
26735         }
26736         return {
26737             records : records,
26738             totalRecords : records.length
26739         };
26740     },
26741     // used when loading children.. @see loadDataFromChildren
26742     toLoadData: function(rec)
26743     {
26744         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26745         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26746         
26747     }
26748     
26749     
26750 });/*
26751  * Based on:
26752  * Ext JS Library 1.1.1
26753  * Copyright(c) 2006-2007, Ext JS, LLC.
26754  *
26755  * Originally Released Under LGPL - original licence link has changed is not relivant.
26756  *
26757  * Fork - LGPL
26758  * <script type="text/javascript">
26759  */
26760
26761
26762 /**
26763  * @class Roo.data.Tree
26764  * @extends Roo.util.Observable
26765  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26766  * in the tree have most standard DOM functionality.
26767  * @constructor
26768  * @param {Node} root (optional) The root node
26769  */
26770 Roo.data.Tree = function(root){
26771    this.nodeHash = {};
26772    /**
26773     * The root node for this tree
26774     * @type Node
26775     */
26776    this.root = null;
26777    if(root){
26778        this.setRootNode(root);
26779    }
26780    this.addEvents({
26781        /**
26782         * @event append
26783         * Fires when a new child node is appended to a node in this tree.
26784         * @param {Tree} tree The owner tree
26785         * @param {Node} parent The parent node
26786         * @param {Node} node The newly appended node
26787         * @param {Number} index The index of the newly appended node
26788         */
26789        "append" : true,
26790        /**
26791         * @event remove
26792         * Fires when a child node is removed from a node in this tree.
26793         * @param {Tree} tree The owner tree
26794         * @param {Node} parent The parent node
26795         * @param {Node} node The child node removed
26796         */
26797        "remove" : true,
26798        /**
26799         * @event move
26800         * Fires when a node is moved to a new location in the tree
26801         * @param {Tree} tree The owner tree
26802         * @param {Node} node The node moved
26803         * @param {Node} oldParent The old parent of this node
26804         * @param {Node} newParent The new parent of this node
26805         * @param {Number} index The index it was moved to
26806         */
26807        "move" : true,
26808        /**
26809         * @event insert
26810         * Fires when a new child node is inserted in a node in this tree.
26811         * @param {Tree} tree The owner tree
26812         * @param {Node} parent The parent node
26813         * @param {Node} node The child node inserted
26814         * @param {Node} refNode The child node the node was inserted before
26815         */
26816        "insert" : true,
26817        /**
26818         * @event beforeappend
26819         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26820         * @param {Tree} tree The owner tree
26821         * @param {Node} parent The parent node
26822         * @param {Node} node The child node to be appended
26823         */
26824        "beforeappend" : true,
26825        /**
26826         * @event beforeremove
26827         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26828         * @param {Tree} tree The owner tree
26829         * @param {Node} parent The parent node
26830         * @param {Node} node The child node to be removed
26831         */
26832        "beforeremove" : true,
26833        /**
26834         * @event beforemove
26835         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26836         * @param {Tree} tree The owner tree
26837         * @param {Node} node The node being moved
26838         * @param {Node} oldParent The parent of the node
26839         * @param {Node} newParent The new parent the node is moving to
26840         * @param {Number} index The index it is being moved to
26841         */
26842        "beforemove" : true,
26843        /**
26844         * @event beforeinsert
26845         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26846         * @param {Tree} tree The owner tree
26847         * @param {Node} parent The parent node
26848         * @param {Node} node The child node to be inserted
26849         * @param {Node} refNode The child node the node is being inserted before
26850         */
26851        "beforeinsert" : true
26852    });
26853
26854     Roo.data.Tree.superclass.constructor.call(this);
26855 };
26856
26857 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26858     pathSeparator: "/",
26859
26860     proxyNodeEvent : function(){
26861         return this.fireEvent.apply(this, arguments);
26862     },
26863
26864     /**
26865      * Returns the root node for this tree.
26866      * @return {Node}
26867      */
26868     getRootNode : function(){
26869         return this.root;
26870     },
26871
26872     /**
26873      * Sets the root node for this tree.
26874      * @param {Node} node
26875      * @return {Node}
26876      */
26877     setRootNode : function(node){
26878         this.root = node;
26879         node.ownerTree = this;
26880         node.isRoot = true;
26881         this.registerNode(node);
26882         return node;
26883     },
26884
26885     /**
26886      * Gets a node in this tree by its id.
26887      * @param {String} id
26888      * @return {Node}
26889      */
26890     getNodeById : function(id){
26891         return this.nodeHash[id];
26892     },
26893
26894     registerNode : function(node){
26895         this.nodeHash[node.id] = node;
26896     },
26897
26898     unregisterNode : function(node){
26899         delete this.nodeHash[node.id];
26900     },
26901
26902     toString : function(){
26903         return "[Tree"+(this.id?" "+this.id:"")+"]";
26904     }
26905 });
26906
26907 /**
26908  * @class Roo.data.Node
26909  * @extends Roo.util.Observable
26910  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26911  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26912  * @constructor
26913  * @param {Object} attributes The attributes/config for the node
26914  */
26915 Roo.data.Node = function(attributes){
26916     /**
26917      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26918      * @type {Object}
26919      */
26920     this.attributes = attributes || {};
26921     this.leaf = this.attributes.leaf;
26922     /**
26923      * The node id. @type String
26924      */
26925     this.id = this.attributes.id;
26926     if(!this.id){
26927         this.id = Roo.id(null, "ynode-");
26928         this.attributes.id = this.id;
26929     }
26930      
26931     
26932     /**
26933      * All child nodes of this node. @type Array
26934      */
26935     this.childNodes = [];
26936     if(!this.childNodes.indexOf){ // indexOf is a must
26937         this.childNodes.indexOf = function(o){
26938             for(var i = 0, len = this.length; i < len; i++){
26939                 if(this[i] == o) {
26940                     return i;
26941                 }
26942             }
26943             return -1;
26944         };
26945     }
26946     /**
26947      * The parent node for this node. @type Node
26948      */
26949     this.parentNode = null;
26950     /**
26951      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26952      */
26953     this.firstChild = null;
26954     /**
26955      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26956      */
26957     this.lastChild = null;
26958     /**
26959      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26960      */
26961     this.previousSibling = null;
26962     /**
26963      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26964      */
26965     this.nextSibling = null;
26966
26967     this.addEvents({
26968        /**
26969         * @event append
26970         * Fires when a new child node is appended
26971         * @param {Tree} tree The owner tree
26972         * @param {Node} this This node
26973         * @param {Node} node The newly appended node
26974         * @param {Number} index The index of the newly appended node
26975         */
26976        "append" : true,
26977        /**
26978         * @event remove
26979         * Fires when a child node is removed
26980         * @param {Tree} tree The owner tree
26981         * @param {Node} this This node
26982         * @param {Node} node The removed node
26983         */
26984        "remove" : true,
26985        /**
26986         * @event move
26987         * Fires when this node is moved to a new location in the tree
26988         * @param {Tree} tree The owner tree
26989         * @param {Node} this This node
26990         * @param {Node} oldParent The old parent of this node
26991         * @param {Node} newParent The new parent of this node
26992         * @param {Number} index The index it was moved to
26993         */
26994        "move" : true,
26995        /**
26996         * @event insert
26997         * Fires when a new child node is inserted.
26998         * @param {Tree} tree The owner tree
26999         * @param {Node} this This node
27000         * @param {Node} node The child node inserted
27001         * @param {Node} refNode The child node the node was inserted before
27002         */
27003        "insert" : true,
27004        /**
27005         * @event beforeappend
27006         * Fires before a new child is appended, return false to cancel the append.
27007         * @param {Tree} tree The owner tree
27008         * @param {Node} this This node
27009         * @param {Node} node The child node to be appended
27010         */
27011        "beforeappend" : true,
27012        /**
27013         * @event beforeremove
27014         * Fires before a child is removed, return false to cancel the remove.
27015         * @param {Tree} tree The owner tree
27016         * @param {Node} this This node
27017         * @param {Node} node The child node to be removed
27018         */
27019        "beforeremove" : true,
27020        /**
27021         * @event beforemove
27022         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
27023         * @param {Tree} tree The owner tree
27024         * @param {Node} this This node
27025         * @param {Node} oldParent The parent of this node
27026         * @param {Node} newParent The new parent this node is moving to
27027         * @param {Number} index The index it is being moved to
27028         */
27029        "beforemove" : true,
27030        /**
27031         * @event beforeinsert
27032         * Fires before a new child is inserted, return false to cancel the insert.
27033         * @param {Tree} tree The owner tree
27034         * @param {Node} this This node
27035         * @param {Node} node The child node to be inserted
27036         * @param {Node} refNode The child node the node is being inserted before
27037         */
27038        "beforeinsert" : true
27039    });
27040     this.listeners = this.attributes.listeners;
27041     Roo.data.Node.superclass.constructor.call(this);
27042 };
27043
27044 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27045     fireEvent : function(evtName){
27046         // first do standard event for this node
27047         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27048             return false;
27049         }
27050         // then bubble it up to the tree if the event wasn't cancelled
27051         var ot = this.getOwnerTree();
27052         if(ot){
27053             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27054                 return false;
27055             }
27056         }
27057         return true;
27058     },
27059
27060     /**
27061      * Returns true if this node is a leaf
27062      * @return {Boolean}
27063      */
27064     isLeaf : function(){
27065         return this.leaf === true;
27066     },
27067
27068     // private
27069     setFirstChild : function(node){
27070         this.firstChild = node;
27071     },
27072
27073     //private
27074     setLastChild : function(node){
27075         this.lastChild = node;
27076     },
27077
27078
27079     /**
27080      * Returns true if this node is the last child of its parent
27081      * @return {Boolean}
27082      */
27083     isLast : function(){
27084        return (!this.parentNode ? true : this.parentNode.lastChild == this);
27085     },
27086
27087     /**
27088      * Returns true if this node is the first child of its parent
27089      * @return {Boolean}
27090      */
27091     isFirst : function(){
27092        return (!this.parentNode ? true : this.parentNode.firstChild == this);
27093     },
27094
27095     hasChildNodes : function(){
27096         return !this.isLeaf() && this.childNodes.length > 0;
27097     },
27098
27099     /**
27100      * Insert node(s) as the last child node of this node.
27101      * @param {Node/Array} node The node or Array of nodes to append
27102      * @return {Node} The appended node if single append, or null if an array was passed
27103      */
27104     appendChild : function(node){
27105         var multi = false;
27106         if(node instanceof Array){
27107             multi = node;
27108         }else if(arguments.length > 1){
27109             multi = arguments;
27110         }
27111         
27112         // if passed an array or multiple args do them one by one
27113         if(multi){
27114             for(var i = 0, len = multi.length; i < len; i++) {
27115                 this.appendChild(multi[i]);
27116             }
27117         }else{
27118             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27119                 return false;
27120             }
27121             var index = this.childNodes.length;
27122             var oldParent = node.parentNode;
27123             // it's a move, make sure we move it cleanly
27124             if(oldParent){
27125                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27126                     return false;
27127                 }
27128                 oldParent.removeChild(node);
27129             }
27130             
27131             index = this.childNodes.length;
27132             if(index == 0){
27133                 this.setFirstChild(node);
27134             }
27135             this.childNodes.push(node);
27136             node.parentNode = this;
27137             var ps = this.childNodes[index-1];
27138             if(ps){
27139                 node.previousSibling = ps;
27140                 ps.nextSibling = node;
27141             }else{
27142                 node.previousSibling = null;
27143             }
27144             node.nextSibling = null;
27145             this.setLastChild(node);
27146             node.setOwnerTree(this.getOwnerTree());
27147             this.fireEvent("append", this.ownerTree, this, node, index);
27148             if(this.ownerTree) {
27149                 this.ownerTree.fireEvent("appendnode", this, node, index);
27150             }
27151             if(oldParent){
27152                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27153             }
27154             return node;
27155         }
27156     },
27157
27158     /**
27159      * Removes a child node from this node.
27160      * @param {Node} node The node to remove
27161      * @return {Node} The removed node
27162      */
27163     removeChild : function(node){
27164         var index = this.childNodes.indexOf(node);
27165         if(index == -1){
27166             return false;
27167         }
27168         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27169             return false;
27170         }
27171
27172         // remove it from childNodes collection
27173         this.childNodes.splice(index, 1);
27174
27175         // update siblings
27176         if(node.previousSibling){
27177             node.previousSibling.nextSibling = node.nextSibling;
27178         }
27179         if(node.nextSibling){
27180             node.nextSibling.previousSibling = node.previousSibling;
27181         }
27182
27183         // update child refs
27184         if(this.firstChild == node){
27185             this.setFirstChild(node.nextSibling);
27186         }
27187         if(this.lastChild == node){
27188             this.setLastChild(node.previousSibling);
27189         }
27190
27191         node.setOwnerTree(null);
27192         // clear any references from the node
27193         node.parentNode = null;
27194         node.previousSibling = null;
27195         node.nextSibling = null;
27196         this.fireEvent("remove", this.ownerTree, this, node);
27197         return node;
27198     },
27199
27200     /**
27201      * Inserts the first node before the second node in this nodes childNodes collection.
27202      * @param {Node} node The node to insert
27203      * @param {Node} refNode The node to insert before (if null the node is appended)
27204      * @return {Node} The inserted node
27205      */
27206     insertBefore : function(node, refNode){
27207         if(!refNode){ // like standard Dom, refNode can be null for append
27208             return this.appendChild(node);
27209         }
27210         // nothing to do
27211         if(node == refNode){
27212             return false;
27213         }
27214
27215         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27216             return false;
27217         }
27218         var index = this.childNodes.indexOf(refNode);
27219         var oldParent = node.parentNode;
27220         var refIndex = index;
27221
27222         // when moving internally, indexes will change after remove
27223         if(oldParent == this && this.childNodes.indexOf(node) < index){
27224             refIndex--;
27225         }
27226
27227         // it's a move, make sure we move it cleanly
27228         if(oldParent){
27229             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27230                 return false;
27231             }
27232             oldParent.removeChild(node);
27233         }
27234         if(refIndex == 0){
27235             this.setFirstChild(node);
27236         }
27237         this.childNodes.splice(refIndex, 0, node);
27238         node.parentNode = this;
27239         var ps = this.childNodes[refIndex-1];
27240         if(ps){
27241             node.previousSibling = ps;
27242             ps.nextSibling = node;
27243         }else{
27244             node.previousSibling = null;
27245         }
27246         node.nextSibling = refNode;
27247         refNode.previousSibling = node;
27248         node.setOwnerTree(this.getOwnerTree());
27249         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27250         if(oldParent){
27251             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27252         }
27253         return node;
27254     },
27255
27256     /**
27257      * Returns the child node at the specified index.
27258      * @param {Number} index
27259      * @return {Node}
27260      */
27261     item : function(index){
27262         return this.childNodes[index];
27263     },
27264
27265     /**
27266      * Replaces one child node in this node with another.
27267      * @param {Node} newChild The replacement node
27268      * @param {Node} oldChild The node to replace
27269      * @return {Node} The replaced node
27270      */
27271     replaceChild : function(newChild, oldChild){
27272         this.insertBefore(newChild, oldChild);
27273         this.removeChild(oldChild);
27274         return oldChild;
27275     },
27276
27277     /**
27278      * Returns the index of a child node
27279      * @param {Node} node
27280      * @return {Number} The index of the node or -1 if it was not found
27281      */
27282     indexOf : function(child){
27283         return this.childNodes.indexOf(child);
27284     },
27285
27286     /**
27287      * Returns the tree this node is in.
27288      * @return {Tree}
27289      */
27290     getOwnerTree : function(){
27291         // if it doesn't have one, look for one
27292         if(!this.ownerTree){
27293             var p = this;
27294             while(p){
27295                 if(p.ownerTree){
27296                     this.ownerTree = p.ownerTree;
27297                     break;
27298                 }
27299                 p = p.parentNode;
27300             }
27301         }
27302         return this.ownerTree;
27303     },
27304
27305     /**
27306      * Returns depth of this node (the root node has a depth of 0)
27307      * @return {Number}
27308      */
27309     getDepth : function(){
27310         var depth = 0;
27311         var p = this;
27312         while(p.parentNode){
27313             ++depth;
27314             p = p.parentNode;
27315         }
27316         return depth;
27317     },
27318
27319     // private
27320     setOwnerTree : function(tree){
27321         // if it's move, we need to update everyone
27322         if(tree != this.ownerTree){
27323             if(this.ownerTree){
27324                 this.ownerTree.unregisterNode(this);
27325             }
27326             this.ownerTree = tree;
27327             var cs = this.childNodes;
27328             for(var i = 0, len = cs.length; i < len; i++) {
27329                 cs[i].setOwnerTree(tree);
27330             }
27331             if(tree){
27332                 tree.registerNode(this);
27333             }
27334         }
27335     },
27336
27337     /**
27338      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27339      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27340      * @return {String} The path
27341      */
27342     getPath : function(attr){
27343         attr = attr || "id";
27344         var p = this.parentNode;
27345         var b = [this.attributes[attr]];
27346         while(p){
27347             b.unshift(p.attributes[attr]);
27348             p = p.parentNode;
27349         }
27350         var sep = this.getOwnerTree().pathSeparator;
27351         return sep + b.join(sep);
27352     },
27353
27354     /**
27355      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27356      * function call will be the scope provided or the current node. The arguments to the function
27357      * will be the args provided or the current node. If the function returns false at any point,
27358      * the bubble is stopped.
27359      * @param {Function} fn The function to call
27360      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27361      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27362      */
27363     bubble : function(fn, scope, args){
27364         var p = this;
27365         while(p){
27366             if(fn.call(scope || p, args || p) === false){
27367                 break;
27368             }
27369             p = p.parentNode;
27370         }
27371     },
27372
27373     /**
27374      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27375      * function call will be the scope provided or the current node. The arguments to the function
27376      * will be the args provided or the current node. If the function returns false at any point,
27377      * the cascade is stopped on that branch.
27378      * @param {Function} fn The function to call
27379      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27380      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27381      */
27382     cascade : function(fn, scope, args){
27383         if(fn.call(scope || this, args || this) !== false){
27384             var cs = this.childNodes;
27385             for(var i = 0, len = cs.length; i < len; i++) {
27386                 cs[i].cascade(fn, scope, args);
27387             }
27388         }
27389     },
27390
27391     /**
27392      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27393      * function call will be the scope provided or the current node. The arguments to the function
27394      * will be the args provided or the current node. If the function returns false at any point,
27395      * the iteration stops.
27396      * @param {Function} fn The function to call
27397      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27398      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27399      */
27400     eachChild : function(fn, scope, args){
27401         var cs = this.childNodes;
27402         for(var i = 0, len = cs.length; i < len; i++) {
27403                 if(fn.call(scope || this, args || cs[i]) === false){
27404                     break;
27405                 }
27406         }
27407     },
27408
27409     /**
27410      * Finds the first child that has the attribute with the specified value.
27411      * @param {String} attribute The attribute name
27412      * @param {Mixed} value The value to search for
27413      * @return {Node} The found child or null if none was found
27414      */
27415     findChild : function(attribute, value){
27416         var cs = this.childNodes;
27417         for(var i = 0, len = cs.length; i < len; i++) {
27418                 if(cs[i].attributes[attribute] == value){
27419                     return cs[i];
27420                 }
27421         }
27422         return null;
27423     },
27424
27425     /**
27426      * Finds the first child by a custom function. The child matches if the function passed
27427      * returns true.
27428      * @param {Function} fn
27429      * @param {Object} scope (optional)
27430      * @return {Node} The found child or null if none was found
27431      */
27432     findChildBy : function(fn, scope){
27433         var cs = this.childNodes;
27434         for(var i = 0, len = cs.length; i < len; i++) {
27435                 if(fn.call(scope||cs[i], cs[i]) === true){
27436                     return cs[i];
27437                 }
27438         }
27439         return null;
27440     },
27441
27442     /**
27443      * Sorts this nodes children using the supplied sort function
27444      * @param {Function} fn
27445      * @param {Object} scope (optional)
27446      */
27447     sort : function(fn, scope){
27448         var cs = this.childNodes;
27449         var len = cs.length;
27450         if(len > 0){
27451             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27452             cs.sort(sortFn);
27453             for(var i = 0; i < len; i++){
27454                 var n = cs[i];
27455                 n.previousSibling = cs[i-1];
27456                 n.nextSibling = cs[i+1];
27457                 if(i == 0){
27458                     this.setFirstChild(n);
27459                 }
27460                 if(i == len-1){
27461                     this.setLastChild(n);
27462                 }
27463             }
27464         }
27465     },
27466
27467     /**
27468      * Returns true if this node is an ancestor (at any point) of the passed node.
27469      * @param {Node} node
27470      * @return {Boolean}
27471      */
27472     contains : function(node){
27473         return node.isAncestor(this);
27474     },
27475
27476     /**
27477      * Returns true if the passed node is an ancestor (at any point) of this node.
27478      * @param {Node} node
27479      * @return {Boolean}
27480      */
27481     isAncestor : function(node){
27482         var p = this.parentNode;
27483         while(p){
27484             if(p == node){
27485                 return true;
27486             }
27487             p = p.parentNode;
27488         }
27489         return false;
27490     },
27491
27492     toString : function(){
27493         return "[Node"+(this.id?" "+this.id:"")+"]";
27494     }
27495 });/*
27496  * Based on:
27497  * Ext JS Library 1.1.1
27498  * Copyright(c) 2006-2007, Ext JS, LLC.
27499  *
27500  * Originally Released Under LGPL - original licence link has changed is not relivant.
27501  *
27502  * Fork - LGPL
27503  * <script type="text/javascript">
27504  */
27505
27506
27507 /**
27508  * @class Roo.Shadow
27509  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27510  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27511  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27512  * @constructor
27513  * Create a new Shadow
27514  * @param {Object} config The config object
27515  */
27516 Roo.Shadow = function(config){
27517     Roo.apply(this, config);
27518     if(typeof this.mode != "string"){
27519         this.mode = this.defaultMode;
27520     }
27521     var o = this.offset, a = {h: 0};
27522     var rad = Math.floor(this.offset/2);
27523     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27524         case "drop":
27525             a.w = 0;
27526             a.l = a.t = o;
27527             a.t -= 1;
27528             if(Roo.isIE){
27529                 a.l -= this.offset + rad;
27530                 a.t -= this.offset + rad;
27531                 a.w -= rad;
27532                 a.h -= rad;
27533                 a.t += 1;
27534             }
27535         break;
27536         case "sides":
27537             a.w = (o*2);
27538             a.l = -o;
27539             a.t = o-1;
27540             if(Roo.isIE){
27541                 a.l -= (this.offset - rad);
27542                 a.t -= this.offset + rad;
27543                 a.l += 1;
27544                 a.w -= (this.offset - rad)*2;
27545                 a.w -= rad + 1;
27546                 a.h -= 1;
27547             }
27548         break;
27549         case "frame":
27550             a.w = a.h = (o*2);
27551             a.l = a.t = -o;
27552             a.t += 1;
27553             a.h -= 2;
27554             if(Roo.isIE){
27555                 a.l -= (this.offset - rad);
27556                 a.t -= (this.offset - rad);
27557                 a.l += 1;
27558                 a.w -= (this.offset + rad + 1);
27559                 a.h -= (this.offset + rad);
27560                 a.h += 1;
27561             }
27562         break;
27563     };
27564
27565     this.adjusts = a;
27566 };
27567
27568 Roo.Shadow.prototype = {
27569     /**
27570      * @cfg {String} mode
27571      * The shadow display mode.  Supports the following options:<br />
27572      * sides: Shadow displays on both sides and bottom only<br />
27573      * frame: Shadow displays equally on all four sides<br />
27574      * drop: Traditional bottom-right drop shadow (default)
27575      */
27576     mode: false,
27577     /**
27578      * @cfg {String} offset
27579      * The number of pixels to offset the shadow from the element (defaults to 4)
27580      */
27581     offset: 4,
27582
27583     // private
27584     defaultMode: "drop",
27585
27586     /**
27587      * Displays the shadow under the target element
27588      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27589      */
27590     show : function(target){
27591         target = Roo.get(target);
27592         if(!this.el){
27593             this.el = Roo.Shadow.Pool.pull();
27594             if(this.el.dom.nextSibling != target.dom){
27595                 this.el.insertBefore(target);
27596             }
27597         }
27598         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27599         if(Roo.isIE){
27600             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27601         }
27602         this.realign(
27603             target.getLeft(true),
27604             target.getTop(true),
27605             target.getWidth(),
27606             target.getHeight()
27607         );
27608         this.el.dom.style.display = "block";
27609     },
27610
27611     /**
27612      * Returns true if the shadow is visible, else false
27613      */
27614     isVisible : function(){
27615         return this.el ? true : false;  
27616     },
27617
27618     /**
27619      * Direct alignment when values are already available. Show must be called at least once before
27620      * calling this method to ensure it is initialized.
27621      * @param {Number} left The target element left position
27622      * @param {Number} top The target element top position
27623      * @param {Number} width The target element width
27624      * @param {Number} height The target element height
27625      */
27626     realign : function(l, t, w, h){
27627         if(!this.el){
27628             return;
27629         }
27630         var a = this.adjusts, d = this.el.dom, s = d.style;
27631         var iea = 0;
27632         s.left = (l+a.l)+"px";
27633         s.top = (t+a.t)+"px";
27634         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27635  
27636         if(s.width != sws || s.height != shs){
27637             s.width = sws;
27638             s.height = shs;
27639             if(!Roo.isIE){
27640                 var cn = d.childNodes;
27641                 var sww = Math.max(0, (sw-12))+"px";
27642                 cn[0].childNodes[1].style.width = sww;
27643                 cn[1].childNodes[1].style.width = sww;
27644                 cn[2].childNodes[1].style.width = sww;
27645                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27646             }
27647         }
27648     },
27649
27650     /**
27651      * Hides this shadow
27652      */
27653     hide : function(){
27654         if(this.el){
27655             this.el.dom.style.display = "none";
27656             Roo.Shadow.Pool.push(this.el);
27657             delete this.el;
27658         }
27659     },
27660
27661     /**
27662      * Adjust the z-index of this shadow
27663      * @param {Number} zindex The new z-index
27664      */
27665     setZIndex : function(z){
27666         this.zIndex = z;
27667         if(this.el){
27668             this.el.setStyle("z-index", z);
27669         }
27670     }
27671 };
27672
27673 // Private utility class that manages the internal Shadow cache
27674 Roo.Shadow.Pool = function(){
27675     var p = [];
27676     var markup = Roo.isIE ?
27677                  '<div class="x-ie-shadow"></div>' :
27678                  '<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>';
27679     return {
27680         pull : function(){
27681             var sh = p.shift();
27682             if(!sh){
27683                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27684                 sh.autoBoxAdjust = false;
27685             }
27686             return sh;
27687         },
27688
27689         push : function(sh){
27690             p.push(sh);
27691         }
27692     };
27693 }();/*
27694  * Based on:
27695  * Ext JS Library 1.1.1
27696  * Copyright(c) 2006-2007, Ext JS, LLC.
27697  *
27698  * Originally Released Under LGPL - original licence link has changed is not relivant.
27699  *
27700  * Fork - LGPL
27701  * <script type="text/javascript">
27702  */
27703
27704
27705 /**
27706  * @class Roo.SplitBar
27707  * @extends Roo.util.Observable
27708  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27709  * <br><br>
27710  * Usage:
27711  * <pre><code>
27712 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27713                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27714 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27715 split.minSize = 100;
27716 split.maxSize = 600;
27717 split.animate = true;
27718 split.on('moved', splitterMoved);
27719 </code></pre>
27720  * @constructor
27721  * Create a new SplitBar
27722  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27723  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27724  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27725  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27726                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27727                         position of the SplitBar).
27728  */
27729 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27730     
27731     /** @private */
27732     this.el = Roo.get(dragElement, true);
27733     this.el.dom.unselectable = "on";
27734     /** @private */
27735     this.resizingEl = Roo.get(resizingElement, true);
27736
27737     /**
27738      * @private
27739      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27740      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27741      * @type Number
27742      */
27743     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27744     
27745     /**
27746      * The minimum size of the resizing element. (Defaults to 0)
27747      * @type Number
27748      */
27749     this.minSize = 0;
27750     
27751     /**
27752      * The maximum size of the resizing element. (Defaults to 2000)
27753      * @type Number
27754      */
27755     this.maxSize = 2000;
27756     
27757     /**
27758      * Whether to animate the transition to the new size
27759      * @type Boolean
27760      */
27761     this.animate = false;
27762     
27763     /**
27764      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27765      * @type Boolean
27766      */
27767     this.useShim = false;
27768     
27769     /** @private */
27770     this.shim = null;
27771     
27772     if(!existingProxy){
27773         /** @private */
27774         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27775     }else{
27776         this.proxy = Roo.get(existingProxy).dom;
27777     }
27778     /** @private */
27779     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27780     
27781     /** @private */
27782     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27783     
27784     /** @private */
27785     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27786     
27787     /** @private */
27788     this.dragSpecs = {};
27789     
27790     /**
27791      * @private The adapter to use to positon and resize elements
27792      */
27793     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27794     this.adapter.init(this);
27795     
27796     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27797         /** @private */
27798         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27799         this.el.addClass("x-splitbar-h");
27800     }else{
27801         /** @private */
27802         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27803         this.el.addClass("x-splitbar-v");
27804     }
27805     
27806     this.addEvents({
27807         /**
27808          * @event resize
27809          * Fires when the splitter is moved (alias for {@link #event-moved})
27810          * @param {Roo.SplitBar} this
27811          * @param {Number} newSize the new width or height
27812          */
27813         "resize" : true,
27814         /**
27815          * @event moved
27816          * Fires when the splitter is moved
27817          * @param {Roo.SplitBar} this
27818          * @param {Number} newSize the new width or height
27819          */
27820         "moved" : true,
27821         /**
27822          * @event beforeresize
27823          * Fires before the splitter is dragged
27824          * @param {Roo.SplitBar} this
27825          */
27826         "beforeresize" : true,
27827
27828         "beforeapply" : true
27829     });
27830
27831     Roo.util.Observable.call(this);
27832 };
27833
27834 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27835     onStartProxyDrag : function(x, y){
27836         this.fireEvent("beforeresize", this);
27837         if(!this.overlay){
27838             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27839             o.unselectable();
27840             o.enableDisplayMode("block");
27841             // all splitbars share the same overlay
27842             Roo.SplitBar.prototype.overlay = o;
27843         }
27844         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27845         this.overlay.show();
27846         Roo.get(this.proxy).setDisplayed("block");
27847         var size = this.adapter.getElementSize(this);
27848         this.activeMinSize = this.getMinimumSize();;
27849         this.activeMaxSize = this.getMaximumSize();;
27850         var c1 = size - this.activeMinSize;
27851         var c2 = Math.max(this.activeMaxSize - size, 0);
27852         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27853             this.dd.resetConstraints();
27854             this.dd.setXConstraint(
27855                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27856                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27857             );
27858             this.dd.setYConstraint(0, 0);
27859         }else{
27860             this.dd.resetConstraints();
27861             this.dd.setXConstraint(0, 0);
27862             this.dd.setYConstraint(
27863                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27864                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27865             );
27866          }
27867         this.dragSpecs.startSize = size;
27868         this.dragSpecs.startPoint = [x, y];
27869         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27870     },
27871     
27872     /** 
27873      * @private Called after the drag operation by the DDProxy
27874      */
27875     onEndProxyDrag : function(e){
27876         Roo.get(this.proxy).setDisplayed(false);
27877         var endPoint = Roo.lib.Event.getXY(e);
27878         if(this.overlay){
27879             this.overlay.hide();
27880         }
27881         var newSize;
27882         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27883             newSize = this.dragSpecs.startSize + 
27884                 (this.placement == Roo.SplitBar.LEFT ?
27885                     endPoint[0] - this.dragSpecs.startPoint[0] :
27886                     this.dragSpecs.startPoint[0] - endPoint[0]
27887                 );
27888         }else{
27889             newSize = this.dragSpecs.startSize + 
27890                 (this.placement == Roo.SplitBar.TOP ?
27891                     endPoint[1] - this.dragSpecs.startPoint[1] :
27892                     this.dragSpecs.startPoint[1] - endPoint[1]
27893                 );
27894         }
27895         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27896         if(newSize != this.dragSpecs.startSize){
27897             if(this.fireEvent('beforeapply', this, newSize) !== false){
27898                 this.adapter.setElementSize(this, newSize);
27899                 this.fireEvent("moved", this, newSize);
27900                 this.fireEvent("resize", this, newSize);
27901             }
27902         }
27903     },
27904     
27905     /**
27906      * Get the adapter this SplitBar uses
27907      * @return The adapter object
27908      */
27909     getAdapter : function(){
27910         return this.adapter;
27911     },
27912     
27913     /**
27914      * Set the adapter this SplitBar uses
27915      * @param {Object} adapter A SplitBar adapter object
27916      */
27917     setAdapter : function(adapter){
27918         this.adapter = adapter;
27919         this.adapter.init(this);
27920     },
27921     
27922     /**
27923      * Gets the minimum size for the resizing element
27924      * @return {Number} The minimum size
27925      */
27926     getMinimumSize : function(){
27927         return this.minSize;
27928     },
27929     
27930     /**
27931      * Sets the minimum size for the resizing element
27932      * @param {Number} minSize The minimum size
27933      */
27934     setMinimumSize : function(minSize){
27935         this.minSize = minSize;
27936     },
27937     
27938     /**
27939      * Gets the maximum size for the resizing element
27940      * @return {Number} The maximum size
27941      */
27942     getMaximumSize : function(){
27943         return this.maxSize;
27944     },
27945     
27946     /**
27947      * Sets the maximum size for the resizing element
27948      * @param {Number} maxSize The maximum size
27949      */
27950     setMaximumSize : function(maxSize){
27951         this.maxSize = maxSize;
27952     },
27953     
27954     /**
27955      * Sets the initialize size for the resizing element
27956      * @param {Number} size The initial size
27957      */
27958     setCurrentSize : function(size){
27959         var oldAnimate = this.animate;
27960         this.animate = false;
27961         this.adapter.setElementSize(this, size);
27962         this.animate = oldAnimate;
27963     },
27964     
27965     /**
27966      * Destroy this splitbar. 
27967      * @param {Boolean} removeEl True to remove the element
27968      */
27969     destroy : function(removeEl){
27970         if(this.shim){
27971             this.shim.remove();
27972         }
27973         this.dd.unreg();
27974         this.proxy.parentNode.removeChild(this.proxy);
27975         if(removeEl){
27976             this.el.remove();
27977         }
27978     }
27979 });
27980
27981 /**
27982  * @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.
27983  */
27984 Roo.SplitBar.createProxy = function(dir){
27985     var proxy = new Roo.Element(document.createElement("div"));
27986     proxy.unselectable();
27987     var cls = 'x-splitbar-proxy';
27988     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27989     document.body.appendChild(proxy.dom);
27990     return proxy.dom;
27991 };
27992
27993 /** 
27994  * @class Roo.SplitBar.BasicLayoutAdapter
27995  * Default Adapter. It assumes the splitter and resizing element are not positioned
27996  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27997  */
27998 Roo.SplitBar.BasicLayoutAdapter = function(){
27999 };
28000
28001 Roo.SplitBar.BasicLayoutAdapter.prototype = {
28002     // do nothing for now
28003     init : function(s){
28004     
28005     },
28006     /**
28007      * Called before drag operations to get the current size of the resizing element. 
28008      * @param {Roo.SplitBar} s The SplitBar using this adapter
28009      */
28010      getElementSize : function(s){
28011         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28012             return s.resizingEl.getWidth();
28013         }else{
28014             return s.resizingEl.getHeight();
28015         }
28016     },
28017     
28018     /**
28019      * Called after drag operations to set the size of the resizing element.
28020      * @param {Roo.SplitBar} s The SplitBar using this adapter
28021      * @param {Number} newSize The new size to set
28022      * @param {Function} onComplete A function to be invoked when resizing is complete
28023      */
28024     setElementSize : function(s, newSize, onComplete){
28025         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28026             if(!s.animate){
28027                 s.resizingEl.setWidth(newSize);
28028                 if(onComplete){
28029                     onComplete(s, newSize);
28030                 }
28031             }else{
28032                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28033             }
28034         }else{
28035             
28036             if(!s.animate){
28037                 s.resizingEl.setHeight(newSize);
28038                 if(onComplete){
28039                     onComplete(s, newSize);
28040                 }
28041             }else{
28042                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28043             }
28044         }
28045     }
28046 };
28047
28048 /** 
28049  *@class Roo.SplitBar.AbsoluteLayoutAdapter
28050  * @extends Roo.SplitBar.BasicLayoutAdapter
28051  * Adapter that  moves the splitter element to align with the resized sizing element. 
28052  * Used with an absolute positioned SplitBar.
28053  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28054  * document.body, make sure you assign an id to the body element.
28055  */
28056 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28057     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28058     this.container = Roo.get(container);
28059 };
28060
28061 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28062     init : function(s){
28063         this.basic.init(s);
28064     },
28065     
28066     getElementSize : function(s){
28067         return this.basic.getElementSize(s);
28068     },
28069     
28070     setElementSize : function(s, newSize, onComplete){
28071         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28072     },
28073     
28074     moveSplitter : function(s){
28075         var yes = Roo.SplitBar;
28076         switch(s.placement){
28077             case yes.LEFT:
28078                 s.el.setX(s.resizingEl.getRight());
28079                 break;
28080             case yes.RIGHT:
28081                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28082                 break;
28083             case yes.TOP:
28084                 s.el.setY(s.resizingEl.getBottom());
28085                 break;
28086             case yes.BOTTOM:
28087                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28088                 break;
28089         }
28090     }
28091 };
28092
28093 /**
28094  * Orientation constant - Create a vertical SplitBar
28095  * @static
28096  * @type Number
28097  */
28098 Roo.SplitBar.VERTICAL = 1;
28099
28100 /**
28101  * Orientation constant - Create a horizontal SplitBar
28102  * @static
28103  * @type Number
28104  */
28105 Roo.SplitBar.HORIZONTAL = 2;
28106
28107 /**
28108  * Placement constant - The resizing element is to the left of the splitter element
28109  * @static
28110  * @type Number
28111  */
28112 Roo.SplitBar.LEFT = 1;
28113
28114 /**
28115  * Placement constant - The resizing element is to the right of the splitter element
28116  * @static
28117  * @type Number
28118  */
28119 Roo.SplitBar.RIGHT = 2;
28120
28121 /**
28122  * Placement constant - The resizing element is positioned above the splitter element
28123  * @static
28124  * @type Number
28125  */
28126 Roo.SplitBar.TOP = 3;
28127
28128 /**
28129  * Placement constant - The resizing element is positioned under splitter element
28130  * @static
28131  * @type Number
28132  */
28133 Roo.SplitBar.BOTTOM = 4;
28134 /*
28135  * Based on:
28136  * Ext JS Library 1.1.1
28137  * Copyright(c) 2006-2007, Ext JS, LLC.
28138  *
28139  * Originally Released Under LGPL - original licence link has changed is not relivant.
28140  *
28141  * Fork - LGPL
28142  * <script type="text/javascript">
28143  */
28144
28145 /**
28146  * @class Roo.View
28147  * @extends Roo.util.Observable
28148  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
28149  * This class also supports single and multi selection modes. <br>
28150  * Create a data model bound view:
28151  <pre><code>
28152  var store = new Roo.data.Store(...);
28153
28154  var view = new Roo.View({
28155     el : "my-element",
28156     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
28157  
28158     singleSelect: true,
28159     selectedClass: "ydataview-selected",
28160     store: store
28161  });
28162
28163  // listen for node click?
28164  view.on("click", function(vw, index, node, e){
28165  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28166  });
28167
28168  // load XML data
28169  dataModel.load("foobar.xml");
28170  </code></pre>
28171  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28172  * <br><br>
28173  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28174  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28175  * 
28176  * Note: old style constructor is still suported (container, template, config)
28177  * 
28178  * @constructor
28179  * Create a new View
28180  * @param {Object} config The config object
28181  * 
28182  */
28183 Roo.View = function(config, depreciated_tpl, depreciated_config){
28184     
28185     this.parent = false;
28186     
28187     if (typeof(depreciated_tpl) == 'undefined') {
28188         // new way.. - universal constructor.
28189         Roo.apply(this, config);
28190         this.el  = Roo.get(this.el);
28191     } else {
28192         // old format..
28193         this.el  = Roo.get(config);
28194         this.tpl = depreciated_tpl;
28195         Roo.apply(this, depreciated_config);
28196     }
28197     this.wrapEl  = this.el.wrap().wrap();
28198     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28199     
28200     
28201     if(typeof(this.tpl) == "string"){
28202         this.tpl = new Roo.Template(this.tpl);
28203     } else {
28204         // support xtype ctors..
28205         this.tpl = new Roo.factory(this.tpl, Roo);
28206     }
28207     
28208     
28209     this.tpl.compile();
28210     
28211     /** @private */
28212     this.addEvents({
28213         /**
28214          * @event beforeclick
28215          * Fires before a click is processed. Returns false to cancel the default action.
28216          * @param {Roo.View} this
28217          * @param {Number} index The index of the target node
28218          * @param {HTMLElement} node The target node
28219          * @param {Roo.EventObject} e The raw event object
28220          */
28221             "beforeclick" : true,
28222         /**
28223          * @event click
28224          * Fires when a template node is clicked.
28225          * @param {Roo.View} this
28226          * @param {Number} index The index of the target node
28227          * @param {HTMLElement} node The target node
28228          * @param {Roo.EventObject} e The raw event object
28229          */
28230             "click" : true,
28231         /**
28232          * @event dblclick
28233          * Fires when a template node is double clicked.
28234          * @param {Roo.View} this
28235          * @param {Number} index The index of the target node
28236          * @param {HTMLElement} node The target node
28237          * @param {Roo.EventObject} e The raw event object
28238          */
28239             "dblclick" : true,
28240         /**
28241          * @event contextmenu
28242          * Fires when a template node is right clicked.
28243          * @param {Roo.View} this
28244          * @param {Number} index The index of the target node
28245          * @param {HTMLElement} node The target node
28246          * @param {Roo.EventObject} e The raw event object
28247          */
28248             "contextmenu" : true,
28249         /**
28250          * @event selectionchange
28251          * Fires when the selected nodes change.
28252          * @param {Roo.View} this
28253          * @param {Array} selections Array of the selected nodes
28254          */
28255             "selectionchange" : true,
28256     
28257         /**
28258          * @event beforeselect
28259          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28260          * @param {Roo.View} this
28261          * @param {HTMLElement} node The node to be selected
28262          * @param {Array} selections Array of currently selected nodes
28263          */
28264             "beforeselect" : true,
28265         /**
28266          * @event preparedata
28267          * Fires on every row to render, to allow you to change the data.
28268          * @param {Roo.View} this
28269          * @param {Object} data to be rendered (change this)
28270          */
28271           "preparedata" : true
28272           
28273           
28274         });
28275
28276
28277
28278     this.el.on({
28279         "click": this.onClick,
28280         "dblclick": this.onDblClick,
28281         "contextmenu": this.onContextMenu,
28282         scope:this
28283     });
28284
28285     this.selections = [];
28286     this.nodes = [];
28287     this.cmp = new Roo.CompositeElementLite([]);
28288     if(this.store){
28289         this.store = Roo.factory(this.store, Roo.data);
28290         this.setStore(this.store, true);
28291     }
28292     
28293     if ( this.footer && this.footer.xtype) {
28294            
28295          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28296         
28297         this.footer.dataSource = this.store;
28298         this.footer.container = fctr;
28299         this.footer = Roo.factory(this.footer, Roo);
28300         fctr.insertFirst(this.el);
28301         
28302         // this is a bit insane - as the paging toolbar seems to detach the el..
28303 //        dom.parentNode.parentNode.parentNode
28304          // they get detached?
28305     }
28306     
28307     
28308     Roo.View.superclass.constructor.call(this);
28309     
28310     
28311 };
28312
28313 Roo.extend(Roo.View, Roo.util.Observable, {
28314     
28315      /**
28316      * @cfg {Roo.data.Store} store Data store to load data from.
28317      */
28318     store : false,
28319     
28320     /**
28321      * @cfg {String|Roo.Element} el The container element.
28322      */
28323     el : '',
28324     
28325     /**
28326      * @cfg {String|Roo.Template} tpl The template used by this View 
28327      */
28328     tpl : false,
28329     /**
28330      * @cfg {String} dataName the named area of the template to use as the data area
28331      *                          Works with domtemplates roo-name="name"
28332      */
28333     dataName: false,
28334     /**
28335      * @cfg {String} selectedClass The css class to add to selected nodes
28336      */
28337     selectedClass : "x-view-selected",
28338      /**
28339      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28340      */
28341     emptyText : "",
28342     
28343     /**
28344      * @cfg {String} text to display on mask (default Loading)
28345      */
28346     mask : false,
28347     /**
28348      * @cfg {Boolean} multiSelect Allow multiple selection
28349      */
28350     multiSelect : false,
28351     /**
28352      * @cfg {Boolean} singleSelect Allow single selection
28353      */
28354     singleSelect:  false,
28355     
28356     /**
28357      * @cfg {Boolean} toggleSelect - selecting 
28358      */
28359     toggleSelect : false,
28360     
28361     /**
28362      * @cfg {Boolean} tickable - selecting 
28363      */
28364     tickable : false,
28365     
28366     /**
28367      * Returns the element this view is bound to.
28368      * @return {Roo.Element}
28369      */
28370     getEl : function(){
28371         return this.wrapEl;
28372     },
28373     
28374     
28375
28376     /**
28377      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28378      */
28379     refresh : function(){
28380         //Roo.log('refresh');
28381         var t = this.tpl;
28382         
28383         // if we are using something like 'domtemplate', then
28384         // the what gets used is:
28385         // t.applySubtemplate(NAME, data, wrapping data..)
28386         // the outer template then get' applied with
28387         //     the store 'extra data'
28388         // and the body get's added to the
28389         //      roo-name="data" node?
28390         //      <span class='roo-tpl-{name}'></span> ?????
28391         
28392         
28393         
28394         this.clearSelections();
28395         this.el.update("");
28396         var html = [];
28397         var records = this.store.getRange();
28398         if(records.length < 1) {
28399             
28400             // is this valid??  = should it render a template??
28401             
28402             this.el.update(this.emptyText);
28403             return;
28404         }
28405         var el = this.el;
28406         if (this.dataName) {
28407             this.el.update(t.apply(this.store.meta)); //????
28408             el = this.el.child('.roo-tpl-' + this.dataName);
28409         }
28410         
28411         for(var i = 0, len = records.length; i < len; i++){
28412             var data = this.prepareData(records[i].data, i, records[i]);
28413             this.fireEvent("preparedata", this, data, i, records[i]);
28414             
28415             var d = Roo.apply({}, data);
28416             
28417             if(this.tickable){
28418                 Roo.apply(d, {'roo-id' : Roo.id()});
28419                 
28420                 var _this = this;
28421             
28422                 Roo.each(this.parent.item, function(item){
28423                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28424                         return;
28425                     }
28426                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28427                 });
28428             }
28429             
28430             html[html.length] = Roo.util.Format.trim(
28431                 this.dataName ?
28432                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28433                     t.apply(d)
28434             );
28435         }
28436         
28437         
28438         
28439         el.update(html.join(""));
28440         this.nodes = el.dom.childNodes;
28441         this.updateIndexes(0);
28442     },
28443     
28444
28445     /**
28446      * Function to override to reformat the data that is sent to
28447      * the template for each node.
28448      * DEPRICATED - use the preparedata event handler.
28449      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28450      * a JSON object for an UpdateManager bound view).
28451      */
28452     prepareData : function(data, index, record)
28453     {
28454         this.fireEvent("preparedata", this, data, index, record);
28455         return data;
28456     },
28457
28458     onUpdate : function(ds, record){
28459         // Roo.log('on update');   
28460         this.clearSelections();
28461         var index = this.store.indexOf(record);
28462         var n = this.nodes[index];
28463         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28464         n.parentNode.removeChild(n);
28465         this.updateIndexes(index, index);
28466     },
28467
28468     
28469     
28470 // --------- FIXME     
28471     onAdd : function(ds, records, index)
28472     {
28473         //Roo.log(['on Add', ds, records, index] );        
28474         this.clearSelections();
28475         if(this.nodes.length == 0){
28476             this.refresh();
28477             return;
28478         }
28479         var n = this.nodes[index];
28480         for(var i = 0, len = records.length; i < len; i++){
28481             var d = this.prepareData(records[i].data, i, records[i]);
28482             if(n){
28483                 this.tpl.insertBefore(n, d);
28484             }else{
28485                 
28486                 this.tpl.append(this.el, d);
28487             }
28488         }
28489         this.updateIndexes(index);
28490     },
28491
28492     onRemove : function(ds, record, index){
28493        // Roo.log('onRemove');
28494         this.clearSelections();
28495         var el = this.dataName  ?
28496             this.el.child('.roo-tpl-' + this.dataName) :
28497             this.el; 
28498         
28499         el.dom.removeChild(this.nodes[index]);
28500         this.updateIndexes(index);
28501     },
28502
28503     /**
28504      * Refresh an individual node.
28505      * @param {Number} index
28506      */
28507     refreshNode : function(index){
28508         this.onUpdate(this.store, this.store.getAt(index));
28509     },
28510
28511     updateIndexes : function(startIndex, endIndex){
28512         var ns = this.nodes;
28513         startIndex = startIndex || 0;
28514         endIndex = endIndex || ns.length - 1;
28515         for(var i = startIndex; i <= endIndex; i++){
28516             ns[i].nodeIndex = i;
28517         }
28518     },
28519
28520     /**
28521      * Changes the data store this view uses and refresh the view.
28522      * @param {Store} store
28523      */
28524     setStore : function(store, initial){
28525         if(!initial && this.store){
28526             this.store.un("datachanged", this.refresh);
28527             this.store.un("add", this.onAdd);
28528             this.store.un("remove", this.onRemove);
28529             this.store.un("update", this.onUpdate);
28530             this.store.un("clear", this.refresh);
28531             this.store.un("beforeload", this.onBeforeLoad);
28532             this.store.un("load", this.onLoad);
28533             this.store.un("loadexception", this.onLoad);
28534         }
28535         if(store){
28536           
28537             store.on("datachanged", this.refresh, this);
28538             store.on("add", this.onAdd, this);
28539             store.on("remove", this.onRemove, this);
28540             store.on("update", this.onUpdate, this);
28541             store.on("clear", this.refresh, this);
28542             store.on("beforeload", this.onBeforeLoad, this);
28543             store.on("load", this.onLoad, this);
28544             store.on("loadexception", this.onLoad, this);
28545         }
28546         
28547         if(store){
28548             this.refresh();
28549         }
28550     },
28551     /**
28552      * onbeforeLoad - masks the loading area.
28553      *
28554      */
28555     onBeforeLoad : function(store,opts)
28556     {
28557          //Roo.log('onBeforeLoad');   
28558         if (!opts.add) {
28559             this.el.update("");
28560         }
28561         this.el.mask(this.mask ? this.mask : "Loading" ); 
28562     },
28563     onLoad : function ()
28564     {
28565         this.el.unmask();
28566     },
28567     
28568
28569     /**
28570      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28571      * @param {HTMLElement} node
28572      * @return {HTMLElement} The template node
28573      */
28574     findItemFromChild : function(node){
28575         var el = this.dataName  ?
28576             this.el.child('.roo-tpl-' + this.dataName,true) :
28577             this.el.dom; 
28578         
28579         if(!node || node.parentNode == el){
28580                     return node;
28581             }
28582             var p = node.parentNode;
28583             while(p && p != el){
28584             if(p.parentNode == el){
28585                 return p;
28586             }
28587             p = p.parentNode;
28588         }
28589             return null;
28590     },
28591
28592     /** @ignore */
28593     onClick : function(e){
28594         var item = this.findItemFromChild(e.getTarget());
28595         if(item){
28596             var index = this.indexOf(item);
28597             if(this.onItemClick(item, index, e) !== false){
28598                 this.fireEvent("click", this, index, item, e);
28599             }
28600         }else{
28601             this.clearSelections();
28602         }
28603     },
28604
28605     /** @ignore */
28606     onContextMenu : function(e){
28607         var item = this.findItemFromChild(e.getTarget());
28608         if(item){
28609             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28610         }
28611     },
28612
28613     /** @ignore */
28614     onDblClick : function(e){
28615         var item = this.findItemFromChild(e.getTarget());
28616         if(item){
28617             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28618         }
28619     },
28620
28621     onItemClick : function(item, index, e)
28622     {
28623         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28624             return false;
28625         }
28626         if (this.toggleSelect) {
28627             var m = this.isSelected(item) ? 'unselect' : 'select';
28628             //Roo.log(m);
28629             var _t = this;
28630             _t[m](item, true, false);
28631             return true;
28632         }
28633         if(this.multiSelect || this.singleSelect){
28634             if(this.multiSelect && e.shiftKey && this.lastSelection){
28635                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28636             }else{
28637                 this.select(item, this.multiSelect && e.ctrlKey);
28638                 this.lastSelection = item;
28639             }
28640             
28641             if(!this.tickable){
28642                 e.preventDefault();
28643             }
28644             
28645         }
28646         return true;
28647     },
28648
28649     /**
28650      * Get the number of selected nodes.
28651      * @return {Number}
28652      */
28653     getSelectionCount : function(){
28654         return this.selections.length;
28655     },
28656
28657     /**
28658      * Get the currently selected nodes.
28659      * @return {Array} An array of HTMLElements
28660      */
28661     getSelectedNodes : function(){
28662         return this.selections;
28663     },
28664
28665     /**
28666      * Get the indexes of the selected nodes.
28667      * @return {Array}
28668      */
28669     getSelectedIndexes : function(){
28670         var indexes = [], s = this.selections;
28671         for(var i = 0, len = s.length; i < len; i++){
28672             indexes.push(s[i].nodeIndex);
28673         }
28674         return indexes;
28675     },
28676
28677     /**
28678      * Clear all selections
28679      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28680      */
28681     clearSelections : function(suppressEvent){
28682         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28683             this.cmp.elements = this.selections;
28684             this.cmp.removeClass(this.selectedClass);
28685             this.selections = [];
28686             if(!suppressEvent){
28687                 this.fireEvent("selectionchange", this, this.selections);
28688             }
28689         }
28690     },
28691
28692     /**
28693      * Returns true if the passed node is selected
28694      * @param {HTMLElement/Number} node The node or node index
28695      * @return {Boolean}
28696      */
28697     isSelected : function(node){
28698         var s = this.selections;
28699         if(s.length < 1){
28700             return false;
28701         }
28702         node = this.getNode(node);
28703         return s.indexOf(node) !== -1;
28704     },
28705
28706     /**
28707      * Selects nodes.
28708      * @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
28709      * @param {Boolean} keepExisting (optional) true to keep existing selections
28710      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28711      */
28712     select : function(nodeInfo, keepExisting, suppressEvent){
28713         if(nodeInfo instanceof Array){
28714             if(!keepExisting){
28715                 this.clearSelections(true);
28716             }
28717             for(var i = 0, len = nodeInfo.length; i < len; i++){
28718                 this.select(nodeInfo[i], true, true);
28719             }
28720             return;
28721         } 
28722         var node = this.getNode(nodeInfo);
28723         if(!node || this.isSelected(node)){
28724             return; // already selected.
28725         }
28726         if(!keepExisting){
28727             this.clearSelections(true);
28728         }
28729         
28730         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28731             Roo.fly(node).addClass(this.selectedClass);
28732             this.selections.push(node);
28733             if(!suppressEvent){
28734                 this.fireEvent("selectionchange", this, this.selections);
28735             }
28736         }
28737         
28738         
28739     },
28740       /**
28741      * Unselects nodes.
28742      * @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
28743      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28744      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28745      */
28746     unselect : function(nodeInfo, keepExisting, suppressEvent)
28747     {
28748         if(nodeInfo instanceof Array){
28749             Roo.each(this.selections, function(s) {
28750                 this.unselect(s, nodeInfo);
28751             }, this);
28752             return;
28753         }
28754         var node = this.getNode(nodeInfo);
28755         if(!node || !this.isSelected(node)){
28756             //Roo.log("not selected");
28757             return; // not selected.
28758         }
28759         // fireevent???
28760         var ns = [];
28761         Roo.each(this.selections, function(s) {
28762             if (s == node ) {
28763                 Roo.fly(node).removeClass(this.selectedClass);
28764
28765                 return;
28766             }
28767             ns.push(s);
28768         },this);
28769         
28770         this.selections= ns;
28771         this.fireEvent("selectionchange", this, this.selections);
28772     },
28773
28774     /**
28775      * Gets a template node.
28776      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28777      * @return {HTMLElement} The node or null if it wasn't found
28778      */
28779     getNode : function(nodeInfo){
28780         if(typeof nodeInfo == "string"){
28781             return document.getElementById(nodeInfo);
28782         }else if(typeof nodeInfo == "number"){
28783             return this.nodes[nodeInfo];
28784         }
28785         return nodeInfo;
28786     },
28787
28788     /**
28789      * Gets a range template nodes.
28790      * @param {Number} startIndex
28791      * @param {Number} endIndex
28792      * @return {Array} An array of nodes
28793      */
28794     getNodes : function(start, end){
28795         var ns = this.nodes;
28796         start = start || 0;
28797         end = typeof end == "undefined" ? ns.length - 1 : end;
28798         var nodes = [];
28799         if(start <= end){
28800             for(var i = start; i <= end; i++){
28801                 nodes.push(ns[i]);
28802             }
28803         } else{
28804             for(var i = start; i >= end; i--){
28805                 nodes.push(ns[i]);
28806             }
28807         }
28808         return nodes;
28809     },
28810
28811     /**
28812      * Finds the index of the passed node
28813      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28814      * @return {Number} The index of the node or -1
28815      */
28816     indexOf : function(node){
28817         node = this.getNode(node);
28818         if(typeof node.nodeIndex == "number"){
28819             return node.nodeIndex;
28820         }
28821         var ns = this.nodes;
28822         for(var i = 0, len = ns.length; i < len; i++){
28823             if(ns[i] == node){
28824                 return i;
28825             }
28826         }
28827         return -1;
28828     }
28829 });
28830 /*
28831  * Based on:
28832  * Ext JS Library 1.1.1
28833  * Copyright(c) 2006-2007, Ext JS, LLC.
28834  *
28835  * Originally Released Under LGPL - original licence link has changed is not relivant.
28836  *
28837  * Fork - LGPL
28838  * <script type="text/javascript">
28839  */
28840
28841 /**
28842  * @class Roo.JsonView
28843  * @extends Roo.View
28844  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28845 <pre><code>
28846 var view = new Roo.JsonView({
28847     container: "my-element",
28848     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28849     multiSelect: true, 
28850     jsonRoot: "data" 
28851 });
28852
28853 // listen for node click?
28854 view.on("click", function(vw, index, node, e){
28855     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28856 });
28857
28858 // direct load of JSON data
28859 view.load("foobar.php");
28860
28861 // Example from my blog list
28862 var tpl = new Roo.Template(
28863     '&lt;div class="entry"&gt;' +
28864     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28865     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28866     "&lt;/div&gt;&lt;hr /&gt;"
28867 );
28868
28869 var moreView = new Roo.JsonView({
28870     container :  "entry-list", 
28871     template : tpl,
28872     jsonRoot: "posts"
28873 });
28874 moreView.on("beforerender", this.sortEntries, this);
28875 moreView.load({
28876     url: "/blog/get-posts.php",
28877     params: "allposts=true",
28878     text: "Loading Blog Entries..."
28879 });
28880 </code></pre>
28881
28882 * Note: old code is supported with arguments : (container, template, config)
28883
28884
28885  * @constructor
28886  * Create a new JsonView
28887  * 
28888  * @param {Object} config The config object
28889  * 
28890  */
28891 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28892     
28893     
28894     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28895
28896     var um = this.el.getUpdateManager();
28897     um.setRenderer(this);
28898     um.on("update", this.onLoad, this);
28899     um.on("failure", this.onLoadException, this);
28900
28901     /**
28902      * @event beforerender
28903      * Fires before rendering of the downloaded JSON data.
28904      * @param {Roo.JsonView} this
28905      * @param {Object} data The JSON data loaded
28906      */
28907     /**
28908      * @event load
28909      * Fires when data is loaded.
28910      * @param {Roo.JsonView} this
28911      * @param {Object} data The JSON data loaded
28912      * @param {Object} response The raw Connect response object
28913      */
28914     /**
28915      * @event loadexception
28916      * Fires when loading fails.
28917      * @param {Roo.JsonView} this
28918      * @param {Object} response The raw Connect response object
28919      */
28920     this.addEvents({
28921         'beforerender' : true,
28922         'load' : true,
28923         'loadexception' : true
28924     });
28925 };
28926 Roo.extend(Roo.JsonView, Roo.View, {
28927     /**
28928      * @type {String} The root property in the loaded JSON object that contains the data
28929      */
28930     jsonRoot : "",
28931
28932     /**
28933      * Refreshes the view.
28934      */
28935     refresh : function(){
28936         this.clearSelections();
28937         this.el.update("");
28938         var html = [];
28939         var o = this.jsonData;
28940         if(o && o.length > 0){
28941             for(var i = 0, len = o.length; i < len; i++){
28942                 var data = this.prepareData(o[i], i, o);
28943                 html[html.length] = this.tpl.apply(data);
28944             }
28945         }else{
28946             html.push(this.emptyText);
28947         }
28948         this.el.update(html.join(""));
28949         this.nodes = this.el.dom.childNodes;
28950         this.updateIndexes(0);
28951     },
28952
28953     /**
28954      * 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.
28955      * @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:
28956      <pre><code>
28957      view.load({
28958          url: "your-url.php",
28959          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28960          callback: yourFunction,
28961          scope: yourObject, //(optional scope)
28962          discardUrl: false,
28963          nocache: false,
28964          text: "Loading...",
28965          timeout: 30,
28966          scripts: false
28967      });
28968      </code></pre>
28969      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28970      * 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.
28971      * @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}
28972      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28973      * @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.
28974      */
28975     load : function(){
28976         var um = this.el.getUpdateManager();
28977         um.update.apply(um, arguments);
28978     },
28979
28980     // note - render is a standard framework call...
28981     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28982     render : function(el, response){
28983         
28984         this.clearSelections();
28985         this.el.update("");
28986         var o;
28987         try{
28988             if (response != '') {
28989                 o = Roo.util.JSON.decode(response.responseText);
28990                 if(this.jsonRoot){
28991                     
28992                     o = o[this.jsonRoot];
28993                 }
28994             }
28995         } catch(e){
28996         }
28997         /**
28998          * The current JSON data or null
28999          */
29000         this.jsonData = o;
29001         this.beforeRender();
29002         this.refresh();
29003     },
29004
29005 /**
29006  * Get the number of records in the current JSON dataset
29007  * @return {Number}
29008  */
29009     getCount : function(){
29010         return this.jsonData ? this.jsonData.length : 0;
29011     },
29012
29013 /**
29014  * Returns the JSON object for the specified node(s)
29015  * @param {HTMLElement/Array} node The node or an array of nodes
29016  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
29017  * you get the JSON object for the node
29018  */
29019     getNodeData : function(node){
29020         if(node instanceof Array){
29021             var data = [];
29022             for(var i = 0, len = node.length; i < len; i++){
29023                 data.push(this.getNodeData(node[i]));
29024             }
29025             return data;
29026         }
29027         return this.jsonData[this.indexOf(node)] || null;
29028     },
29029
29030     beforeRender : function(){
29031         this.snapshot = this.jsonData;
29032         if(this.sortInfo){
29033             this.sort.apply(this, this.sortInfo);
29034         }
29035         this.fireEvent("beforerender", this, this.jsonData);
29036     },
29037
29038     onLoad : function(el, o){
29039         this.fireEvent("load", this, this.jsonData, o);
29040     },
29041
29042     onLoadException : function(el, o){
29043         this.fireEvent("loadexception", this, o);
29044     },
29045
29046 /**
29047  * Filter the data by a specific property.
29048  * @param {String} property A property on your JSON objects
29049  * @param {String/RegExp} value Either string that the property values
29050  * should start with, or a RegExp to test against the property
29051  */
29052     filter : function(property, value){
29053         if(this.jsonData){
29054             var data = [];
29055             var ss = this.snapshot;
29056             if(typeof value == "string"){
29057                 var vlen = value.length;
29058                 if(vlen == 0){
29059                     this.clearFilter();
29060                     return;
29061                 }
29062                 value = value.toLowerCase();
29063                 for(var i = 0, len = ss.length; i < len; i++){
29064                     var o = ss[i];
29065                     if(o[property].substr(0, vlen).toLowerCase() == value){
29066                         data.push(o);
29067                     }
29068                 }
29069             } else if(value.exec){ // regex?
29070                 for(var i = 0, len = ss.length; i < len; i++){
29071                     var o = ss[i];
29072                     if(value.test(o[property])){
29073                         data.push(o);
29074                     }
29075                 }
29076             } else{
29077                 return;
29078             }
29079             this.jsonData = data;
29080             this.refresh();
29081         }
29082     },
29083
29084 /**
29085  * Filter by a function. The passed function will be called with each
29086  * object in the current dataset. If the function returns true the value is kept,
29087  * otherwise it is filtered.
29088  * @param {Function} fn
29089  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29090  */
29091     filterBy : function(fn, scope){
29092         if(this.jsonData){
29093             var data = [];
29094             var ss = this.snapshot;
29095             for(var i = 0, len = ss.length; i < len; i++){
29096                 var o = ss[i];
29097                 if(fn.call(scope || this, o)){
29098                     data.push(o);
29099                 }
29100             }
29101             this.jsonData = data;
29102             this.refresh();
29103         }
29104     },
29105
29106 /**
29107  * Clears the current filter.
29108  */
29109     clearFilter : function(){
29110         if(this.snapshot && this.jsonData != this.snapshot){
29111             this.jsonData = this.snapshot;
29112             this.refresh();
29113         }
29114     },
29115
29116
29117 /**
29118  * Sorts the data for this view and refreshes it.
29119  * @param {String} property A property on your JSON objects to sort on
29120  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29121  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29122  */
29123     sort : function(property, dir, sortType){
29124         this.sortInfo = Array.prototype.slice.call(arguments, 0);
29125         if(this.jsonData){
29126             var p = property;
29127             var dsc = dir && dir.toLowerCase() == "desc";
29128             var f = function(o1, o2){
29129                 var v1 = sortType ? sortType(o1[p]) : o1[p];
29130                 var v2 = sortType ? sortType(o2[p]) : o2[p];
29131                 ;
29132                 if(v1 < v2){
29133                     return dsc ? +1 : -1;
29134                 } else if(v1 > v2){
29135                     return dsc ? -1 : +1;
29136                 } else{
29137                     return 0;
29138                 }
29139             };
29140             this.jsonData.sort(f);
29141             this.refresh();
29142             if(this.jsonData != this.snapshot){
29143                 this.snapshot.sort(f);
29144             }
29145         }
29146     }
29147 });/*
29148  * Based on:
29149  * Ext JS Library 1.1.1
29150  * Copyright(c) 2006-2007, Ext JS, LLC.
29151  *
29152  * Originally Released Under LGPL - original licence link has changed is not relivant.
29153  *
29154  * Fork - LGPL
29155  * <script type="text/javascript">
29156  */
29157  
29158
29159 /**
29160  * @class Roo.ColorPalette
29161  * @extends Roo.Component
29162  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
29163  * Here's an example of typical usage:
29164  * <pre><code>
29165 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
29166 cp.render('my-div');
29167
29168 cp.on('select', function(palette, selColor){
29169     // do something with selColor
29170 });
29171 </code></pre>
29172  * @constructor
29173  * Create a new ColorPalette
29174  * @param {Object} config The config object
29175  */
29176 Roo.ColorPalette = function(config){
29177     Roo.ColorPalette.superclass.constructor.call(this, config);
29178     this.addEvents({
29179         /**
29180              * @event select
29181              * Fires when a color is selected
29182              * @param {ColorPalette} this
29183              * @param {String} color The 6-digit color hex code (without the # symbol)
29184              */
29185         select: true
29186     });
29187
29188     if(this.handler){
29189         this.on("select", this.handler, this.scope, true);
29190     }
29191 };
29192 Roo.extend(Roo.ColorPalette, Roo.Component, {
29193     /**
29194      * @cfg {String} itemCls
29195      * The CSS class to apply to the containing element (defaults to "x-color-palette")
29196      */
29197     itemCls : "x-color-palette",
29198     /**
29199      * @cfg {String} value
29200      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29201      * the hex codes are case-sensitive.
29202      */
29203     value : null,
29204     clickEvent:'click',
29205     // private
29206     ctype: "Roo.ColorPalette",
29207
29208     /**
29209      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29210      */
29211     allowReselect : false,
29212
29213     /**
29214      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29215      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29216      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29217      * of colors with the width setting until the box is symmetrical.</p>
29218      * <p>You can override individual colors if needed:</p>
29219      * <pre><code>
29220 var cp = new Roo.ColorPalette();
29221 cp.colors[0] = "FF0000";  // change the first box to red
29222 </code></pre>
29223
29224 Or you can provide a custom array of your own for complete control:
29225 <pre><code>
29226 var cp = new Roo.ColorPalette();
29227 cp.colors = ["000000", "993300", "333300"];
29228 </code></pre>
29229      * @type Array
29230      */
29231     colors : [
29232         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29233         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29234         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29235         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29236         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29237     ],
29238
29239     // private
29240     onRender : function(container, position){
29241         var t = new Roo.MasterTemplate(
29242             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29243         );
29244         var c = this.colors;
29245         for(var i = 0, len = c.length; i < len; i++){
29246             t.add([c[i]]);
29247         }
29248         var el = document.createElement("div");
29249         el.className = this.itemCls;
29250         t.overwrite(el);
29251         container.dom.insertBefore(el, position);
29252         this.el = Roo.get(el);
29253         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29254         if(this.clickEvent != 'click'){
29255             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29256         }
29257     },
29258
29259     // private
29260     afterRender : function(){
29261         Roo.ColorPalette.superclass.afterRender.call(this);
29262         if(this.value){
29263             var s = this.value;
29264             this.value = null;
29265             this.select(s);
29266         }
29267     },
29268
29269     // private
29270     handleClick : function(e, t){
29271         e.preventDefault();
29272         if(!this.disabled){
29273             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29274             this.select(c.toUpperCase());
29275         }
29276     },
29277
29278     /**
29279      * Selects the specified color in the palette (fires the select event)
29280      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29281      */
29282     select : function(color){
29283         color = color.replace("#", "");
29284         if(color != this.value || this.allowReselect){
29285             var el = this.el;
29286             if(this.value){
29287                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29288             }
29289             el.child("a.color-"+color).addClass("x-color-palette-sel");
29290             this.value = color;
29291             this.fireEvent("select", this, color);
29292         }
29293     }
29294 });/*
29295  * Based on:
29296  * Ext JS Library 1.1.1
29297  * Copyright(c) 2006-2007, Ext JS, LLC.
29298  *
29299  * Originally Released Under LGPL - original licence link has changed is not relivant.
29300  *
29301  * Fork - LGPL
29302  * <script type="text/javascript">
29303  */
29304  
29305 /**
29306  * @class Roo.DatePicker
29307  * @extends Roo.Component
29308  * Simple date picker class.
29309  * @constructor
29310  * Create a new DatePicker
29311  * @param {Object} config The config object
29312  */
29313 Roo.DatePicker = function(config){
29314     Roo.DatePicker.superclass.constructor.call(this, config);
29315
29316     this.value = config && config.value ?
29317                  config.value.clearTime() : new Date().clearTime();
29318
29319     this.addEvents({
29320         /**
29321              * @event select
29322              * Fires when a date is selected
29323              * @param {DatePicker} this
29324              * @param {Date} date The selected date
29325              */
29326         'select': true,
29327         /**
29328              * @event monthchange
29329              * Fires when the displayed month changes 
29330              * @param {DatePicker} this
29331              * @param {Date} date The selected month
29332              */
29333         'monthchange': true
29334     });
29335
29336     if(this.handler){
29337         this.on("select", this.handler,  this.scope || this);
29338     }
29339     // build the disabledDatesRE
29340     if(!this.disabledDatesRE && this.disabledDates){
29341         var dd = this.disabledDates;
29342         var re = "(?:";
29343         for(var i = 0; i < dd.length; i++){
29344             re += dd[i];
29345             if(i != dd.length-1) {
29346                 re += "|";
29347             }
29348         }
29349         this.disabledDatesRE = new RegExp(re + ")");
29350     }
29351 };
29352
29353 Roo.extend(Roo.DatePicker, Roo.Component, {
29354     /**
29355      * @cfg {String} todayText
29356      * The text to display on the button that selects the current date (defaults to "Today")
29357      */
29358     todayText : "Today",
29359     /**
29360      * @cfg {String} okText
29361      * The text to display on the ok button
29362      */
29363     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29364     /**
29365      * @cfg {String} cancelText
29366      * The text to display on the cancel button
29367      */
29368     cancelText : "Cancel",
29369     /**
29370      * @cfg {String} todayTip
29371      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29372      */
29373     todayTip : "{0} (Spacebar)",
29374     /**
29375      * @cfg {Date} minDate
29376      * Minimum allowable date (JavaScript date object, defaults to null)
29377      */
29378     minDate : null,
29379     /**
29380      * @cfg {Date} maxDate
29381      * Maximum allowable date (JavaScript date object, defaults to null)
29382      */
29383     maxDate : null,
29384     /**
29385      * @cfg {String} minText
29386      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29387      */
29388     minText : "This date is before the minimum date",
29389     /**
29390      * @cfg {String} maxText
29391      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29392      */
29393     maxText : "This date is after the maximum date",
29394     /**
29395      * @cfg {String} format
29396      * The default date format string which can be overriden for localization support.  The format must be
29397      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29398      */
29399     format : "m/d/y",
29400     /**
29401      * @cfg {Array} disabledDays
29402      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29403      */
29404     disabledDays : null,
29405     /**
29406      * @cfg {String} disabledDaysText
29407      * The tooltip to display when the date falls on a disabled day (defaults to "")
29408      */
29409     disabledDaysText : "",
29410     /**
29411      * @cfg {RegExp} disabledDatesRE
29412      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29413      */
29414     disabledDatesRE : null,
29415     /**
29416      * @cfg {String} disabledDatesText
29417      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29418      */
29419     disabledDatesText : "",
29420     /**
29421      * @cfg {Boolean} constrainToViewport
29422      * True to constrain the date picker to the viewport (defaults to true)
29423      */
29424     constrainToViewport : true,
29425     /**
29426      * @cfg {Array} monthNames
29427      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29428      */
29429     monthNames : Date.monthNames,
29430     /**
29431      * @cfg {Array} dayNames
29432      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29433      */
29434     dayNames : Date.dayNames,
29435     /**
29436      * @cfg {String} nextText
29437      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29438      */
29439     nextText: 'Next Month (Control+Right)',
29440     /**
29441      * @cfg {String} prevText
29442      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29443      */
29444     prevText: 'Previous Month (Control+Left)',
29445     /**
29446      * @cfg {String} monthYearText
29447      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29448      */
29449     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29450     /**
29451      * @cfg {Number} startDay
29452      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29453      */
29454     startDay : 0,
29455     /**
29456      * @cfg {Bool} showClear
29457      * Show a clear button (usefull for date form elements that can be blank.)
29458      */
29459     
29460     showClear: false,
29461     
29462     /**
29463      * Sets the value of the date field
29464      * @param {Date} value The date to set
29465      */
29466     setValue : function(value){
29467         var old = this.value;
29468         
29469         if (typeof(value) == 'string') {
29470          
29471             value = Date.parseDate(value, this.format);
29472         }
29473         if (!value) {
29474             value = new Date();
29475         }
29476         
29477         this.value = value.clearTime(true);
29478         if(this.el){
29479             this.update(this.value);
29480         }
29481     },
29482
29483     /**
29484      * Gets the current selected value of the date field
29485      * @return {Date} The selected date
29486      */
29487     getValue : function(){
29488         return this.value;
29489     },
29490
29491     // private
29492     focus : function(){
29493         if(this.el){
29494             this.update(this.activeDate);
29495         }
29496     },
29497
29498     // privateval
29499     onRender : function(container, position){
29500         
29501         var m = [
29502              '<table cellspacing="0">',
29503                 '<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>',
29504                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29505         var dn = this.dayNames;
29506         for(var i = 0; i < 7; i++){
29507             var d = this.startDay+i;
29508             if(d > 6){
29509                 d = d-7;
29510             }
29511             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29512         }
29513         m[m.length] = "</tr></thead><tbody><tr>";
29514         for(var i = 0; i < 42; i++) {
29515             if(i % 7 == 0 && i != 0){
29516                 m[m.length] = "</tr><tr>";
29517             }
29518             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29519         }
29520         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29521             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29522
29523         var el = document.createElement("div");
29524         el.className = "x-date-picker";
29525         el.innerHTML = m.join("");
29526
29527         container.dom.insertBefore(el, position);
29528
29529         this.el = Roo.get(el);
29530         this.eventEl = Roo.get(el.firstChild);
29531
29532         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29533             handler: this.showPrevMonth,
29534             scope: this,
29535             preventDefault:true,
29536             stopDefault:true
29537         });
29538
29539         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29540             handler: this.showNextMonth,
29541             scope: this,
29542             preventDefault:true,
29543             stopDefault:true
29544         });
29545
29546         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29547
29548         this.monthPicker = this.el.down('div.x-date-mp');
29549         this.monthPicker.enableDisplayMode('block');
29550         
29551         var kn = new Roo.KeyNav(this.eventEl, {
29552             "left" : function(e){
29553                 e.ctrlKey ?
29554                     this.showPrevMonth() :
29555                     this.update(this.activeDate.add("d", -1));
29556             },
29557
29558             "right" : function(e){
29559                 e.ctrlKey ?
29560                     this.showNextMonth() :
29561                     this.update(this.activeDate.add("d", 1));
29562             },
29563
29564             "up" : function(e){
29565                 e.ctrlKey ?
29566                     this.showNextYear() :
29567                     this.update(this.activeDate.add("d", -7));
29568             },
29569
29570             "down" : function(e){
29571                 e.ctrlKey ?
29572                     this.showPrevYear() :
29573                     this.update(this.activeDate.add("d", 7));
29574             },
29575
29576             "pageUp" : function(e){
29577                 this.showNextMonth();
29578             },
29579
29580             "pageDown" : function(e){
29581                 this.showPrevMonth();
29582             },
29583
29584             "enter" : function(e){
29585                 e.stopPropagation();
29586                 return true;
29587             },
29588
29589             scope : this
29590         });
29591
29592         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29593
29594         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29595
29596         this.el.unselectable();
29597         
29598         this.cells = this.el.select("table.x-date-inner tbody td");
29599         this.textNodes = this.el.query("table.x-date-inner tbody span");
29600
29601         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29602             text: "&#160;",
29603             tooltip: this.monthYearText
29604         });
29605
29606         this.mbtn.on('click', this.showMonthPicker, this);
29607         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29608
29609
29610         var today = (new Date()).dateFormat(this.format);
29611         
29612         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29613         if (this.showClear) {
29614             baseTb.add( new Roo.Toolbar.Fill());
29615         }
29616         baseTb.add({
29617             text: String.format(this.todayText, today),
29618             tooltip: String.format(this.todayTip, today),
29619             handler: this.selectToday,
29620             scope: this
29621         });
29622         
29623         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29624             
29625         //});
29626         if (this.showClear) {
29627             
29628             baseTb.add( new Roo.Toolbar.Fill());
29629             baseTb.add({
29630                 text: '&#160;',
29631                 cls: 'x-btn-icon x-btn-clear',
29632                 handler: function() {
29633                     //this.value = '';
29634                     this.fireEvent("select", this, '');
29635                 },
29636                 scope: this
29637             });
29638         }
29639         
29640         
29641         if(Roo.isIE){
29642             this.el.repaint();
29643         }
29644         this.update(this.value);
29645     },
29646
29647     createMonthPicker : function(){
29648         if(!this.monthPicker.dom.firstChild){
29649             var buf = ['<table border="0" cellspacing="0">'];
29650             for(var i = 0; i < 6; i++){
29651                 buf.push(
29652                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29653                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29654                     i == 0 ?
29655                     '<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>' :
29656                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29657                 );
29658             }
29659             buf.push(
29660                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29661                     this.okText,
29662                     '</button><button type="button" class="x-date-mp-cancel">',
29663                     this.cancelText,
29664                     '</button></td></tr>',
29665                 '</table>'
29666             );
29667             this.monthPicker.update(buf.join(''));
29668             this.monthPicker.on('click', this.onMonthClick, this);
29669             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29670
29671             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29672             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29673
29674             this.mpMonths.each(function(m, a, i){
29675                 i += 1;
29676                 if((i%2) == 0){
29677                     m.dom.xmonth = 5 + Math.round(i * .5);
29678                 }else{
29679                     m.dom.xmonth = Math.round((i-1) * .5);
29680                 }
29681             });
29682         }
29683     },
29684
29685     showMonthPicker : function(){
29686         this.createMonthPicker();
29687         var size = this.el.getSize();
29688         this.monthPicker.setSize(size);
29689         this.monthPicker.child('table').setSize(size);
29690
29691         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29692         this.updateMPMonth(this.mpSelMonth);
29693         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29694         this.updateMPYear(this.mpSelYear);
29695
29696         this.monthPicker.slideIn('t', {duration:.2});
29697     },
29698
29699     updateMPYear : function(y){
29700         this.mpyear = y;
29701         var ys = this.mpYears.elements;
29702         for(var i = 1; i <= 10; i++){
29703             var td = ys[i-1], y2;
29704             if((i%2) == 0){
29705                 y2 = y + Math.round(i * .5);
29706                 td.firstChild.innerHTML = y2;
29707                 td.xyear = y2;
29708             }else{
29709                 y2 = y - (5-Math.round(i * .5));
29710                 td.firstChild.innerHTML = y2;
29711                 td.xyear = y2;
29712             }
29713             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29714         }
29715     },
29716
29717     updateMPMonth : function(sm){
29718         this.mpMonths.each(function(m, a, i){
29719             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29720         });
29721     },
29722
29723     selectMPMonth: function(m){
29724         
29725     },
29726
29727     onMonthClick : function(e, t){
29728         e.stopEvent();
29729         var el = new Roo.Element(t), pn;
29730         if(el.is('button.x-date-mp-cancel')){
29731             this.hideMonthPicker();
29732         }
29733         else if(el.is('button.x-date-mp-ok')){
29734             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29735             this.hideMonthPicker();
29736         }
29737         else if(pn = el.up('td.x-date-mp-month', 2)){
29738             this.mpMonths.removeClass('x-date-mp-sel');
29739             pn.addClass('x-date-mp-sel');
29740             this.mpSelMonth = pn.dom.xmonth;
29741         }
29742         else if(pn = el.up('td.x-date-mp-year', 2)){
29743             this.mpYears.removeClass('x-date-mp-sel');
29744             pn.addClass('x-date-mp-sel');
29745             this.mpSelYear = pn.dom.xyear;
29746         }
29747         else if(el.is('a.x-date-mp-prev')){
29748             this.updateMPYear(this.mpyear-10);
29749         }
29750         else if(el.is('a.x-date-mp-next')){
29751             this.updateMPYear(this.mpyear+10);
29752         }
29753     },
29754
29755     onMonthDblClick : function(e, t){
29756         e.stopEvent();
29757         var el = new Roo.Element(t), pn;
29758         if(pn = el.up('td.x-date-mp-month', 2)){
29759             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29760             this.hideMonthPicker();
29761         }
29762         else if(pn = el.up('td.x-date-mp-year', 2)){
29763             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29764             this.hideMonthPicker();
29765         }
29766     },
29767
29768     hideMonthPicker : function(disableAnim){
29769         if(this.monthPicker){
29770             if(disableAnim === true){
29771                 this.monthPicker.hide();
29772             }else{
29773                 this.monthPicker.slideOut('t', {duration:.2});
29774             }
29775         }
29776     },
29777
29778     // private
29779     showPrevMonth : function(e){
29780         this.update(this.activeDate.add("mo", -1));
29781     },
29782
29783     // private
29784     showNextMonth : function(e){
29785         this.update(this.activeDate.add("mo", 1));
29786     },
29787
29788     // private
29789     showPrevYear : function(){
29790         this.update(this.activeDate.add("y", -1));
29791     },
29792
29793     // private
29794     showNextYear : function(){
29795         this.update(this.activeDate.add("y", 1));
29796     },
29797
29798     // private
29799     handleMouseWheel : function(e){
29800         var delta = e.getWheelDelta();
29801         if(delta > 0){
29802             this.showPrevMonth();
29803             e.stopEvent();
29804         } else if(delta < 0){
29805             this.showNextMonth();
29806             e.stopEvent();
29807         }
29808     },
29809
29810     // private
29811     handleDateClick : function(e, t){
29812         e.stopEvent();
29813         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29814             this.setValue(new Date(t.dateValue));
29815             this.fireEvent("select", this, this.value);
29816         }
29817     },
29818
29819     // private
29820     selectToday : function(){
29821         this.setValue(new Date().clearTime());
29822         this.fireEvent("select", this, this.value);
29823     },
29824
29825     // private
29826     update : function(date)
29827     {
29828         var vd = this.activeDate;
29829         this.activeDate = date;
29830         if(vd && this.el){
29831             var t = date.getTime();
29832             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29833                 this.cells.removeClass("x-date-selected");
29834                 this.cells.each(function(c){
29835                    if(c.dom.firstChild.dateValue == t){
29836                        c.addClass("x-date-selected");
29837                        setTimeout(function(){
29838                             try{c.dom.firstChild.focus();}catch(e){}
29839                        }, 50);
29840                        return false;
29841                    }
29842                 });
29843                 return;
29844             }
29845         }
29846         
29847         var days = date.getDaysInMonth();
29848         var firstOfMonth = date.getFirstDateOfMonth();
29849         var startingPos = firstOfMonth.getDay()-this.startDay;
29850
29851         if(startingPos <= this.startDay){
29852             startingPos += 7;
29853         }
29854
29855         var pm = date.add("mo", -1);
29856         var prevStart = pm.getDaysInMonth()-startingPos;
29857
29858         var cells = this.cells.elements;
29859         var textEls = this.textNodes;
29860         days += startingPos;
29861
29862         // convert everything to numbers so it's fast
29863         var day = 86400000;
29864         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29865         var today = new Date().clearTime().getTime();
29866         var sel = date.clearTime().getTime();
29867         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29868         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29869         var ddMatch = this.disabledDatesRE;
29870         var ddText = this.disabledDatesText;
29871         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29872         var ddaysText = this.disabledDaysText;
29873         var format = this.format;
29874
29875         var setCellClass = function(cal, cell){
29876             cell.title = "";
29877             var t = d.getTime();
29878             cell.firstChild.dateValue = t;
29879             if(t == today){
29880                 cell.className += " x-date-today";
29881                 cell.title = cal.todayText;
29882             }
29883             if(t == sel){
29884                 cell.className += " x-date-selected";
29885                 setTimeout(function(){
29886                     try{cell.firstChild.focus();}catch(e){}
29887                 }, 50);
29888             }
29889             // disabling
29890             if(t < min) {
29891                 cell.className = " x-date-disabled";
29892                 cell.title = cal.minText;
29893                 return;
29894             }
29895             if(t > max) {
29896                 cell.className = " x-date-disabled";
29897                 cell.title = cal.maxText;
29898                 return;
29899             }
29900             if(ddays){
29901                 if(ddays.indexOf(d.getDay()) != -1){
29902                     cell.title = ddaysText;
29903                     cell.className = " x-date-disabled";
29904                 }
29905             }
29906             if(ddMatch && format){
29907                 var fvalue = d.dateFormat(format);
29908                 if(ddMatch.test(fvalue)){
29909                     cell.title = ddText.replace("%0", fvalue);
29910                     cell.className = " x-date-disabled";
29911                 }
29912             }
29913         };
29914
29915         var i = 0;
29916         for(; i < startingPos; i++) {
29917             textEls[i].innerHTML = (++prevStart);
29918             d.setDate(d.getDate()+1);
29919             cells[i].className = "x-date-prevday";
29920             setCellClass(this, cells[i]);
29921         }
29922         for(; i < days; i++){
29923             intDay = i - startingPos + 1;
29924             textEls[i].innerHTML = (intDay);
29925             d.setDate(d.getDate()+1);
29926             cells[i].className = "x-date-active";
29927             setCellClass(this, cells[i]);
29928         }
29929         var extraDays = 0;
29930         for(; i < 42; i++) {
29931              textEls[i].innerHTML = (++extraDays);
29932              d.setDate(d.getDate()+1);
29933              cells[i].className = "x-date-nextday";
29934              setCellClass(this, cells[i]);
29935         }
29936
29937         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29938         this.fireEvent('monthchange', this, date);
29939         
29940         if(!this.internalRender){
29941             var main = this.el.dom.firstChild;
29942             var w = main.offsetWidth;
29943             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29944             Roo.fly(main).setWidth(w);
29945             this.internalRender = true;
29946             // opera does not respect the auto grow header center column
29947             // then, after it gets a width opera refuses to recalculate
29948             // without a second pass
29949             if(Roo.isOpera && !this.secondPass){
29950                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29951                 this.secondPass = true;
29952                 this.update.defer(10, this, [date]);
29953             }
29954         }
29955         
29956         
29957     }
29958 });        /*
29959  * Based on:
29960  * Ext JS Library 1.1.1
29961  * Copyright(c) 2006-2007, Ext JS, LLC.
29962  *
29963  * Originally Released Under LGPL - original licence link has changed is not relivant.
29964  *
29965  * Fork - LGPL
29966  * <script type="text/javascript">
29967  */
29968 /**
29969  * @class Roo.TabPanel
29970  * @extends Roo.util.Observable
29971  * A lightweight tab container.
29972  * <br><br>
29973  * Usage:
29974  * <pre><code>
29975 // basic tabs 1, built from existing content
29976 var tabs = new Roo.TabPanel("tabs1");
29977 tabs.addTab("script", "View Script");
29978 tabs.addTab("markup", "View Markup");
29979 tabs.activate("script");
29980
29981 // more advanced tabs, built from javascript
29982 var jtabs = new Roo.TabPanel("jtabs");
29983 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29984
29985 // set up the UpdateManager
29986 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29987 var updater = tab2.getUpdateManager();
29988 updater.setDefaultUrl("ajax1.htm");
29989 tab2.on('activate', updater.refresh, updater, true);
29990
29991 // Use setUrl for Ajax loading
29992 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29993 tab3.setUrl("ajax2.htm", null, true);
29994
29995 // Disabled tab
29996 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29997 tab4.disable();
29998
29999 jtabs.activate("jtabs-1");
30000  * </code></pre>
30001  * @constructor
30002  * Create a new TabPanel.
30003  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
30004  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
30005  */
30006 Roo.TabPanel = function(container, config){
30007     /**
30008     * The container element for this TabPanel.
30009     * @type Roo.Element
30010     */
30011     this.el = Roo.get(container, true);
30012     if(config){
30013         if(typeof config == "boolean"){
30014             this.tabPosition = config ? "bottom" : "top";
30015         }else{
30016             Roo.apply(this, config);
30017         }
30018     }
30019     if(this.tabPosition == "bottom"){
30020         this.bodyEl = Roo.get(this.createBody(this.el.dom));
30021         this.el.addClass("x-tabs-bottom");
30022     }
30023     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
30024     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
30025     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
30026     if(Roo.isIE){
30027         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
30028     }
30029     if(this.tabPosition != "bottom"){
30030         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
30031          * @type Roo.Element
30032          */
30033         this.bodyEl = Roo.get(this.createBody(this.el.dom));
30034         this.el.addClass("x-tabs-top");
30035     }
30036     this.items = [];
30037
30038     this.bodyEl.setStyle("position", "relative");
30039
30040     this.active = null;
30041     this.activateDelegate = this.activate.createDelegate(this);
30042
30043     this.addEvents({
30044         /**
30045          * @event tabchange
30046          * Fires when the active tab changes
30047          * @param {Roo.TabPanel} this
30048          * @param {Roo.TabPanelItem} activePanel The new active tab
30049          */
30050         "tabchange": true,
30051         /**
30052          * @event beforetabchange
30053          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
30054          * @param {Roo.TabPanel} this
30055          * @param {Object} e Set cancel to true on this object to cancel the tab change
30056          * @param {Roo.TabPanelItem} tab The tab being changed to
30057          */
30058         "beforetabchange" : true
30059     });
30060
30061     Roo.EventManager.onWindowResize(this.onResize, this);
30062     this.cpad = this.el.getPadding("lr");
30063     this.hiddenCount = 0;
30064
30065
30066     // toolbar on the tabbar support...
30067     if (this.toolbar) {
30068         var tcfg = this.toolbar;
30069         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
30070         this.toolbar = new Roo.Toolbar(tcfg);
30071         if (Roo.isSafari) {
30072             var tbl = tcfg.container.child('table', true);
30073             tbl.setAttribute('width', '100%');
30074         }
30075         
30076     }
30077    
30078
30079
30080     Roo.TabPanel.superclass.constructor.call(this);
30081 };
30082
30083 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
30084     /*
30085      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
30086      */
30087     tabPosition : "top",
30088     /*
30089      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
30090      */
30091     currentTabWidth : 0,
30092     /*
30093      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
30094      */
30095     minTabWidth : 40,
30096     /*
30097      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
30098      */
30099     maxTabWidth : 250,
30100     /*
30101      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
30102      */
30103     preferredTabWidth : 175,
30104     /*
30105      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
30106      */
30107     resizeTabs : false,
30108     /*
30109      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
30110      */
30111     monitorResize : true,
30112     /*
30113      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
30114      */
30115     toolbar : false,
30116
30117     /**
30118      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
30119      * @param {String} id The id of the div to use <b>or create</b>
30120      * @param {String} text The text for the tab
30121      * @param {String} content (optional) Content to put in the TabPanelItem body
30122      * @param {Boolean} closable (optional) True to create a close icon on the tab
30123      * @return {Roo.TabPanelItem} The created TabPanelItem
30124      */
30125     addTab : function(id, text, content, closable){
30126         var item = new Roo.TabPanelItem(this, id, text, closable);
30127         this.addTabItem(item);
30128         if(content){
30129             item.setContent(content);
30130         }
30131         return item;
30132     },
30133
30134     /**
30135      * Returns the {@link Roo.TabPanelItem} with the specified id/index
30136      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
30137      * @return {Roo.TabPanelItem}
30138      */
30139     getTab : function(id){
30140         return this.items[id];
30141     },
30142
30143     /**
30144      * Hides the {@link Roo.TabPanelItem} with the specified id/index
30145      * @param {String/Number} id The id or index of the TabPanelItem to hide.
30146      */
30147     hideTab : function(id){
30148         var t = this.items[id];
30149         if(!t.isHidden()){
30150            t.setHidden(true);
30151            this.hiddenCount++;
30152            this.autoSizeTabs();
30153         }
30154     },
30155
30156     /**
30157      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
30158      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
30159      */
30160     unhideTab : function(id){
30161         var t = this.items[id];
30162         if(t.isHidden()){
30163            t.setHidden(false);
30164            this.hiddenCount--;
30165            this.autoSizeTabs();
30166         }
30167     },
30168
30169     /**
30170      * Adds an existing {@link Roo.TabPanelItem}.
30171      * @param {Roo.TabPanelItem} item The TabPanelItem to add
30172      */
30173     addTabItem : function(item){
30174         this.items[item.id] = item;
30175         this.items.push(item);
30176         if(this.resizeTabs){
30177            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
30178            this.autoSizeTabs();
30179         }else{
30180             item.autoSize();
30181         }
30182     },
30183
30184     /**
30185      * Removes a {@link Roo.TabPanelItem}.
30186      * @param {String/Number} id The id or index of the TabPanelItem to remove.
30187      */
30188     removeTab : function(id){
30189         var items = this.items;
30190         var tab = items[id];
30191         if(!tab) { return; }
30192         var index = items.indexOf(tab);
30193         if(this.active == tab && items.length > 1){
30194             var newTab = this.getNextAvailable(index);
30195             if(newTab) {
30196                 newTab.activate();
30197             }
30198         }
30199         this.stripEl.dom.removeChild(tab.pnode.dom);
30200         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30201             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30202         }
30203         items.splice(index, 1);
30204         delete this.items[tab.id];
30205         tab.fireEvent("close", tab);
30206         tab.purgeListeners();
30207         this.autoSizeTabs();
30208     },
30209
30210     getNextAvailable : function(start){
30211         var items = this.items;
30212         var index = start;
30213         // look for a next tab that will slide over to
30214         // replace the one being removed
30215         while(index < items.length){
30216             var item = items[++index];
30217             if(item && !item.isHidden()){
30218                 return item;
30219             }
30220         }
30221         // if one isn't found select the previous tab (on the left)
30222         index = start;
30223         while(index >= 0){
30224             var item = items[--index];
30225             if(item && !item.isHidden()){
30226                 return item;
30227             }
30228         }
30229         return null;
30230     },
30231
30232     /**
30233      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30234      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30235      */
30236     disableTab : function(id){
30237         var tab = this.items[id];
30238         if(tab && this.active != tab){
30239             tab.disable();
30240         }
30241     },
30242
30243     /**
30244      * Enables a {@link Roo.TabPanelItem} that is disabled.
30245      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30246      */
30247     enableTab : function(id){
30248         var tab = this.items[id];
30249         tab.enable();
30250     },
30251
30252     /**
30253      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30254      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30255      * @return {Roo.TabPanelItem} The TabPanelItem.
30256      */
30257     activate : function(id){
30258         var tab = this.items[id];
30259         if(!tab){
30260             return null;
30261         }
30262         if(tab == this.active || tab.disabled){
30263             return tab;
30264         }
30265         var e = {};
30266         this.fireEvent("beforetabchange", this, e, tab);
30267         if(e.cancel !== true && !tab.disabled){
30268             if(this.active){
30269                 this.active.hide();
30270             }
30271             this.active = this.items[id];
30272             this.active.show();
30273             this.fireEvent("tabchange", this, this.active);
30274         }
30275         return tab;
30276     },
30277
30278     /**
30279      * Gets the active {@link Roo.TabPanelItem}.
30280      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30281      */
30282     getActiveTab : function(){
30283         return this.active;
30284     },
30285
30286     /**
30287      * Updates the tab body element to fit the height of the container element
30288      * for overflow scrolling
30289      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30290      */
30291     syncHeight : function(targetHeight){
30292         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30293         var bm = this.bodyEl.getMargins();
30294         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30295         this.bodyEl.setHeight(newHeight);
30296         return newHeight;
30297     },
30298
30299     onResize : function(){
30300         if(this.monitorResize){
30301             this.autoSizeTabs();
30302         }
30303     },
30304
30305     /**
30306      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30307      */
30308     beginUpdate : function(){
30309         this.updating = true;
30310     },
30311
30312     /**
30313      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30314      */
30315     endUpdate : function(){
30316         this.updating = false;
30317         this.autoSizeTabs();
30318     },
30319
30320     /**
30321      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30322      */
30323     autoSizeTabs : function(){
30324         var count = this.items.length;
30325         var vcount = count - this.hiddenCount;
30326         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30327             return;
30328         }
30329         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30330         var availWidth = Math.floor(w / vcount);
30331         var b = this.stripBody;
30332         if(b.getWidth() > w){
30333             var tabs = this.items;
30334             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30335             if(availWidth < this.minTabWidth){
30336                 /*if(!this.sleft){    // incomplete scrolling code
30337                     this.createScrollButtons();
30338                 }
30339                 this.showScroll();
30340                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30341             }
30342         }else{
30343             if(this.currentTabWidth < this.preferredTabWidth){
30344                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30345             }
30346         }
30347     },
30348
30349     /**
30350      * Returns the number of tabs in this TabPanel.
30351      * @return {Number}
30352      */
30353      getCount : function(){
30354          return this.items.length;
30355      },
30356
30357     /**
30358      * Resizes all the tabs to the passed width
30359      * @param {Number} The new width
30360      */
30361     setTabWidth : function(width){
30362         this.currentTabWidth = width;
30363         for(var i = 0, len = this.items.length; i < len; i++) {
30364                 if(!this.items[i].isHidden()) {
30365                 this.items[i].setWidth(width);
30366             }
30367         }
30368     },
30369
30370     /**
30371      * Destroys this TabPanel
30372      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30373      */
30374     destroy : function(removeEl){
30375         Roo.EventManager.removeResizeListener(this.onResize, this);
30376         for(var i = 0, len = this.items.length; i < len; i++){
30377             this.items[i].purgeListeners();
30378         }
30379         if(removeEl === true){
30380             this.el.update("");
30381             this.el.remove();
30382         }
30383     }
30384 });
30385
30386 /**
30387  * @class Roo.TabPanelItem
30388  * @extends Roo.util.Observable
30389  * Represents an individual item (tab plus body) in a TabPanel.
30390  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30391  * @param {String} id The id of this TabPanelItem
30392  * @param {String} text The text for the tab of this TabPanelItem
30393  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30394  */
30395 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30396     /**
30397      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30398      * @type Roo.TabPanel
30399      */
30400     this.tabPanel = tabPanel;
30401     /**
30402      * The id for this TabPanelItem
30403      * @type String
30404      */
30405     this.id = id;
30406     /** @private */
30407     this.disabled = false;
30408     /** @private */
30409     this.text = text;
30410     /** @private */
30411     this.loaded = false;
30412     this.closable = closable;
30413
30414     /**
30415      * The body element for this TabPanelItem.
30416      * @type Roo.Element
30417      */
30418     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30419     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30420     this.bodyEl.setStyle("display", "block");
30421     this.bodyEl.setStyle("zoom", "1");
30422     this.hideAction();
30423
30424     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30425     /** @private */
30426     this.el = Roo.get(els.el, true);
30427     this.inner = Roo.get(els.inner, true);
30428     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30429     this.pnode = Roo.get(els.el.parentNode, true);
30430     this.el.on("mousedown", this.onTabMouseDown, this);
30431     this.el.on("click", this.onTabClick, this);
30432     /** @private */
30433     if(closable){
30434         var c = Roo.get(els.close, true);
30435         c.dom.title = this.closeText;
30436         c.addClassOnOver("close-over");
30437         c.on("click", this.closeClick, this);
30438      }
30439
30440     this.addEvents({
30441          /**
30442          * @event activate
30443          * Fires when this tab becomes the active tab.
30444          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30445          * @param {Roo.TabPanelItem} this
30446          */
30447         "activate": true,
30448         /**
30449          * @event beforeclose
30450          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30451          * @param {Roo.TabPanelItem} this
30452          * @param {Object} e Set cancel to true on this object to cancel the close.
30453          */
30454         "beforeclose": true,
30455         /**
30456          * @event close
30457          * Fires when this tab is closed.
30458          * @param {Roo.TabPanelItem} this
30459          */
30460          "close": true,
30461         /**
30462          * @event deactivate
30463          * Fires when this tab is no longer the active tab.
30464          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30465          * @param {Roo.TabPanelItem} this
30466          */
30467          "deactivate" : true
30468     });
30469     this.hidden = false;
30470
30471     Roo.TabPanelItem.superclass.constructor.call(this);
30472 };
30473
30474 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30475     purgeListeners : function(){
30476        Roo.util.Observable.prototype.purgeListeners.call(this);
30477        this.el.removeAllListeners();
30478     },
30479     /**
30480      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30481      */
30482     show : function(){
30483         this.pnode.addClass("on");
30484         this.showAction();
30485         if(Roo.isOpera){
30486             this.tabPanel.stripWrap.repaint();
30487         }
30488         this.fireEvent("activate", this.tabPanel, this);
30489     },
30490
30491     /**
30492      * Returns true if this tab is the active tab.
30493      * @return {Boolean}
30494      */
30495     isActive : function(){
30496         return this.tabPanel.getActiveTab() == this;
30497     },
30498
30499     /**
30500      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30501      */
30502     hide : function(){
30503         this.pnode.removeClass("on");
30504         this.hideAction();
30505         this.fireEvent("deactivate", this.tabPanel, this);
30506     },
30507
30508     hideAction : function(){
30509         this.bodyEl.hide();
30510         this.bodyEl.setStyle("position", "absolute");
30511         this.bodyEl.setLeft("-20000px");
30512         this.bodyEl.setTop("-20000px");
30513     },
30514
30515     showAction : function(){
30516         this.bodyEl.setStyle("position", "relative");
30517         this.bodyEl.setTop("");
30518         this.bodyEl.setLeft("");
30519         this.bodyEl.show();
30520     },
30521
30522     /**
30523      * Set the tooltip for the tab.
30524      * @param {String} tooltip The tab's tooltip
30525      */
30526     setTooltip : function(text){
30527         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30528             this.textEl.dom.qtip = text;
30529             this.textEl.dom.removeAttribute('title');
30530         }else{
30531             this.textEl.dom.title = text;
30532         }
30533     },
30534
30535     onTabClick : function(e){
30536         e.preventDefault();
30537         this.tabPanel.activate(this.id);
30538     },
30539
30540     onTabMouseDown : function(e){
30541         e.preventDefault();
30542         this.tabPanel.activate(this.id);
30543     },
30544
30545     getWidth : function(){
30546         return this.inner.getWidth();
30547     },
30548
30549     setWidth : function(width){
30550         var iwidth = width - this.pnode.getPadding("lr");
30551         this.inner.setWidth(iwidth);
30552         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30553         this.pnode.setWidth(width);
30554     },
30555
30556     /**
30557      * Show or hide the tab
30558      * @param {Boolean} hidden True to hide or false to show.
30559      */
30560     setHidden : function(hidden){
30561         this.hidden = hidden;
30562         this.pnode.setStyle("display", hidden ? "none" : "");
30563     },
30564
30565     /**
30566      * Returns true if this tab is "hidden"
30567      * @return {Boolean}
30568      */
30569     isHidden : function(){
30570         return this.hidden;
30571     },
30572
30573     /**
30574      * Returns the text for this tab
30575      * @return {String}
30576      */
30577     getText : function(){
30578         return this.text;
30579     },
30580
30581     autoSize : function(){
30582         //this.el.beginMeasure();
30583         this.textEl.setWidth(1);
30584         /*
30585          *  #2804 [new] Tabs in Roojs
30586          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30587          */
30588         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30589         //this.el.endMeasure();
30590     },
30591
30592     /**
30593      * Sets the text for the tab (Note: this also sets the tooltip text)
30594      * @param {String} text The tab's text and tooltip
30595      */
30596     setText : function(text){
30597         this.text = text;
30598         this.textEl.update(text);
30599         this.setTooltip(text);
30600         if(!this.tabPanel.resizeTabs){
30601             this.autoSize();
30602         }
30603     },
30604     /**
30605      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30606      */
30607     activate : function(){
30608         this.tabPanel.activate(this.id);
30609     },
30610
30611     /**
30612      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30613      */
30614     disable : function(){
30615         if(this.tabPanel.active != this){
30616             this.disabled = true;
30617             this.pnode.addClass("disabled");
30618         }
30619     },
30620
30621     /**
30622      * Enables this TabPanelItem if it was previously disabled.
30623      */
30624     enable : function(){
30625         this.disabled = false;
30626         this.pnode.removeClass("disabled");
30627     },
30628
30629     /**
30630      * Sets the content for this TabPanelItem.
30631      * @param {String} content The content
30632      * @param {Boolean} loadScripts true to look for and load scripts
30633      */
30634     setContent : function(content, loadScripts){
30635         this.bodyEl.update(content, loadScripts);
30636     },
30637
30638     /**
30639      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30640      * @return {Roo.UpdateManager} The UpdateManager
30641      */
30642     getUpdateManager : function(){
30643         return this.bodyEl.getUpdateManager();
30644     },
30645
30646     /**
30647      * Set a URL to be used to load the content for this TabPanelItem.
30648      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30649      * @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)
30650      * @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)
30651      * @return {Roo.UpdateManager} The UpdateManager
30652      */
30653     setUrl : function(url, params, loadOnce){
30654         if(this.refreshDelegate){
30655             this.un('activate', this.refreshDelegate);
30656         }
30657         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30658         this.on("activate", this.refreshDelegate);
30659         return this.bodyEl.getUpdateManager();
30660     },
30661
30662     /** @private */
30663     _handleRefresh : function(url, params, loadOnce){
30664         if(!loadOnce || !this.loaded){
30665             var updater = this.bodyEl.getUpdateManager();
30666             updater.update(url, params, this._setLoaded.createDelegate(this));
30667         }
30668     },
30669
30670     /**
30671      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30672      *   Will fail silently if the setUrl method has not been called.
30673      *   This does not activate the panel, just updates its content.
30674      */
30675     refresh : function(){
30676         if(this.refreshDelegate){
30677            this.loaded = false;
30678            this.refreshDelegate();
30679         }
30680     },
30681
30682     /** @private */
30683     _setLoaded : function(){
30684         this.loaded = true;
30685     },
30686
30687     /** @private */
30688     closeClick : function(e){
30689         var o = {};
30690         e.stopEvent();
30691         this.fireEvent("beforeclose", this, o);
30692         if(o.cancel !== true){
30693             this.tabPanel.removeTab(this.id);
30694         }
30695     },
30696     /**
30697      * The text displayed in the tooltip for the close icon.
30698      * @type String
30699      */
30700     closeText : "Close this tab"
30701 });
30702
30703 /** @private */
30704 Roo.TabPanel.prototype.createStrip = function(container){
30705     var strip = document.createElement("div");
30706     strip.className = "x-tabs-wrap";
30707     container.appendChild(strip);
30708     return strip;
30709 };
30710 /** @private */
30711 Roo.TabPanel.prototype.createStripList = function(strip){
30712     // div wrapper for retard IE
30713     // returns the "tr" element.
30714     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30715         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30716         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30717     return strip.firstChild.firstChild.firstChild.firstChild;
30718 };
30719 /** @private */
30720 Roo.TabPanel.prototype.createBody = function(container){
30721     var body = document.createElement("div");
30722     Roo.id(body, "tab-body");
30723     Roo.fly(body).addClass("x-tabs-body");
30724     container.appendChild(body);
30725     return body;
30726 };
30727 /** @private */
30728 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30729     var body = Roo.getDom(id);
30730     if(!body){
30731         body = document.createElement("div");
30732         body.id = id;
30733     }
30734     Roo.fly(body).addClass("x-tabs-item-body");
30735     bodyEl.insertBefore(body, bodyEl.firstChild);
30736     return body;
30737 };
30738 /** @private */
30739 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30740     var td = document.createElement("td");
30741     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30742     //stripEl.appendChild(td);
30743     if(closable){
30744         td.className = "x-tabs-closable";
30745         if(!this.closeTpl){
30746             this.closeTpl = new Roo.Template(
30747                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30748                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30749                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30750             );
30751         }
30752         var el = this.closeTpl.overwrite(td, {"text": text});
30753         var close = el.getElementsByTagName("div")[0];
30754         var inner = el.getElementsByTagName("em")[0];
30755         return {"el": el, "close": close, "inner": inner};
30756     } else {
30757         if(!this.tabTpl){
30758             this.tabTpl = new Roo.Template(
30759                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30760                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30761             );
30762         }
30763         var el = this.tabTpl.overwrite(td, {"text": text});
30764         var inner = el.getElementsByTagName("em")[0];
30765         return {"el": el, "inner": inner};
30766     }
30767 };/*
30768  * Based on:
30769  * Ext JS Library 1.1.1
30770  * Copyright(c) 2006-2007, Ext JS, LLC.
30771  *
30772  * Originally Released Under LGPL - original licence link has changed is not relivant.
30773  *
30774  * Fork - LGPL
30775  * <script type="text/javascript">
30776  */
30777
30778 /**
30779  * @class Roo.Button
30780  * @extends Roo.util.Observable
30781  * Simple Button class
30782  * @cfg {String} text The button text
30783  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30784  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30785  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30786  * @cfg {Object} scope The scope of the handler
30787  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30788  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30789  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30790  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30791  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30792  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30793    applies if enableToggle = true)
30794  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30795  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30796   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30797  * @constructor
30798  * Create a new button
30799  * @param {Object} config The config object
30800  */
30801 Roo.Button = function(renderTo, config)
30802 {
30803     if (!config) {
30804         config = renderTo;
30805         renderTo = config.renderTo || false;
30806     }
30807     
30808     Roo.apply(this, config);
30809     this.addEvents({
30810         /**
30811              * @event click
30812              * Fires when this button is clicked
30813              * @param {Button} this
30814              * @param {EventObject} e The click event
30815              */
30816             "click" : true,
30817         /**
30818              * @event toggle
30819              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30820              * @param {Button} this
30821              * @param {Boolean} pressed
30822              */
30823             "toggle" : true,
30824         /**
30825              * @event mouseover
30826              * Fires when the mouse hovers over the button
30827              * @param {Button} this
30828              * @param {Event} e The event object
30829              */
30830         'mouseover' : true,
30831         /**
30832              * @event mouseout
30833              * Fires when the mouse exits the button
30834              * @param {Button} this
30835              * @param {Event} e The event object
30836              */
30837         'mouseout': true,
30838          /**
30839              * @event render
30840              * Fires when the button is rendered
30841              * @param {Button} this
30842              */
30843         'render': true
30844     });
30845     if(this.menu){
30846         this.menu = Roo.menu.MenuMgr.get(this.menu);
30847     }
30848     // register listeners first!!  - so render can be captured..
30849     Roo.util.Observable.call(this);
30850     if(renderTo){
30851         this.render(renderTo);
30852     }
30853     
30854   
30855 };
30856
30857 Roo.extend(Roo.Button, Roo.util.Observable, {
30858     /**
30859      * 
30860      */
30861     
30862     /**
30863      * Read-only. True if this button is hidden
30864      * @type Boolean
30865      */
30866     hidden : false,
30867     /**
30868      * Read-only. True if this button is disabled
30869      * @type Boolean
30870      */
30871     disabled : false,
30872     /**
30873      * Read-only. True if this button is pressed (only if enableToggle = true)
30874      * @type Boolean
30875      */
30876     pressed : false,
30877
30878     /**
30879      * @cfg {Number} tabIndex 
30880      * The DOM tabIndex for this button (defaults to undefined)
30881      */
30882     tabIndex : undefined,
30883
30884     /**
30885      * @cfg {Boolean} enableToggle
30886      * True to enable pressed/not pressed toggling (defaults to false)
30887      */
30888     enableToggle: false,
30889     /**
30890      * @cfg {Roo.menu.Menu} menu
30891      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30892      */
30893     menu : undefined,
30894     /**
30895      * @cfg {String} menuAlign
30896      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30897      */
30898     menuAlign : "tl-bl?",
30899
30900     /**
30901      * @cfg {String} iconCls
30902      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30903      */
30904     iconCls : undefined,
30905     /**
30906      * @cfg {String} type
30907      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30908      */
30909     type : 'button',
30910
30911     // private
30912     menuClassTarget: 'tr',
30913
30914     /**
30915      * @cfg {String} clickEvent
30916      * The type of event to map to the button's event handler (defaults to 'click')
30917      */
30918     clickEvent : 'click',
30919
30920     /**
30921      * @cfg {Boolean} handleMouseEvents
30922      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30923      */
30924     handleMouseEvents : true,
30925
30926     /**
30927      * @cfg {String} tooltipType
30928      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30929      */
30930     tooltipType : 'qtip',
30931
30932     /**
30933      * @cfg {String} cls
30934      * A CSS class to apply to the button's main element.
30935      */
30936     
30937     /**
30938      * @cfg {Roo.Template} template (Optional)
30939      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30940      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30941      * require code modifications if required elements (e.g. a button) aren't present.
30942      */
30943
30944     // private
30945     render : function(renderTo){
30946         var btn;
30947         if(this.hideParent){
30948             this.parentEl = Roo.get(renderTo);
30949         }
30950         if(!this.dhconfig){
30951             if(!this.template){
30952                 if(!Roo.Button.buttonTemplate){
30953                     // hideous table template
30954                     Roo.Button.buttonTemplate = new Roo.Template(
30955                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30956                         '<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>',
30957                         "</tr></tbody></table>");
30958                 }
30959                 this.template = Roo.Button.buttonTemplate;
30960             }
30961             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30962             var btnEl = btn.child("button:first");
30963             btnEl.on('focus', this.onFocus, this);
30964             btnEl.on('blur', this.onBlur, this);
30965             if(this.cls){
30966                 btn.addClass(this.cls);
30967             }
30968             if(this.icon){
30969                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30970             }
30971             if(this.iconCls){
30972                 btnEl.addClass(this.iconCls);
30973                 if(!this.cls){
30974                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30975                 }
30976             }
30977             if(this.tabIndex !== undefined){
30978                 btnEl.dom.tabIndex = this.tabIndex;
30979             }
30980             if(this.tooltip){
30981                 if(typeof this.tooltip == 'object'){
30982                     Roo.QuickTips.tips(Roo.apply({
30983                           target: btnEl.id
30984                     }, this.tooltip));
30985                 } else {
30986                     btnEl.dom[this.tooltipType] = this.tooltip;
30987                 }
30988             }
30989         }else{
30990             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30991         }
30992         this.el = btn;
30993         if(this.id){
30994             this.el.dom.id = this.el.id = this.id;
30995         }
30996         if(this.menu){
30997             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30998             this.menu.on("show", this.onMenuShow, this);
30999             this.menu.on("hide", this.onMenuHide, this);
31000         }
31001         btn.addClass("x-btn");
31002         if(Roo.isIE && !Roo.isIE7){
31003             this.autoWidth.defer(1, this);
31004         }else{
31005             this.autoWidth();
31006         }
31007         if(this.handleMouseEvents){
31008             btn.on("mouseover", this.onMouseOver, this);
31009             btn.on("mouseout", this.onMouseOut, this);
31010             btn.on("mousedown", this.onMouseDown, this);
31011         }
31012         btn.on(this.clickEvent, this.onClick, this);
31013         //btn.on("mouseup", this.onMouseUp, this);
31014         if(this.hidden){
31015             this.hide();
31016         }
31017         if(this.disabled){
31018             this.disable();
31019         }
31020         Roo.ButtonToggleMgr.register(this);
31021         if(this.pressed){
31022             this.el.addClass("x-btn-pressed");
31023         }
31024         if(this.repeat){
31025             var repeater = new Roo.util.ClickRepeater(btn,
31026                 typeof this.repeat == "object" ? this.repeat : {}
31027             );
31028             repeater.on("click", this.onClick,  this);
31029         }
31030         
31031         this.fireEvent('render', this);
31032         
31033     },
31034     /**
31035      * Returns the button's underlying element
31036      * @return {Roo.Element} The element
31037      */
31038     getEl : function(){
31039         return this.el;  
31040     },
31041     
31042     /**
31043      * Destroys this Button and removes any listeners.
31044      */
31045     destroy : function(){
31046         Roo.ButtonToggleMgr.unregister(this);
31047         this.el.removeAllListeners();
31048         this.purgeListeners();
31049         this.el.remove();
31050     },
31051
31052     // private
31053     autoWidth : function(){
31054         if(this.el){
31055             this.el.setWidth("auto");
31056             if(Roo.isIE7 && Roo.isStrict){
31057                 var ib = this.el.child('button');
31058                 if(ib && ib.getWidth() > 20){
31059                     ib.clip();
31060                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31061                 }
31062             }
31063             if(this.minWidth){
31064                 if(this.hidden){
31065                     this.el.beginMeasure();
31066                 }
31067                 if(this.el.getWidth() < this.minWidth){
31068                     this.el.setWidth(this.minWidth);
31069                 }
31070                 if(this.hidden){
31071                     this.el.endMeasure();
31072                 }
31073             }
31074         }
31075     },
31076
31077     /**
31078      * Assigns this button's click handler
31079      * @param {Function} handler The function to call when the button is clicked
31080      * @param {Object} scope (optional) Scope for the function passed in
31081      */
31082     setHandler : function(handler, scope){
31083         this.handler = handler;
31084         this.scope = scope;  
31085     },
31086     
31087     /**
31088      * Sets this button's text
31089      * @param {String} text The button text
31090      */
31091     setText : function(text){
31092         this.text = text;
31093         if(this.el){
31094             this.el.child("td.x-btn-center button.x-btn-text").update(text);
31095         }
31096         this.autoWidth();
31097     },
31098     
31099     /**
31100      * Gets the text for this button
31101      * @return {String} The button text
31102      */
31103     getText : function(){
31104         return this.text;  
31105     },
31106     
31107     /**
31108      * Show this button
31109      */
31110     show: function(){
31111         this.hidden = false;
31112         if(this.el){
31113             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
31114         }
31115     },
31116     
31117     /**
31118      * Hide this button
31119      */
31120     hide: function(){
31121         this.hidden = true;
31122         if(this.el){
31123             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
31124         }
31125     },
31126     
31127     /**
31128      * Convenience function for boolean show/hide
31129      * @param {Boolean} visible True to show, false to hide
31130      */
31131     setVisible: function(visible){
31132         if(visible) {
31133             this.show();
31134         }else{
31135             this.hide();
31136         }
31137     },
31138     
31139     /**
31140      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
31141      * @param {Boolean} state (optional) Force a particular state
31142      */
31143     toggle : function(state){
31144         state = state === undefined ? !this.pressed : state;
31145         if(state != this.pressed){
31146             if(state){
31147                 this.el.addClass("x-btn-pressed");
31148                 this.pressed = true;
31149                 this.fireEvent("toggle", this, true);
31150             }else{
31151                 this.el.removeClass("x-btn-pressed");
31152                 this.pressed = false;
31153                 this.fireEvent("toggle", this, false);
31154             }
31155             if(this.toggleHandler){
31156                 this.toggleHandler.call(this.scope || this, this, state);
31157             }
31158         }
31159     },
31160     
31161     /**
31162      * Focus the button
31163      */
31164     focus : function(){
31165         this.el.child('button:first').focus();
31166     },
31167     
31168     /**
31169      * Disable this button
31170      */
31171     disable : function(){
31172         if(this.el){
31173             this.el.addClass("x-btn-disabled");
31174         }
31175         this.disabled = true;
31176     },
31177     
31178     /**
31179      * Enable this button
31180      */
31181     enable : function(){
31182         if(this.el){
31183             this.el.removeClass("x-btn-disabled");
31184         }
31185         this.disabled = false;
31186     },
31187
31188     /**
31189      * Convenience function for boolean enable/disable
31190      * @param {Boolean} enabled True to enable, false to disable
31191      */
31192     setDisabled : function(v){
31193         this[v !== true ? "enable" : "disable"]();
31194     },
31195
31196     // private
31197     onClick : function(e)
31198     {
31199         if(e){
31200             e.preventDefault();
31201         }
31202         if(e.button != 0){
31203             return;
31204         }
31205         if(!this.disabled){
31206             if(this.enableToggle){
31207                 this.toggle();
31208             }
31209             if(this.menu && !this.menu.isVisible()){
31210                 this.menu.show(this.el, this.menuAlign);
31211             }
31212             this.fireEvent("click", this, e);
31213             if(this.handler){
31214                 this.el.removeClass("x-btn-over");
31215                 this.handler.call(this.scope || this, this, e);
31216             }
31217         }
31218     },
31219     // private
31220     onMouseOver : function(e){
31221         if(!this.disabled){
31222             this.el.addClass("x-btn-over");
31223             this.fireEvent('mouseover', this, e);
31224         }
31225     },
31226     // private
31227     onMouseOut : function(e){
31228         if(!e.within(this.el,  true)){
31229             this.el.removeClass("x-btn-over");
31230             this.fireEvent('mouseout', this, e);
31231         }
31232     },
31233     // private
31234     onFocus : function(e){
31235         if(!this.disabled){
31236             this.el.addClass("x-btn-focus");
31237         }
31238     },
31239     // private
31240     onBlur : function(e){
31241         this.el.removeClass("x-btn-focus");
31242     },
31243     // private
31244     onMouseDown : function(e){
31245         if(!this.disabled && e.button == 0){
31246             this.el.addClass("x-btn-click");
31247             Roo.get(document).on('mouseup', this.onMouseUp, this);
31248         }
31249     },
31250     // private
31251     onMouseUp : function(e){
31252         if(e.button == 0){
31253             this.el.removeClass("x-btn-click");
31254             Roo.get(document).un('mouseup', this.onMouseUp, this);
31255         }
31256     },
31257     // private
31258     onMenuShow : function(e){
31259         this.el.addClass("x-btn-menu-active");
31260     },
31261     // private
31262     onMenuHide : function(e){
31263         this.el.removeClass("x-btn-menu-active");
31264     }   
31265 });
31266
31267 // Private utility class used by Button
31268 Roo.ButtonToggleMgr = function(){
31269    var groups = {};
31270    
31271    function toggleGroup(btn, state){
31272        if(state){
31273            var g = groups[btn.toggleGroup];
31274            for(var i = 0, l = g.length; i < l; i++){
31275                if(g[i] != btn){
31276                    g[i].toggle(false);
31277                }
31278            }
31279        }
31280    }
31281    
31282    return {
31283        register : function(btn){
31284            if(!btn.toggleGroup){
31285                return;
31286            }
31287            var g = groups[btn.toggleGroup];
31288            if(!g){
31289                g = groups[btn.toggleGroup] = [];
31290            }
31291            g.push(btn);
31292            btn.on("toggle", toggleGroup);
31293        },
31294        
31295        unregister : function(btn){
31296            if(!btn.toggleGroup){
31297                return;
31298            }
31299            var g = groups[btn.toggleGroup];
31300            if(g){
31301                g.remove(btn);
31302                btn.un("toggle", toggleGroup);
31303            }
31304        }
31305    };
31306 }();/*
31307  * Based on:
31308  * Ext JS Library 1.1.1
31309  * Copyright(c) 2006-2007, Ext JS, LLC.
31310  *
31311  * Originally Released Under LGPL - original licence link has changed is not relivant.
31312  *
31313  * Fork - LGPL
31314  * <script type="text/javascript">
31315  */
31316  
31317 /**
31318  * @class Roo.SplitButton
31319  * @extends Roo.Button
31320  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31321  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31322  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31323  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31324  * @cfg {String} arrowTooltip The title attribute of the arrow
31325  * @constructor
31326  * Create a new menu button
31327  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31328  * @param {Object} config The config object
31329  */
31330 Roo.SplitButton = function(renderTo, config){
31331     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31332     /**
31333      * @event arrowclick
31334      * Fires when this button's arrow is clicked
31335      * @param {SplitButton} this
31336      * @param {EventObject} e The click event
31337      */
31338     this.addEvents({"arrowclick":true});
31339 };
31340
31341 Roo.extend(Roo.SplitButton, Roo.Button, {
31342     render : function(renderTo){
31343         // this is one sweet looking template!
31344         var tpl = new Roo.Template(
31345             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31346             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31347             '<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>',
31348             "</tbody></table></td><td>",
31349             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31350             '<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>',
31351             "</tbody></table></td></tr></table>"
31352         );
31353         var btn = tpl.append(renderTo, [this.text, this.type], true);
31354         var btnEl = btn.child("button");
31355         if(this.cls){
31356             btn.addClass(this.cls);
31357         }
31358         if(this.icon){
31359             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31360         }
31361         if(this.iconCls){
31362             btnEl.addClass(this.iconCls);
31363             if(!this.cls){
31364                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31365             }
31366         }
31367         this.el = btn;
31368         if(this.handleMouseEvents){
31369             btn.on("mouseover", this.onMouseOver, this);
31370             btn.on("mouseout", this.onMouseOut, this);
31371             btn.on("mousedown", this.onMouseDown, this);
31372             btn.on("mouseup", this.onMouseUp, this);
31373         }
31374         btn.on(this.clickEvent, this.onClick, this);
31375         if(this.tooltip){
31376             if(typeof this.tooltip == 'object'){
31377                 Roo.QuickTips.tips(Roo.apply({
31378                       target: btnEl.id
31379                 }, this.tooltip));
31380             } else {
31381                 btnEl.dom[this.tooltipType] = this.tooltip;
31382             }
31383         }
31384         if(this.arrowTooltip){
31385             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31386         }
31387         if(this.hidden){
31388             this.hide();
31389         }
31390         if(this.disabled){
31391             this.disable();
31392         }
31393         if(this.pressed){
31394             this.el.addClass("x-btn-pressed");
31395         }
31396         if(Roo.isIE && !Roo.isIE7){
31397             this.autoWidth.defer(1, this);
31398         }else{
31399             this.autoWidth();
31400         }
31401         if(this.menu){
31402             this.menu.on("show", this.onMenuShow, this);
31403             this.menu.on("hide", this.onMenuHide, this);
31404         }
31405         this.fireEvent('render', this);
31406     },
31407
31408     // private
31409     autoWidth : function(){
31410         if(this.el){
31411             var tbl = this.el.child("table:first");
31412             var tbl2 = this.el.child("table:last");
31413             this.el.setWidth("auto");
31414             tbl.setWidth("auto");
31415             if(Roo.isIE7 && Roo.isStrict){
31416                 var ib = this.el.child('button:first');
31417                 if(ib && ib.getWidth() > 20){
31418                     ib.clip();
31419                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31420                 }
31421             }
31422             if(this.minWidth){
31423                 if(this.hidden){
31424                     this.el.beginMeasure();
31425                 }
31426                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31427                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31428                 }
31429                 if(this.hidden){
31430                     this.el.endMeasure();
31431                 }
31432             }
31433             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31434         } 
31435     },
31436     /**
31437      * Sets this button's click handler
31438      * @param {Function} handler The function to call when the button is clicked
31439      * @param {Object} scope (optional) Scope for the function passed above
31440      */
31441     setHandler : function(handler, scope){
31442         this.handler = handler;
31443         this.scope = scope;  
31444     },
31445     
31446     /**
31447      * Sets this button's arrow click handler
31448      * @param {Function} handler The function to call when the arrow is clicked
31449      * @param {Object} scope (optional) Scope for the function passed above
31450      */
31451     setArrowHandler : function(handler, scope){
31452         this.arrowHandler = handler;
31453         this.scope = scope;  
31454     },
31455     
31456     /**
31457      * Focus the button
31458      */
31459     focus : function(){
31460         if(this.el){
31461             this.el.child("button:first").focus();
31462         }
31463     },
31464
31465     // private
31466     onClick : function(e){
31467         e.preventDefault();
31468         if(!this.disabled){
31469             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31470                 if(this.menu && !this.menu.isVisible()){
31471                     this.menu.show(this.el, this.menuAlign);
31472                 }
31473                 this.fireEvent("arrowclick", this, e);
31474                 if(this.arrowHandler){
31475                     this.arrowHandler.call(this.scope || this, this, e);
31476                 }
31477             }else{
31478                 this.fireEvent("click", this, e);
31479                 if(this.handler){
31480                     this.handler.call(this.scope || this, this, e);
31481                 }
31482             }
31483         }
31484     },
31485     // private
31486     onMouseDown : function(e){
31487         if(!this.disabled){
31488             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31489         }
31490     },
31491     // private
31492     onMouseUp : function(e){
31493         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31494     }   
31495 });
31496
31497
31498 // backwards compat
31499 Roo.MenuButton = Roo.SplitButton;/*
31500  * Based on:
31501  * Ext JS Library 1.1.1
31502  * Copyright(c) 2006-2007, Ext JS, LLC.
31503  *
31504  * Originally Released Under LGPL - original licence link has changed is not relivant.
31505  *
31506  * Fork - LGPL
31507  * <script type="text/javascript">
31508  */
31509
31510 /**
31511  * @class Roo.Toolbar
31512  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31513  * Basic Toolbar class.
31514  * @constructor
31515  * Creates a new Toolbar
31516  * @param {Object} container The config object
31517  */ 
31518 Roo.Toolbar = function(container, buttons, config)
31519 {
31520     /// old consturctor format still supported..
31521     if(container instanceof Array){ // omit the container for later rendering
31522         buttons = container;
31523         config = buttons;
31524         container = null;
31525     }
31526     if (typeof(container) == 'object' && container.xtype) {
31527         config = container;
31528         container = config.container;
31529         buttons = config.buttons || []; // not really - use items!!
31530     }
31531     var xitems = [];
31532     if (config && config.items) {
31533         xitems = config.items;
31534         delete config.items;
31535     }
31536     Roo.apply(this, config);
31537     this.buttons = buttons;
31538     
31539     if(container){
31540         this.render(container);
31541     }
31542     this.xitems = xitems;
31543     Roo.each(xitems, function(b) {
31544         this.add(b);
31545     }, this);
31546     
31547 };
31548
31549 Roo.Toolbar.prototype = {
31550     /**
31551      * @cfg {Array} items
31552      * array of button configs or elements to add (will be converted to a MixedCollection)
31553      */
31554     items: false,
31555     /**
31556      * @cfg {String/HTMLElement/Element} container
31557      * The id or element that will contain the toolbar
31558      */
31559     // private
31560     render : function(ct){
31561         this.el = Roo.get(ct);
31562         if(this.cls){
31563             this.el.addClass(this.cls);
31564         }
31565         // using a table allows for vertical alignment
31566         // 100% width is needed by Safari...
31567         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31568         this.tr = this.el.child("tr", true);
31569         var autoId = 0;
31570         this.items = new Roo.util.MixedCollection(false, function(o){
31571             return o.id || ("item" + (++autoId));
31572         });
31573         if(this.buttons){
31574             this.add.apply(this, this.buttons);
31575             delete this.buttons;
31576         }
31577     },
31578
31579     /**
31580      * Adds element(s) to the toolbar -- this function takes a variable number of 
31581      * arguments of mixed type and adds them to the toolbar.
31582      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31583      * <ul>
31584      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31585      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31586      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31587      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31588      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31589      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31590      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31591      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31592      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31593      * </ul>
31594      * @param {Mixed} arg2
31595      * @param {Mixed} etc.
31596      */
31597     add : function(){
31598         var a = arguments, l = a.length;
31599         for(var i = 0; i < l; i++){
31600             this._add(a[i]);
31601         }
31602     },
31603     // private..
31604     _add : function(el) {
31605         
31606         if (el.xtype) {
31607             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31608         }
31609         
31610         if (el.applyTo){ // some kind of form field
31611             return this.addField(el);
31612         } 
31613         if (el.render){ // some kind of Toolbar.Item
31614             return this.addItem(el);
31615         }
31616         if (typeof el == "string"){ // string
31617             if(el == "separator" || el == "-"){
31618                 return this.addSeparator();
31619             }
31620             if (el == " "){
31621                 return this.addSpacer();
31622             }
31623             if(el == "->"){
31624                 return this.addFill();
31625             }
31626             return this.addText(el);
31627             
31628         }
31629         if(el.tagName){ // element
31630             return this.addElement(el);
31631         }
31632         if(typeof el == "object"){ // must be button config?
31633             return this.addButton(el);
31634         }
31635         // and now what?!?!
31636         return false;
31637         
31638     },
31639     
31640     /**
31641      * Add an Xtype element
31642      * @param {Object} xtype Xtype Object
31643      * @return {Object} created Object
31644      */
31645     addxtype : function(e){
31646         return this.add(e);  
31647     },
31648     
31649     /**
31650      * Returns the Element for this toolbar.
31651      * @return {Roo.Element}
31652      */
31653     getEl : function(){
31654         return this.el;  
31655     },
31656     
31657     /**
31658      * Adds a separator
31659      * @return {Roo.Toolbar.Item} The separator item
31660      */
31661     addSeparator : function(){
31662         return this.addItem(new Roo.Toolbar.Separator());
31663     },
31664
31665     /**
31666      * Adds a spacer element
31667      * @return {Roo.Toolbar.Spacer} The spacer item
31668      */
31669     addSpacer : function(){
31670         return this.addItem(new Roo.Toolbar.Spacer());
31671     },
31672
31673     /**
31674      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31675      * @return {Roo.Toolbar.Fill} The fill item
31676      */
31677     addFill : function(){
31678         return this.addItem(new Roo.Toolbar.Fill());
31679     },
31680
31681     /**
31682      * Adds any standard HTML element to the toolbar
31683      * @param {String/HTMLElement/Element} el The element or id of the element to add
31684      * @return {Roo.Toolbar.Item} The element's item
31685      */
31686     addElement : function(el){
31687         return this.addItem(new Roo.Toolbar.Item(el));
31688     },
31689     /**
31690      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31691      * @type Roo.util.MixedCollection  
31692      */
31693     items : false,
31694      
31695     /**
31696      * Adds any Toolbar.Item or subclass
31697      * @param {Roo.Toolbar.Item} item
31698      * @return {Roo.Toolbar.Item} The item
31699      */
31700     addItem : function(item){
31701         var td = this.nextBlock();
31702         item.render(td);
31703         this.items.add(item);
31704         return item;
31705     },
31706     
31707     /**
31708      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31709      * @param {Object/Array} config A button config or array of configs
31710      * @return {Roo.Toolbar.Button/Array}
31711      */
31712     addButton : function(config){
31713         if(config instanceof Array){
31714             var buttons = [];
31715             for(var i = 0, len = config.length; i < len; i++) {
31716                 buttons.push(this.addButton(config[i]));
31717             }
31718             return buttons;
31719         }
31720         var b = config;
31721         if(!(config instanceof Roo.Toolbar.Button)){
31722             b = config.split ?
31723                 new Roo.Toolbar.SplitButton(config) :
31724                 new Roo.Toolbar.Button(config);
31725         }
31726         var td = this.nextBlock();
31727         b.render(td);
31728         this.items.add(b);
31729         return b;
31730     },
31731     
31732     /**
31733      * Adds text to the toolbar
31734      * @param {String} text The text to add
31735      * @return {Roo.Toolbar.Item} The element's item
31736      */
31737     addText : function(text){
31738         return this.addItem(new Roo.Toolbar.TextItem(text));
31739     },
31740     
31741     /**
31742      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31743      * @param {Number} index The index where the item is to be inserted
31744      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31745      * @return {Roo.Toolbar.Button/Item}
31746      */
31747     insertButton : function(index, item){
31748         if(item instanceof Array){
31749             var buttons = [];
31750             for(var i = 0, len = item.length; i < len; i++) {
31751                buttons.push(this.insertButton(index + i, item[i]));
31752             }
31753             return buttons;
31754         }
31755         if (!(item instanceof Roo.Toolbar.Button)){
31756            item = new Roo.Toolbar.Button(item);
31757         }
31758         var td = document.createElement("td");
31759         this.tr.insertBefore(td, this.tr.childNodes[index]);
31760         item.render(td);
31761         this.items.insert(index, item);
31762         return item;
31763     },
31764     
31765     /**
31766      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31767      * @param {Object} config
31768      * @return {Roo.Toolbar.Item} The element's item
31769      */
31770     addDom : function(config, returnEl){
31771         var td = this.nextBlock();
31772         Roo.DomHelper.overwrite(td, config);
31773         var ti = new Roo.Toolbar.Item(td.firstChild);
31774         ti.render(td);
31775         this.items.add(ti);
31776         return ti;
31777     },
31778
31779     /**
31780      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31781      * @type Roo.util.MixedCollection  
31782      */
31783     fields : false,
31784     
31785     /**
31786      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31787      * Note: the field should not have been rendered yet. For a field that has already been
31788      * rendered, use {@link #addElement}.
31789      * @param {Roo.form.Field} field
31790      * @return {Roo.ToolbarItem}
31791      */
31792      
31793       
31794     addField : function(field) {
31795         if (!this.fields) {
31796             var autoId = 0;
31797             this.fields = new Roo.util.MixedCollection(false, function(o){
31798                 return o.id || ("item" + (++autoId));
31799             });
31800
31801         }
31802         
31803         var td = this.nextBlock();
31804         field.render(td);
31805         var ti = new Roo.Toolbar.Item(td.firstChild);
31806         ti.render(td);
31807         this.items.add(ti);
31808         this.fields.add(field);
31809         return ti;
31810     },
31811     /**
31812      * Hide the toolbar
31813      * @method hide
31814      */
31815      
31816       
31817     hide : function()
31818     {
31819         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31820         this.el.child('div').hide();
31821     },
31822     /**
31823      * Show the toolbar
31824      * @method show
31825      */
31826     show : function()
31827     {
31828         this.el.child('div').show();
31829     },
31830       
31831     // private
31832     nextBlock : function(){
31833         var td = document.createElement("td");
31834         this.tr.appendChild(td);
31835         return td;
31836     },
31837
31838     // private
31839     destroy : function(){
31840         if(this.items){ // rendered?
31841             Roo.destroy.apply(Roo, this.items.items);
31842         }
31843         if(this.fields){ // rendered?
31844             Roo.destroy.apply(Roo, this.fields.items);
31845         }
31846         Roo.Element.uncache(this.el, this.tr);
31847     }
31848 };
31849
31850 /**
31851  * @class Roo.Toolbar.Item
31852  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31853  * @constructor
31854  * Creates a new Item
31855  * @param {HTMLElement} el 
31856  */
31857 Roo.Toolbar.Item = function(el){
31858     var cfg = {};
31859     if (typeof (el.xtype) != 'undefined') {
31860         cfg = el;
31861         el = cfg.el;
31862     }
31863     
31864     this.el = Roo.getDom(el);
31865     this.id = Roo.id(this.el);
31866     this.hidden = false;
31867     
31868     this.addEvents({
31869          /**
31870              * @event render
31871              * Fires when the button is rendered
31872              * @param {Button} this
31873              */
31874         'render': true
31875     });
31876     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31877 };
31878 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31879 //Roo.Toolbar.Item.prototype = {
31880     
31881     /**
31882      * Get this item's HTML Element
31883      * @return {HTMLElement}
31884      */
31885     getEl : function(){
31886        return this.el;  
31887     },
31888
31889     // private
31890     render : function(td){
31891         
31892          this.td = td;
31893         td.appendChild(this.el);
31894         
31895         this.fireEvent('render', this);
31896     },
31897     
31898     /**
31899      * Removes and destroys this item.
31900      */
31901     destroy : function(){
31902         this.td.parentNode.removeChild(this.td);
31903     },
31904     
31905     /**
31906      * Shows this item.
31907      */
31908     show: function(){
31909         this.hidden = false;
31910         this.td.style.display = "";
31911     },
31912     
31913     /**
31914      * Hides this item.
31915      */
31916     hide: function(){
31917         this.hidden = true;
31918         this.td.style.display = "none";
31919     },
31920     
31921     /**
31922      * Convenience function for boolean show/hide.
31923      * @param {Boolean} visible true to show/false to hide
31924      */
31925     setVisible: function(visible){
31926         if(visible) {
31927             this.show();
31928         }else{
31929             this.hide();
31930         }
31931     },
31932     
31933     /**
31934      * Try to focus this item.
31935      */
31936     focus : function(){
31937         Roo.fly(this.el).focus();
31938     },
31939     
31940     /**
31941      * Disables this item.
31942      */
31943     disable : function(){
31944         Roo.fly(this.td).addClass("x-item-disabled");
31945         this.disabled = true;
31946         this.el.disabled = true;
31947     },
31948     
31949     /**
31950      * Enables this item.
31951      */
31952     enable : function(){
31953         Roo.fly(this.td).removeClass("x-item-disabled");
31954         this.disabled = false;
31955         this.el.disabled = false;
31956     }
31957 });
31958
31959
31960 /**
31961  * @class Roo.Toolbar.Separator
31962  * @extends Roo.Toolbar.Item
31963  * A simple toolbar separator class
31964  * @constructor
31965  * Creates a new Separator
31966  */
31967 Roo.Toolbar.Separator = function(cfg){
31968     
31969     var s = document.createElement("span");
31970     s.className = "ytb-sep";
31971     if (cfg) {
31972         cfg.el = s;
31973     }
31974     
31975     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31976 };
31977 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31978     enable:Roo.emptyFn,
31979     disable:Roo.emptyFn,
31980     focus:Roo.emptyFn
31981 });
31982
31983 /**
31984  * @class Roo.Toolbar.Spacer
31985  * @extends Roo.Toolbar.Item
31986  * A simple element that adds extra horizontal space to a toolbar.
31987  * @constructor
31988  * Creates a new Spacer
31989  */
31990 Roo.Toolbar.Spacer = function(cfg){
31991     var s = document.createElement("div");
31992     s.className = "ytb-spacer";
31993     if (cfg) {
31994         cfg.el = s;
31995     }
31996     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31997 };
31998 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31999     enable:Roo.emptyFn,
32000     disable:Roo.emptyFn,
32001     focus:Roo.emptyFn
32002 });
32003
32004 /**
32005  * @class Roo.Toolbar.Fill
32006  * @extends Roo.Toolbar.Spacer
32007  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
32008  * @constructor
32009  * Creates a new Spacer
32010  */
32011 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
32012     // private
32013     render : function(td){
32014         td.style.width = '100%';
32015         Roo.Toolbar.Fill.superclass.render.call(this, td);
32016     }
32017 });
32018
32019 /**
32020  * @class Roo.Toolbar.TextItem
32021  * @extends Roo.Toolbar.Item
32022  * A simple class that renders text directly into a toolbar.
32023  * @constructor
32024  * Creates a new TextItem
32025  * @cfg {string} text 
32026  */
32027 Roo.Toolbar.TextItem = function(cfg){
32028     var  text = cfg || "";
32029     if (typeof(cfg) == 'object') {
32030         text = cfg.text || "";
32031     }  else {
32032         cfg = null;
32033     }
32034     var s = document.createElement("span");
32035     s.className = "ytb-text";
32036     s.innerHTML = text;
32037     if (cfg) {
32038         cfg.el  = s;
32039     }
32040     
32041     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
32042 };
32043 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
32044     
32045      
32046     enable:Roo.emptyFn,
32047     disable:Roo.emptyFn,
32048     focus:Roo.emptyFn,
32049      /**
32050      * Shows this button
32051      */
32052     show: function(){
32053         this.hidden = false;
32054         this.el.style.display = "";
32055     },
32056     
32057     /**
32058      * Hides this button
32059      */
32060     hide: function(){
32061         this.hidden = true;
32062         this.el.style.display = "none";
32063     }
32064     
32065 });
32066
32067 /**
32068  * @class Roo.Toolbar.Button
32069  * @extends Roo.Button
32070  * A button that renders into a toolbar.
32071  * @constructor
32072  * Creates a new Button
32073  * @param {Object} config A standard {@link Roo.Button} config object
32074  */
32075 Roo.Toolbar.Button = function(config){
32076     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
32077 };
32078 Roo.extend(Roo.Toolbar.Button, Roo.Button,
32079 {
32080     
32081     
32082     render : function(td){
32083         this.td = td;
32084         Roo.Toolbar.Button.superclass.render.call(this, td);
32085     },
32086     
32087     /**
32088      * Removes and destroys this button
32089      */
32090     destroy : function(){
32091         Roo.Toolbar.Button.superclass.destroy.call(this);
32092         this.td.parentNode.removeChild(this.td);
32093     },
32094     
32095     /**
32096      * Shows this button
32097      */
32098     show: function(){
32099         this.hidden = false;
32100         this.td.style.display = "";
32101     },
32102     
32103     /**
32104      * Hides this button
32105      */
32106     hide: function(){
32107         this.hidden = true;
32108         this.td.style.display = "none";
32109     },
32110
32111     /**
32112      * Disables this item
32113      */
32114     disable : function(){
32115         Roo.fly(this.td).addClass("x-item-disabled");
32116         this.disabled = true;
32117     },
32118
32119     /**
32120      * Enables this item
32121      */
32122     enable : function(){
32123         Roo.fly(this.td).removeClass("x-item-disabled");
32124         this.disabled = false;
32125     }
32126 });
32127 // backwards compat
32128 Roo.ToolbarButton = Roo.Toolbar.Button;
32129
32130 /**
32131  * @class Roo.Toolbar.SplitButton
32132  * @extends Roo.SplitButton
32133  * A menu button that renders into a toolbar.
32134  * @constructor
32135  * Creates a new SplitButton
32136  * @param {Object} config A standard {@link Roo.SplitButton} config object
32137  */
32138 Roo.Toolbar.SplitButton = function(config){
32139     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
32140 };
32141 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
32142     render : function(td){
32143         this.td = td;
32144         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
32145     },
32146     
32147     /**
32148      * Removes and destroys this button
32149      */
32150     destroy : function(){
32151         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
32152         this.td.parentNode.removeChild(this.td);
32153     },
32154     
32155     /**
32156      * Shows this button
32157      */
32158     show: function(){
32159         this.hidden = false;
32160         this.td.style.display = "";
32161     },
32162     
32163     /**
32164      * Hides this button
32165      */
32166     hide: function(){
32167         this.hidden = true;
32168         this.td.style.display = "none";
32169     }
32170 });
32171
32172 // backwards compat
32173 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
32174  * Based on:
32175  * Ext JS Library 1.1.1
32176  * Copyright(c) 2006-2007, Ext JS, LLC.
32177  *
32178  * Originally Released Under LGPL - original licence link has changed is not relivant.
32179  *
32180  * Fork - LGPL
32181  * <script type="text/javascript">
32182  */
32183  
32184 /**
32185  * @class Roo.PagingToolbar
32186  * @extends Roo.Toolbar
32187  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
32188  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32189  * @constructor
32190  * Create a new PagingToolbar
32191  * @param {Object} config The config object
32192  */
32193 Roo.PagingToolbar = function(el, ds, config)
32194 {
32195     // old args format still supported... - xtype is prefered..
32196     if (typeof(el) == 'object' && el.xtype) {
32197         // created from xtype...
32198         config = el;
32199         ds = el.dataSource;
32200         el = config.container;
32201     }
32202     var items = [];
32203     if (config.items) {
32204         items = config.items;
32205         config.items = [];
32206     }
32207     
32208     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32209     this.ds = ds;
32210     this.cursor = 0;
32211     this.renderButtons(this.el);
32212     this.bind(ds);
32213     
32214     // supprot items array.
32215    
32216     Roo.each(items, function(e) {
32217         this.add(Roo.factory(e));
32218     },this);
32219     
32220 };
32221
32222 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32223    
32224     /**
32225      * @cfg {String/HTMLElement/Element} container
32226      * container The id or element that will contain the toolbar
32227      */
32228     /**
32229      * @cfg {Boolean} displayInfo
32230      * True to display the displayMsg (defaults to false)
32231      */
32232     
32233     
32234     /**
32235      * @cfg {Number} pageSize
32236      * The number of records to display per page (defaults to 20)
32237      */
32238     pageSize: 20,
32239     /**
32240      * @cfg {String} displayMsg
32241      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32242      */
32243     displayMsg : 'Displaying {0} - {1} of {2}',
32244     /**
32245      * @cfg {String} emptyMsg
32246      * The message to display when no records are found (defaults to "No data to display")
32247      */
32248     emptyMsg : 'No data to display',
32249     /**
32250      * Customizable piece of the default paging text (defaults to "Page")
32251      * @type String
32252      */
32253     beforePageText : "Page",
32254     /**
32255      * Customizable piece of the default paging text (defaults to "of %0")
32256      * @type String
32257      */
32258     afterPageText : "of {0}",
32259     /**
32260      * Customizable piece of the default paging text (defaults to "First Page")
32261      * @type String
32262      */
32263     firstText : "First Page",
32264     /**
32265      * Customizable piece of the default paging text (defaults to "Previous Page")
32266      * @type String
32267      */
32268     prevText : "Previous Page",
32269     /**
32270      * Customizable piece of the default paging text (defaults to "Next Page")
32271      * @type String
32272      */
32273     nextText : "Next Page",
32274     /**
32275      * Customizable piece of the default paging text (defaults to "Last Page")
32276      * @type String
32277      */
32278     lastText : "Last Page",
32279     /**
32280      * Customizable piece of the default paging text (defaults to "Refresh")
32281      * @type String
32282      */
32283     refreshText : "Refresh",
32284
32285     // private
32286     renderButtons : function(el){
32287         Roo.PagingToolbar.superclass.render.call(this, el);
32288         this.first = this.addButton({
32289             tooltip: this.firstText,
32290             cls: "x-btn-icon x-grid-page-first",
32291             disabled: true,
32292             handler: this.onClick.createDelegate(this, ["first"])
32293         });
32294         this.prev = this.addButton({
32295             tooltip: this.prevText,
32296             cls: "x-btn-icon x-grid-page-prev",
32297             disabled: true,
32298             handler: this.onClick.createDelegate(this, ["prev"])
32299         });
32300         //this.addSeparator();
32301         this.add(this.beforePageText);
32302         this.field = Roo.get(this.addDom({
32303            tag: "input",
32304            type: "text",
32305            size: "3",
32306            value: "1",
32307            cls: "x-grid-page-number"
32308         }).el);
32309         this.field.on("keydown", this.onPagingKeydown, this);
32310         this.field.on("focus", function(){this.dom.select();});
32311         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32312         this.field.setHeight(18);
32313         //this.addSeparator();
32314         this.next = this.addButton({
32315             tooltip: this.nextText,
32316             cls: "x-btn-icon x-grid-page-next",
32317             disabled: true,
32318             handler: this.onClick.createDelegate(this, ["next"])
32319         });
32320         this.last = this.addButton({
32321             tooltip: this.lastText,
32322             cls: "x-btn-icon x-grid-page-last",
32323             disabled: true,
32324             handler: this.onClick.createDelegate(this, ["last"])
32325         });
32326         //this.addSeparator();
32327         this.loading = this.addButton({
32328             tooltip: this.refreshText,
32329             cls: "x-btn-icon x-grid-loading",
32330             handler: this.onClick.createDelegate(this, ["refresh"])
32331         });
32332
32333         if(this.displayInfo){
32334             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32335         }
32336     },
32337
32338     // private
32339     updateInfo : function(){
32340         if(this.displayEl){
32341             var count = this.ds.getCount();
32342             var msg = count == 0 ?
32343                 this.emptyMsg :
32344                 String.format(
32345                     this.displayMsg,
32346                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32347                 );
32348             this.displayEl.update(msg);
32349         }
32350     },
32351
32352     // private
32353     onLoad : function(ds, r, o){
32354        this.cursor = o.params ? o.params.start : 0;
32355        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32356
32357        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32358        this.field.dom.value = ap;
32359        this.first.setDisabled(ap == 1);
32360        this.prev.setDisabled(ap == 1);
32361        this.next.setDisabled(ap == ps);
32362        this.last.setDisabled(ap == ps);
32363        this.loading.enable();
32364        this.updateInfo();
32365     },
32366
32367     // private
32368     getPageData : function(){
32369         var total = this.ds.getTotalCount();
32370         return {
32371             total : total,
32372             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32373             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32374         };
32375     },
32376
32377     // private
32378     onLoadError : function(){
32379         this.loading.enable();
32380     },
32381
32382     // private
32383     onPagingKeydown : function(e){
32384         var k = e.getKey();
32385         var d = this.getPageData();
32386         if(k == e.RETURN){
32387             var v = this.field.dom.value, pageNum;
32388             if(!v || isNaN(pageNum = parseInt(v, 10))){
32389                 this.field.dom.value = d.activePage;
32390                 return;
32391             }
32392             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32393             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32394             e.stopEvent();
32395         }
32396         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))
32397         {
32398           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32399           this.field.dom.value = pageNum;
32400           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32401           e.stopEvent();
32402         }
32403         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32404         {
32405           var v = this.field.dom.value, pageNum; 
32406           var increment = (e.shiftKey) ? 10 : 1;
32407           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32408             increment *= -1;
32409           }
32410           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32411             this.field.dom.value = d.activePage;
32412             return;
32413           }
32414           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32415           {
32416             this.field.dom.value = parseInt(v, 10) + increment;
32417             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32418             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32419           }
32420           e.stopEvent();
32421         }
32422     },
32423
32424     // private
32425     beforeLoad : function(){
32426         if(this.loading){
32427             this.loading.disable();
32428         }
32429     },
32430     /**
32431      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
32432      * @param {String} which (first|prev|next|last|refresh)  which button to press.
32433      *
32434      */
32435     // private
32436     onClick : function(which){
32437         var ds = this.ds;
32438         switch(which){
32439             case "first":
32440                 ds.load({params:{start: 0, limit: this.pageSize}});
32441             break;
32442             case "prev":
32443                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32444             break;
32445             case "next":
32446                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32447             break;
32448             case "last":
32449                 var total = ds.getTotalCount();
32450                 var extra = total % this.pageSize;
32451                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32452                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32453             break;
32454             case "refresh":
32455                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32456             break;
32457         }
32458     },
32459
32460     /**
32461      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32462      * @param {Roo.data.Store} store The data store to unbind
32463      */
32464     unbind : function(ds){
32465         ds.un("beforeload", this.beforeLoad, this);
32466         ds.un("load", this.onLoad, this);
32467         ds.un("loadexception", this.onLoadError, this);
32468         ds.un("remove", this.updateInfo, this);
32469         ds.un("add", this.updateInfo, this);
32470         this.ds = undefined;
32471     },
32472
32473     /**
32474      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32475      * @param {Roo.data.Store} store The data store to bind
32476      */
32477     bind : function(ds){
32478         ds.on("beforeload", this.beforeLoad, this);
32479         ds.on("load", this.onLoad, this);
32480         ds.on("loadexception", this.onLoadError, this);
32481         ds.on("remove", this.updateInfo, this);
32482         ds.on("add", this.updateInfo, this);
32483         this.ds = ds;
32484     }
32485 });/*
32486  * Based on:
32487  * Ext JS Library 1.1.1
32488  * Copyright(c) 2006-2007, Ext JS, LLC.
32489  *
32490  * Originally Released Under LGPL - original licence link has changed is not relivant.
32491  *
32492  * Fork - LGPL
32493  * <script type="text/javascript">
32494  */
32495
32496 /**
32497  * @class Roo.Resizable
32498  * @extends Roo.util.Observable
32499  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32500  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32501  * 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
32502  * the element will be wrapped for you automatically.</p>
32503  * <p>Here is the list of valid resize handles:</p>
32504  * <pre>
32505 Value   Description
32506 ------  -------------------
32507  'n'     north
32508  's'     south
32509  'e'     east
32510  'w'     west
32511  'nw'    northwest
32512  'sw'    southwest
32513  'se'    southeast
32514  'ne'    northeast
32515  'hd'    horizontal drag
32516  'all'   all
32517 </pre>
32518  * <p>Here's an example showing the creation of a typical Resizable:</p>
32519  * <pre><code>
32520 var resizer = new Roo.Resizable("element-id", {
32521     handles: 'all',
32522     minWidth: 200,
32523     minHeight: 100,
32524     maxWidth: 500,
32525     maxHeight: 400,
32526     pinned: true
32527 });
32528 resizer.on("resize", myHandler);
32529 </code></pre>
32530  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32531  * resizer.east.setDisplayed(false);</p>
32532  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32533  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32534  * resize operation's new size (defaults to [0, 0])
32535  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32536  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32537  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32538  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32539  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32540  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32541  * @cfg {Number} width The width of the element in pixels (defaults to null)
32542  * @cfg {Number} height The height of the element in pixels (defaults to null)
32543  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32544  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32545  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32546  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32547  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32548  * in favor of the handles config option (defaults to false)
32549  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32550  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32551  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32552  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32553  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32554  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32555  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32556  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32557  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32558  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32559  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32560  * @constructor
32561  * Create a new resizable component
32562  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32563  * @param {Object} config configuration options
32564   */
32565 Roo.Resizable = function(el, config)
32566 {
32567     this.el = Roo.get(el);
32568
32569     if(config && config.wrap){
32570         config.resizeChild = this.el;
32571         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32572         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32573         this.el.setStyle("overflow", "hidden");
32574         this.el.setPositioning(config.resizeChild.getPositioning());
32575         config.resizeChild.clearPositioning();
32576         if(!config.width || !config.height){
32577             var csize = config.resizeChild.getSize();
32578             this.el.setSize(csize.width, csize.height);
32579         }
32580         if(config.pinned && !config.adjustments){
32581             config.adjustments = "auto";
32582         }
32583     }
32584
32585     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32586     this.proxy.unselectable();
32587     this.proxy.enableDisplayMode('block');
32588
32589     Roo.apply(this, config);
32590
32591     if(this.pinned){
32592         this.disableTrackOver = true;
32593         this.el.addClass("x-resizable-pinned");
32594     }
32595     // if the element isn't positioned, make it relative
32596     var position = this.el.getStyle("position");
32597     if(position != "absolute" && position != "fixed"){
32598         this.el.setStyle("position", "relative");
32599     }
32600     if(!this.handles){ // no handles passed, must be legacy style
32601         this.handles = 's,e,se';
32602         if(this.multiDirectional){
32603             this.handles += ',n,w';
32604         }
32605     }
32606     if(this.handles == "all"){
32607         this.handles = "n s e w ne nw se sw";
32608     }
32609     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32610     var ps = Roo.Resizable.positions;
32611     for(var i = 0, len = hs.length; i < len; i++){
32612         if(hs[i] && ps[hs[i]]){
32613             var pos = ps[hs[i]];
32614             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32615         }
32616     }
32617     // legacy
32618     this.corner = this.southeast;
32619     
32620     // updateBox = the box can move..
32621     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32622         this.updateBox = true;
32623     }
32624
32625     this.activeHandle = null;
32626
32627     if(this.resizeChild){
32628         if(typeof this.resizeChild == "boolean"){
32629             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32630         }else{
32631             this.resizeChild = Roo.get(this.resizeChild, true);
32632         }
32633     }
32634     
32635     if(this.adjustments == "auto"){
32636         var rc = this.resizeChild;
32637         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32638         if(rc && (hw || hn)){
32639             rc.position("relative");
32640             rc.setLeft(hw ? hw.el.getWidth() : 0);
32641             rc.setTop(hn ? hn.el.getHeight() : 0);
32642         }
32643         this.adjustments = [
32644             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32645             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32646         ];
32647     }
32648
32649     if(this.draggable){
32650         this.dd = this.dynamic ?
32651             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32652         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32653     }
32654
32655     // public events
32656     this.addEvents({
32657         /**
32658          * @event beforeresize
32659          * Fired before resize is allowed. Set enabled to false to cancel resize.
32660          * @param {Roo.Resizable} this
32661          * @param {Roo.EventObject} e The mousedown event
32662          */
32663         "beforeresize" : true,
32664         /**
32665          * @event resizing
32666          * Fired a resizing.
32667          * @param {Roo.Resizable} this
32668          * @param {Number} x The new x position
32669          * @param {Number} y The new y position
32670          * @param {Number} w The new w width
32671          * @param {Number} h The new h hight
32672          * @param {Roo.EventObject} e The mouseup event
32673          */
32674         "resizing" : true,
32675         /**
32676          * @event resize
32677          * Fired after a resize.
32678          * @param {Roo.Resizable} this
32679          * @param {Number} width The new width
32680          * @param {Number} height The new height
32681          * @param {Roo.EventObject} e The mouseup event
32682          */
32683         "resize" : true
32684     });
32685
32686     if(this.width !== null && this.height !== null){
32687         this.resizeTo(this.width, this.height);
32688     }else{
32689         this.updateChildSize();
32690     }
32691     if(Roo.isIE){
32692         this.el.dom.style.zoom = 1;
32693     }
32694     Roo.Resizable.superclass.constructor.call(this);
32695 };
32696
32697 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32698         resizeChild : false,
32699         adjustments : [0, 0],
32700         minWidth : 5,
32701         minHeight : 5,
32702         maxWidth : 10000,
32703         maxHeight : 10000,
32704         enabled : true,
32705         animate : false,
32706         duration : .35,
32707         dynamic : false,
32708         handles : false,
32709         multiDirectional : false,
32710         disableTrackOver : false,
32711         easing : 'easeOutStrong',
32712         widthIncrement : 0,
32713         heightIncrement : 0,
32714         pinned : false,
32715         width : null,
32716         height : null,
32717         preserveRatio : false,
32718         transparent: false,
32719         minX: 0,
32720         minY: 0,
32721         draggable: false,
32722
32723         /**
32724          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32725          */
32726         constrainTo: undefined,
32727         /**
32728          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32729          */
32730         resizeRegion: undefined,
32731
32732
32733     /**
32734      * Perform a manual resize
32735      * @param {Number} width
32736      * @param {Number} height
32737      */
32738     resizeTo : function(width, height){
32739         this.el.setSize(width, height);
32740         this.updateChildSize();
32741         this.fireEvent("resize", this, width, height, null);
32742     },
32743
32744     // private
32745     startSizing : function(e, handle){
32746         this.fireEvent("beforeresize", this, e);
32747         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32748
32749             if(!this.overlay){
32750                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32751                 this.overlay.unselectable();
32752                 this.overlay.enableDisplayMode("block");
32753                 this.overlay.on("mousemove", this.onMouseMove, this);
32754                 this.overlay.on("mouseup", this.onMouseUp, this);
32755             }
32756             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32757
32758             this.resizing = true;
32759             this.startBox = this.el.getBox();
32760             this.startPoint = e.getXY();
32761             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32762                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32763
32764             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32765             this.overlay.show();
32766
32767             if(this.constrainTo) {
32768                 var ct = Roo.get(this.constrainTo);
32769                 this.resizeRegion = ct.getRegion().adjust(
32770                     ct.getFrameWidth('t'),
32771                     ct.getFrameWidth('l'),
32772                     -ct.getFrameWidth('b'),
32773                     -ct.getFrameWidth('r')
32774                 );
32775             }
32776
32777             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32778             this.proxy.show();
32779             this.proxy.setBox(this.startBox);
32780             if(!this.dynamic){
32781                 this.proxy.setStyle('visibility', 'visible');
32782             }
32783         }
32784     },
32785
32786     // private
32787     onMouseDown : function(handle, e){
32788         if(this.enabled){
32789             e.stopEvent();
32790             this.activeHandle = handle;
32791             this.startSizing(e, handle);
32792         }
32793     },
32794
32795     // private
32796     onMouseUp : function(e){
32797         var size = this.resizeElement();
32798         this.resizing = false;
32799         this.handleOut();
32800         this.overlay.hide();
32801         this.proxy.hide();
32802         this.fireEvent("resize", this, size.width, size.height, e);
32803     },
32804
32805     // private
32806     updateChildSize : function(){
32807         
32808         if(this.resizeChild){
32809             var el = this.el;
32810             var child = this.resizeChild;
32811             var adj = this.adjustments;
32812             if(el.dom.offsetWidth){
32813                 var b = el.getSize(true);
32814                 child.setSize(b.width+adj[0], b.height+adj[1]);
32815             }
32816             // Second call here for IE
32817             // The first call enables instant resizing and
32818             // the second call corrects scroll bars if they
32819             // exist
32820             if(Roo.isIE){
32821                 setTimeout(function(){
32822                     if(el.dom.offsetWidth){
32823                         var b = el.getSize(true);
32824                         child.setSize(b.width+adj[0], b.height+adj[1]);
32825                     }
32826                 }, 10);
32827             }
32828         }
32829     },
32830
32831     // private
32832     snap : function(value, inc, min){
32833         if(!inc || !value) {
32834             return value;
32835         }
32836         var newValue = value;
32837         var m = value % inc;
32838         if(m > 0){
32839             if(m > (inc/2)){
32840                 newValue = value + (inc-m);
32841             }else{
32842                 newValue = value - m;
32843             }
32844         }
32845         return Math.max(min, newValue);
32846     },
32847
32848     // private
32849     resizeElement : function(){
32850         var box = this.proxy.getBox();
32851         if(this.updateBox){
32852             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32853         }else{
32854             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32855         }
32856         this.updateChildSize();
32857         if(!this.dynamic){
32858             this.proxy.hide();
32859         }
32860         return box;
32861     },
32862
32863     // private
32864     constrain : function(v, diff, m, mx){
32865         if(v - diff < m){
32866             diff = v - m;
32867         }else if(v - diff > mx){
32868             diff = mx - v;
32869         }
32870         return diff;
32871     },
32872
32873     // private
32874     onMouseMove : function(e){
32875         
32876         if(this.enabled){
32877             try{// try catch so if something goes wrong the user doesn't get hung
32878
32879             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32880                 return;
32881             }
32882
32883             //var curXY = this.startPoint;
32884             var curSize = this.curSize || this.startBox;
32885             var x = this.startBox.x, y = this.startBox.y;
32886             var ox = x, oy = y;
32887             var w = curSize.width, h = curSize.height;
32888             var ow = w, oh = h;
32889             var mw = this.minWidth, mh = this.minHeight;
32890             var mxw = this.maxWidth, mxh = this.maxHeight;
32891             var wi = this.widthIncrement;
32892             var hi = this.heightIncrement;
32893
32894             var eventXY = e.getXY();
32895             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32896             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32897
32898             var pos = this.activeHandle.position;
32899
32900             switch(pos){
32901                 case "east":
32902                     w += diffX;
32903                     w = Math.min(Math.max(mw, w), mxw);
32904                     break;
32905              
32906                 case "south":
32907                     h += diffY;
32908                     h = Math.min(Math.max(mh, h), mxh);
32909                     break;
32910                 case "southeast":
32911                     w += diffX;
32912                     h += diffY;
32913                     w = Math.min(Math.max(mw, w), mxw);
32914                     h = Math.min(Math.max(mh, h), mxh);
32915                     break;
32916                 case "north":
32917                     diffY = this.constrain(h, diffY, mh, mxh);
32918                     y += diffY;
32919                     h -= diffY;
32920                     break;
32921                 case "hdrag":
32922                     
32923                     if (wi) {
32924                         var adiffX = Math.abs(diffX);
32925                         var sub = (adiffX % wi); // how much 
32926                         if (sub > (wi/2)) { // far enough to snap
32927                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32928                         } else {
32929                             // remove difference.. 
32930                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32931                         }
32932                     }
32933                     x += diffX;
32934                     x = Math.max(this.minX, x);
32935                     break;
32936                 case "west":
32937                     diffX = this.constrain(w, diffX, mw, mxw);
32938                     x += diffX;
32939                     w -= diffX;
32940                     break;
32941                 case "northeast":
32942                     w += diffX;
32943                     w = Math.min(Math.max(mw, w), mxw);
32944                     diffY = this.constrain(h, diffY, mh, mxh);
32945                     y += diffY;
32946                     h -= diffY;
32947                     break;
32948                 case "northwest":
32949                     diffX = this.constrain(w, diffX, mw, mxw);
32950                     diffY = this.constrain(h, diffY, mh, mxh);
32951                     y += diffY;
32952                     h -= diffY;
32953                     x += diffX;
32954                     w -= diffX;
32955                     break;
32956                case "southwest":
32957                     diffX = this.constrain(w, diffX, mw, mxw);
32958                     h += diffY;
32959                     h = Math.min(Math.max(mh, h), mxh);
32960                     x += diffX;
32961                     w -= diffX;
32962                     break;
32963             }
32964
32965             var sw = this.snap(w, wi, mw);
32966             var sh = this.snap(h, hi, mh);
32967             if(sw != w || sh != h){
32968                 switch(pos){
32969                     case "northeast":
32970                         y -= sh - h;
32971                     break;
32972                     case "north":
32973                         y -= sh - h;
32974                         break;
32975                     case "southwest":
32976                         x -= sw - w;
32977                     break;
32978                     case "west":
32979                         x -= sw - w;
32980                         break;
32981                     case "northwest":
32982                         x -= sw - w;
32983                         y -= sh - h;
32984                     break;
32985                 }
32986                 w = sw;
32987                 h = sh;
32988             }
32989
32990             if(this.preserveRatio){
32991                 switch(pos){
32992                     case "southeast":
32993                     case "east":
32994                         h = oh * (w/ow);
32995                         h = Math.min(Math.max(mh, h), mxh);
32996                         w = ow * (h/oh);
32997                        break;
32998                     case "south":
32999                         w = ow * (h/oh);
33000                         w = Math.min(Math.max(mw, w), mxw);
33001                         h = oh * (w/ow);
33002                         break;
33003                     case "northeast":
33004                         w = ow * (h/oh);
33005                         w = Math.min(Math.max(mw, w), mxw);
33006                         h = oh * (w/ow);
33007                     break;
33008                     case "north":
33009                         var tw = w;
33010                         w = ow * (h/oh);
33011                         w = Math.min(Math.max(mw, w), mxw);
33012                         h = oh * (w/ow);
33013                         x += (tw - w) / 2;
33014                         break;
33015                     case "southwest":
33016                         h = oh * (w/ow);
33017                         h = Math.min(Math.max(mh, h), mxh);
33018                         var tw = w;
33019                         w = ow * (h/oh);
33020                         x += tw - w;
33021                         break;
33022                     case "west":
33023                         var th = h;
33024                         h = oh * (w/ow);
33025                         h = Math.min(Math.max(mh, h), mxh);
33026                         y += (th - h) / 2;
33027                         var tw = w;
33028                         w = ow * (h/oh);
33029                         x += tw - w;
33030                        break;
33031                     case "northwest":
33032                         var tw = w;
33033                         var th = h;
33034                         h = oh * (w/ow);
33035                         h = Math.min(Math.max(mh, h), mxh);
33036                         w = ow * (h/oh);
33037                         y += th - h;
33038                         x += tw - w;
33039                        break;
33040
33041                 }
33042             }
33043             if (pos == 'hdrag') {
33044                 w = ow;
33045             }
33046             this.proxy.setBounds(x, y, w, h);
33047             if(this.dynamic){
33048                 this.resizeElement();
33049             }
33050             }catch(e){}
33051         }
33052         this.fireEvent("resizing", this, x, y, w, h, e);
33053     },
33054
33055     // private
33056     handleOver : function(){
33057         if(this.enabled){
33058             this.el.addClass("x-resizable-over");
33059         }
33060     },
33061
33062     // private
33063     handleOut : function(){
33064         if(!this.resizing){
33065             this.el.removeClass("x-resizable-over");
33066         }
33067     },
33068
33069     /**
33070      * Returns the element this component is bound to.
33071      * @return {Roo.Element}
33072      */
33073     getEl : function(){
33074         return this.el;
33075     },
33076
33077     /**
33078      * Returns the resizeChild element (or null).
33079      * @return {Roo.Element}
33080      */
33081     getResizeChild : function(){
33082         return this.resizeChild;
33083     },
33084     groupHandler : function()
33085     {
33086         
33087     },
33088     /**
33089      * Destroys this resizable. If the element was wrapped and
33090      * removeEl is not true then the element remains.
33091      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33092      */
33093     destroy : function(removeEl){
33094         this.proxy.remove();
33095         if(this.overlay){
33096             this.overlay.removeAllListeners();
33097             this.overlay.remove();
33098         }
33099         var ps = Roo.Resizable.positions;
33100         for(var k in ps){
33101             if(typeof ps[k] != "function" && this[ps[k]]){
33102                 var h = this[ps[k]];
33103                 h.el.removeAllListeners();
33104                 h.el.remove();
33105             }
33106         }
33107         if(removeEl){
33108             this.el.update("");
33109             this.el.remove();
33110         }
33111     }
33112 });
33113
33114 // private
33115 // hash to map config positions to true positions
33116 Roo.Resizable.positions = {
33117     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
33118     hd: "hdrag"
33119 };
33120
33121 // private
33122 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
33123     if(!this.tpl){
33124         // only initialize the template if resizable is used
33125         var tpl = Roo.DomHelper.createTemplate(
33126             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
33127         );
33128         tpl.compile();
33129         Roo.Resizable.Handle.prototype.tpl = tpl;
33130     }
33131     this.position = pos;
33132     this.rz = rz;
33133     // show north drag fro topdra
33134     var handlepos = pos == 'hdrag' ? 'north' : pos;
33135     
33136     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
33137     if (pos == 'hdrag') {
33138         this.el.setStyle('cursor', 'pointer');
33139     }
33140     this.el.unselectable();
33141     if(transparent){
33142         this.el.setOpacity(0);
33143     }
33144     this.el.on("mousedown", this.onMouseDown, this);
33145     if(!disableTrackOver){
33146         this.el.on("mouseover", this.onMouseOver, this);
33147         this.el.on("mouseout", this.onMouseOut, this);
33148     }
33149 };
33150
33151 // private
33152 Roo.Resizable.Handle.prototype = {
33153     afterResize : function(rz){
33154         Roo.log('after?');
33155         // do nothing
33156     },
33157     // private
33158     onMouseDown : function(e){
33159         this.rz.onMouseDown(this, e);
33160     },
33161     // private
33162     onMouseOver : function(e){
33163         this.rz.handleOver(this, e);
33164     },
33165     // private
33166     onMouseOut : function(e){
33167         this.rz.handleOut(this, e);
33168     }
33169 };/*
33170  * Based on:
33171  * Ext JS Library 1.1.1
33172  * Copyright(c) 2006-2007, Ext JS, LLC.
33173  *
33174  * Originally Released Under LGPL - original licence link has changed is not relivant.
33175  *
33176  * Fork - LGPL
33177  * <script type="text/javascript">
33178  */
33179
33180 /**
33181  * @class Roo.Editor
33182  * @extends Roo.Component
33183  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
33184  * @constructor
33185  * Create a new Editor
33186  * @param {Roo.form.Field} field The Field object (or descendant)
33187  * @param {Object} config The config object
33188  */
33189 Roo.Editor = function(field, config){
33190     Roo.Editor.superclass.constructor.call(this, config);
33191     this.field = field;
33192     this.addEvents({
33193         /**
33194              * @event beforestartedit
33195              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33196              * false from the handler of this event.
33197              * @param {Editor} this
33198              * @param {Roo.Element} boundEl The underlying element bound to this editor
33199              * @param {Mixed} value The field value being set
33200              */
33201         "beforestartedit" : true,
33202         /**
33203              * @event startedit
33204              * Fires when this editor is displayed
33205              * @param {Roo.Element} boundEl The underlying element bound to this editor
33206              * @param {Mixed} value The starting field value
33207              */
33208         "startedit" : true,
33209         /**
33210              * @event beforecomplete
33211              * Fires after a change has been made to the field, but before the change is reflected in the underlying
33212              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
33213              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33214              * event will not fire since no edit actually occurred.
33215              * @param {Editor} this
33216              * @param {Mixed} value The current field value
33217              * @param {Mixed} startValue The original field value
33218              */
33219         "beforecomplete" : true,
33220         /**
33221              * @event complete
33222              * Fires after editing is complete and any changed value has been written to the underlying field.
33223              * @param {Editor} this
33224              * @param {Mixed} value The current field value
33225              * @param {Mixed} startValue The original field value
33226              */
33227         "complete" : true,
33228         /**
33229          * @event specialkey
33230          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33231          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33232          * @param {Roo.form.Field} this
33233          * @param {Roo.EventObject} e The event object
33234          */
33235         "specialkey" : true
33236     });
33237 };
33238
33239 Roo.extend(Roo.Editor, Roo.Component, {
33240     /**
33241      * @cfg {Boolean/String} autosize
33242      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33243      * or "height" to adopt the height only (defaults to false)
33244      */
33245     /**
33246      * @cfg {Boolean} revertInvalid
33247      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33248      * validation fails (defaults to true)
33249      */
33250     /**
33251      * @cfg {Boolean} ignoreNoChange
33252      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33253      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33254      * will never be ignored.
33255      */
33256     /**
33257      * @cfg {Boolean} hideEl
33258      * False to keep the bound element visible while the editor is displayed (defaults to true)
33259      */
33260     /**
33261      * @cfg {Mixed} value
33262      * The data value of the underlying field (defaults to "")
33263      */
33264     value : "",
33265     /**
33266      * @cfg {String} alignment
33267      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33268      */
33269     alignment: "c-c?",
33270     /**
33271      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33272      * for bottom-right shadow (defaults to "frame")
33273      */
33274     shadow : "frame",
33275     /**
33276      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33277      */
33278     constrain : false,
33279     /**
33280      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33281      */
33282     completeOnEnter : false,
33283     /**
33284      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33285      */
33286     cancelOnEsc : false,
33287     /**
33288      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33289      */
33290     updateEl : false,
33291
33292     // private
33293     onRender : function(ct, position){
33294         this.el = new Roo.Layer({
33295             shadow: this.shadow,
33296             cls: "x-editor",
33297             parentEl : ct,
33298             shim : this.shim,
33299             shadowOffset:4,
33300             id: this.id,
33301             constrain: this.constrain
33302         });
33303         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33304         if(this.field.msgTarget != 'title'){
33305             this.field.msgTarget = 'qtip';
33306         }
33307         this.field.render(this.el);
33308         if(Roo.isGecko){
33309             this.field.el.dom.setAttribute('autocomplete', 'off');
33310         }
33311         this.field.on("specialkey", this.onSpecialKey, this);
33312         if(this.swallowKeys){
33313             this.field.el.swallowEvent(['keydown','keypress']);
33314         }
33315         this.field.show();
33316         this.field.on("blur", this.onBlur, this);
33317         if(this.field.grow){
33318             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33319         }
33320     },
33321
33322     onSpecialKey : function(field, e)
33323     {
33324         //Roo.log('editor onSpecialKey');
33325         if(this.completeOnEnter && e.getKey() == e.ENTER){
33326             e.stopEvent();
33327             this.completeEdit();
33328             return;
33329         }
33330         // do not fire special key otherwise it might hide close the editor...
33331         if(e.getKey() == e.ENTER){    
33332             return;
33333         }
33334         if(this.cancelOnEsc && e.getKey() == e.ESC){
33335             this.cancelEdit();
33336             return;
33337         } 
33338         this.fireEvent('specialkey', field, e);
33339     
33340     },
33341
33342     /**
33343      * Starts the editing process and shows the editor.
33344      * @param {String/HTMLElement/Element} el The element to edit
33345      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33346       * to the innerHTML of el.
33347      */
33348     startEdit : function(el, value){
33349         if(this.editing){
33350             this.completeEdit();
33351         }
33352         this.boundEl = Roo.get(el);
33353         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33354         if(!this.rendered){
33355             this.render(this.parentEl || document.body);
33356         }
33357         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33358             return;
33359         }
33360         this.startValue = v;
33361         this.field.setValue(v);
33362         if(this.autoSize){
33363             var sz = this.boundEl.getSize();
33364             switch(this.autoSize){
33365                 case "width":
33366                 this.setSize(sz.width,  "");
33367                 break;
33368                 case "height":
33369                 this.setSize("",  sz.height);
33370                 break;
33371                 default:
33372                 this.setSize(sz.width,  sz.height);
33373             }
33374         }
33375         this.el.alignTo(this.boundEl, this.alignment);
33376         this.editing = true;
33377         if(Roo.QuickTips){
33378             Roo.QuickTips.disable();
33379         }
33380         this.show();
33381     },
33382
33383     /**
33384      * Sets the height and width of this editor.
33385      * @param {Number} width The new width
33386      * @param {Number} height The new height
33387      */
33388     setSize : function(w, h){
33389         this.field.setSize(w, h);
33390         if(this.el){
33391             this.el.sync();
33392         }
33393     },
33394
33395     /**
33396      * Realigns the editor to the bound field based on the current alignment config value.
33397      */
33398     realign : function(){
33399         this.el.alignTo(this.boundEl, this.alignment);
33400     },
33401
33402     /**
33403      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33404      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33405      */
33406     completeEdit : function(remainVisible){
33407         if(!this.editing){
33408             return;
33409         }
33410         var v = this.getValue();
33411         if(this.revertInvalid !== false && !this.field.isValid()){
33412             v = this.startValue;
33413             this.cancelEdit(true);
33414         }
33415         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33416             this.editing = false;
33417             this.hide();
33418             return;
33419         }
33420         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33421             this.editing = false;
33422             if(this.updateEl && this.boundEl){
33423                 this.boundEl.update(v);
33424             }
33425             if(remainVisible !== true){
33426                 this.hide();
33427             }
33428             this.fireEvent("complete", this, v, this.startValue);
33429         }
33430     },
33431
33432     // private
33433     onShow : function(){
33434         this.el.show();
33435         if(this.hideEl !== false){
33436             this.boundEl.hide();
33437         }
33438         this.field.show();
33439         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33440             this.fixIEFocus = true;
33441             this.deferredFocus.defer(50, this);
33442         }else{
33443             this.field.focus();
33444         }
33445         this.fireEvent("startedit", this.boundEl, this.startValue);
33446     },
33447
33448     deferredFocus : function(){
33449         if(this.editing){
33450             this.field.focus();
33451         }
33452     },
33453
33454     /**
33455      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33456      * reverted to the original starting value.
33457      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33458      * cancel (defaults to false)
33459      */
33460     cancelEdit : function(remainVisible){
33461         if(this.editing){
33462             this.setValue(this.startValue);
33463             if(remainVisible !== true){
33464                 this.hide();
33465             }
33466         }
33467     },
33468
33469     // private
33470     onBlur : function(){
33471         if(this.allowBlur !== true && this.editing){
33472             this.completeEdit();
33473         }
33474     },
33475
33476     // private
33477     onHide : function(){
33478         if(this.editing){
33479             this.completeEdit();
33480             return;
33481         }
33482         this.field.blur();
33483         if(this.field.collapse){
33484             this.field.collapse();
33485         }
33486         this.el.hide();
33487         if(this.hideEl !== false){
33488             this.boundEl.show();
33489         }
33490         if(Roo.QuickTips){
33491             Roo.QuickTips.enable();
33492         }
33493     },
33494
33495     /**
33496      * Sets the data value of the editor
33497      * @param {Mixed} value Any valid value supported by the underlying field
33498      */
33499     setValue : function(v){
33500         this.field.setValue(v);
33501     },
33502
33503     /**
33504      * Gets the data value of the editor
33505      * @return {Mixed} The data value
33506      */
33507     getValue : function(){
33508         return this.field.getValue();
33509     }
33510 });/*
33511  * Based on:
33512  * Ext JS Library 1.1.1
33513  * Copyright(c) 2006-2007, Ext JS, LLC.
33514  *
33515  * Originally Released Under LGPL - original licence link has changed is not relivant.
33516  *
33517  * Fork - LGPL
33518  * <script type="text/javascript">
33519  */
33520  
33521 /**
33522  * @class Roo.BasicDialog
33523  * @extends Roo.util.Observable
33524  * @parent none builder
33525  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33526  * <pre><code>
33527 var dlg = new Roo.BasicDialog("my-dlg", {
33528     height: 200,
33529     width: 300,
33530     minHeight: 100,
33531     minWidth: 150,
33532     modal: true,
33533     proxyDrag: true,
33534     shadow: true
33535 });
33536 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33537 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33538 dlg.addButton('Cancel', dlg.hide, dlg);
33539 dlg.show();
33540 </code></pre>
33541   <b>A Dialog should always be a direct child of the body element.</b>
33542  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33543  * @cfg {String} title Default text to display in the title bar (defaults to null)
33544  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33545  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33546  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33547  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33548  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33549  * (defaults to null with no animation)
33550  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33551  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33552  * property for valid values (defaults to 'all')
33553  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33554  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33555  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33556  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33557  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33558  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33559  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33560  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33561  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33562  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33563  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33564  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33565  * draggable = true (defaults to false)
33566  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33567  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33568  * shadow (defaults to false)
33569  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33570  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33571  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33572  * @cfg {Array} buttons Array of buttons
33573  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33574  * @constructor
33575  * Create a new BasicDialog.
33576  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33577  * @param {Object} config Configuration options
33578  */
33579 Roo.BasicDialog = function(el, config){
33580     this.el = Roo.get(el);
33581     var dh = Roo.DomHelper;
33582     if(!this.el && config && config.autoCreate){
33583         if(typeof config.autoCreate == "object"){
33584             if(!config.autoCreate.id){
33585                 config.autoCreate.id = el;
33586             }
33587             this.el = dh.append(document.body,
33588                         config.autoCreate, true);
33589         }else{
33590             this.el = dh.append(document.body,
33591                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33592         }
33593     }
33594     el = this.el;
33595     el.setDisplayed(true);
33596     el.hide = this.hideAction;
33597     this.id = el.id;
33598     el.addClass("x-dlg");
33599
33600     Roo.apply(this, config);
33601
33602     this.proxy = el.createProxy("x-dlg-proxy");
33603     this.proxy.hide = this.hideAction;
33604     this.proxy.setOpacity(.5);
33605     this.proxy.hide();
33606
33607     if(config.width){
33608         el.setWidth(config.width);
33609     }
33610     if(config.height){
33611         el.setHeight(config.height);
33612     }
33613     this.size = el.getSize();
33614     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33615         this.xy = [config.x,config.y];
33616     }else{
33617         this.xy = el.getCenterXY(true);
33618     }
33619     /** The header element @type Roo.Element */
33620     this.header = el.child("> .x-dlg-hd");
33621     /** The body element @type Roo.Element */
33622     this.body = el.child("> .x-dlg-bd");
33623     /** The footer element @type Roo.Element */
33624     this.footer = el.child("> .x-dlg-ft");
33625
33626     if(!this.header){
33627         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33628     }
33629     if(!this.body){
33630         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33631     }
33632
33633     this.header.unselectable();
33634     if(this.title){
33635         this.header.update(this.title);
33636     }
33637     // this element allows the dialog to be focused for keyboard event
33638     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33639     this.focusEl.swallowEvent("click", true);
33640
33641     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33642
33643     // wrap the body and footer for special rendering
33644     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33645     if(this.footer){
33646         this.bwrap.dom.appendChild(this.footer.dom);
33647     }
33648
33649     this.bg = this.el.createChild({
33650         tag: "div", cls:"x-dlg-bg",
33651         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33652     });
33653     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33654
33655
33656     if(this.autoScroll !== false && !this.autoTabs){
33657         this.body.setStyle("overflow", "auto");
33658     }
33659
33660     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33661
33662     if(this.closable !== false){
33663         this.el.addClass("x-dlg-closable");
33664         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33665         this.close.on("click", this.closeClick, this);
33666         this.close.addClassOnOver("x-dlg-close-over");
33667     }
33668     if(this.collapsible !== false){
33669         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33670         this.collapseBtn.on("click", this.collapseClick, this);
33671         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33672         this.header.on("dblclick", this.collapseClick, this);
33673     }
33674     if(this.resizable !== false){
33675         this.el.addClass("x-dlg-resizable");
33676         this.resizer = new Roo.Resizable(el, {
33677             minWidth: this.minWidth || 80,
33678             minHeight:this.minHeight || 80,
33679             handles: this.resizeHandles || "all",
33680             pinned: true
33681         });
33682         this.resizer.on("beforeresize", this.beforeResize, this);
33683         this.resizer.on("resize", this.onResize, this);
33684     }
33685     if(this.draggable !== false){
33686         el.addClass("x-dlg-draggable");
33687         if (!this.proxyDrag) {
33688             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33689         }
33690         else {
33691             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33692         }
33693         dd.setHandleElId(this.header.id);
33694         dd.endDrag = this.endMove.createDelegate(this);
33695         dd.startDrag = this.startMove.createDelegate(this);
33696         dd.onDrag = this.onDrag.createDelegate(this);
33697         dd.scroll = false;
33698         this.dd = dd;
33699     }
33700     if(this.modal){
33701         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33702         this.mask.enableDisplayMode("block");
33703         this.mask.hide();
33704         this.el.addClass("x-dlg-modal");
33705     }
33706     if(this.shadow){
33707         this.shadow = new Roo.Shadow({
33708             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33709             offset : this.shadowOffset
33710         });
33711     }else{
33712         this.shadowOffset = 0;
33713     }
33714     if(Roo.useShims && this.shim !== false){
33715         this.shim = this.el.createShim();
33716         this.shim.hide = this.hideAction;
33717         this.shim.hide();
33718     }else{
33719         this.shim = false;
33720     }
33721     if(this.autoTabs){
33722         this.initTabs();
33723     }
33724     if (this.buttons) { 
33725         var bts= this.buttons;
33726         this.buttons = [];
33727         Roo.each(bts, function(b) {
33728             this.addButton(b);
33729         }, this);
33730     }
33731     
33732     
33733     this.addEvents({
33734         /**
33735          * @event keydown
33736          * Fires when a key is pressed
33737          * @param {Roo.BasicDialog} this
33738          * @param {Roo.EventObject} e
33739          */
33740         "keydown" : true,
33741         /**
33742          * @event move
33743          * Fires when this dialog is moved by the user.
33744          * @param {Roo.BasicDialog} this
33745          * @param {Number} x The new page X
33746          * @param {Number} y The new page Y
33747          */
33748         "move" : true,
33749         /**
33750          * @event resize
33751          * Fires when this dialog is resized by the user.
33752          * @param {Roo.BasicDialog} this
33753          * @param {Number} width The new width
33754          * @param {Number} height The new height
33755          */
33756         "resize" : true,
33757         /**
33758          * @event beforehide
33759          * Fires before this dialog is hidden.
33760          * @param {Roo.BasicDialog} this
33761          */
33762         "beforehide" : true,
33763         /**
33764          * @event hide
33765          * Fires when this dialog is hidden.
33766          * @param {Roo.BasicDialog} this
33767          */
33768         "hide" : true,
33769         /**
33770          * @event beforeshow
33771          * Fires before this dialog is shown.
33772          * @param {Roo.BasicDialog} this
33773          */
33774         "beforeshow" : true,
33775         /**
33776          * @event show
33777          * Fires when this dialog is shown.
33778          * @param {Roo.BasicDialog} this
33779          */
33780         "show" : true
33781     });
33782     el.on("keydown", this.onKeyDown, this);
33783     el.on("mousedown", this.toFront, this);
33784     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33785     this.el.hide();
33786     Roo.DialogManager.register(this);
33787     Roo.BasicDialog.superclass.constructor.call(this);
33788 };
33789
33790 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33791     shadowOffset: Roo.isIE ? 6 : 5,
33792     minHeight: 80,
33793     minWidth: 200,
33794     minButtonWidth: 75,
33795     defaultButton: null,
33796     buttonAlign: "right",
33797     tabTag: 'div',
33798     firstShow: true,
33799
33800     /**
33801      * Sets the dialog title text
33802      * @param {String} text The title text to display
33803      * @return {Roo.BasicDialog} this
33804      */
33805     setTitle : function(text){
33806         this.header.update(text);
33807         return this;
33808     },
33809
33810     // private
33811     closeClick : function(){
33812         this.hide();
33813     },
33814
33815     // private
33816     collapseClick : function(){
33817         this[this.collapsed ? "expand" : "collapse"]();
33818     },
33819
33820     /**
33821      * Collapses the dialog to its minimized state (only the title bar is visible).
33822      * Equivalent to the user clicking the collapse dialog button.
33823      */
33824     collapse : function(){
33825         if(!this.collapsed){
33826             this.collapsed = true;
33827             this.el.addClass("x-dlg-collapsed");
33828             this.restoreHeight = this.el.getHeight();
33829             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33830         }
33831     },
33832
33833     /**
33834      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33835      * clicking the expand dialog button.
33836      */
33837     expand : function(){
33838         if(this.collapsed){
33839             this.collapsed = false;
33840             this.el.removeClass("x-dlg-collapsed");
33841             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33842         }
33843     },
33844
33845     /**
33846      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33847      * @return {Roo.TabPanel} The tabs component
33848      */
33849     initTabs : function(){
33850         var tabs = this.getTabs();
33851         while(tabs.getTab(0)){
33852             tabs.removeTab(0);
33853         }
33854         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33855             var dom = el.dom;
33856             tabs.addTab(Roo.id(dom), dom.title);
33857             dom.title = "";
33858         });
33859         tabs.activate(0);
33860         return tabs;
33861     },
33862
33863     // private
33864     beforeResize : function(){
33865         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33866     },
33867
33868     // private
33869     onResize : function(){
33870         this.refreshSize();
33871         this.syncBodyHeight();
33872         this.adjustAssets();
33873         this.focus();
33874         this.fireEvent("resize", this, this.size.width, this.size.height);
33875     },
33876
33877     // private
33878     onKeyDown : function(e){
33879         if(this.isVisible()){
33880             this.fireEvent("keydown", this, e);
33881         }
33882     },
33883
33884     /**
33885      * Resizes the dialog.
33886      * @param {Number} width
33887      * @param {Number} height
33888      * @return {Roo.BasicDialog} this
33889      */
33890     resizeTo : function(width, height){
33891         this.el.setSize(width, height);
33892         this.size = {width: width, height: height};
33893         this.syncBodyHeight();
33894         if(this.fixedcenter){
33895             this.center();
33896         }
33897         if(this.isVisible()){
33898             this.constrainXY();
33899             this.adjustAssets();
33900         }
33901         this.fireEvent("resize", this, width, height);
33902         return this;
33903     },
33904
33905
33906     /**
33907      * Resizes the dialog to fit the specified content size.
33908      * @param {Number} width
33909      * @param {Number} height
33910      * @return {Roo.BasicDialog} this
33911      */
33912     setContentSize : function(w, h){
33913         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33914         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33915         //if(!this.el.isBorderBox()){
33916             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33917             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33918         //}
33919         if(this.tabs){
33920             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33921             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33922         }
33923         this.resizeTo(w, h);
33924         return this;
33925     },
33926
33927     /**
33928      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33929      * executed in response to a particular key being pressed while the dialog is active.
33930      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33931      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33932      * @param {Function} fn The function to call
33933      * @param {Object} scope (optional) The scope of the function
33934      * @return {Roo.BasicDialog} this
33935      */
33936     addKeyListener : function(key, fn, scope){
33937         var keyCode, shift, ctrl, alt;
33938         if(typeof key == "object" && !(key instanceof Array)){
33939             keyCode = key["key"];
33940             shift = key["shift"];
33941             ctrl = key["ctrl"];
33942             alt = key["alt"];
33943         }else{
33944             keyCode = key;
33945         }
33946         var handler = function(dlg, e){
33947             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33948                 var k = e.getKey();
33949                 if(keyCode instanceof Array){
33950                     for(var i = 0, len = keyCode.length; i < len; i++){
33951                         if(keyCode[i] == k){
33952                           fn.call(scope || window, dlg, k, e);
33953                           return;
33954                         }
33955                     }
33956                 }else{
33957                     if(k == keyCode){
33958                         fn.call(scope || window, dlg, k, e);
33959                     }
33960                 }
33961             }
33962         };
33963         this.on("keydown", handler);
33964         return this;
33965     },
33966
33967     /**
33968      * Returns the TabPanel component (creates it if it doesn't exist).
33969      * Note: If you wish to simply check for the existence of tabs without creating them,
33970      * check for a null 'tabs' property.
33971      * @return {Roo.TabPanel} The tabs component
33972      */
33973     getTabs : function(){
33974         if(!this.tabs){
33975             this.el.addClass("x-dlg-auto-tabs");
33976             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33977             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33978         }
33979         return this.tabs;
33980     },
33981
33982     /**
33983      * Adds a button to the footer section of the dialog.
33984      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33985      * object or a valid Roo.DomHelper element config
33986      * @param {Function} handler The function called when the button is clicked
33987      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33988      * @return {Roo.Button} The new button
33989      */
33990     addButton : function(config, handler, scope){
33991         var dh = Roo.DomHelper;
33992         if(!this.footer){
33993             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33994         }
33995         if(!this.btnContainer){
33996             var tb = this.footer.createChild({
33997
33998                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33999                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
34000             }, null, true);
34001             this.btnContainer = tb.firstChild.firstChild.firstChild;
34002         }
34003         var bconfig = {
34004             handler: handler,
34005             scope: scope,
34006             minWidth: this.minButtonWidth,
34007             hideParent:true
34008         };
34009         if(typeof config == "string"){
34010             bconfig.text = config;
34011         }else{
34012             if(config.tag){
34013                 bconfig.dhconfig = config;
34014             }else{
34015                 Roo.apply(bconfig, config);
34016             }
34017         }
34018         var fc = false;
34019         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
34020             bconfig.position = Math.max(0, bconfig.position);
34021             fc = this.btnContainer.childNodes[bconfig.position];
34022         }
34023          
34024         var btn = new Roo.Button(
34025             fc ? 
34026                 this.btnContainer.insertBefore(document.createElement("td"),fc)
34027                 : this.btnContainer.appendChild(document.createElement("td")),
34028             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
34029             bconfig
34030         );
34031         this.syncBodyHeight();
34032         if(!this.buttons){
34033             /**
34034              * Array of all the buttons that have been added to this dialog via addButton
34035              * @type Array
34036              */
34037             this.buttons = [];
34038         }
34039         this.buttons.push(btn);
34040         return btn;
34041     },
34042
34043     /**
34044      * Sets the default button to be focused when the dialog is displayed.
34045      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
34046      * @return {Roo.BasicDialog} this
34047      */
34048     setDefaultButton : function(btn){
34049         this.defaultButton = btn;
34050         return this;
34051     },
34052
34053     // private
34054     getHeaderFooterHeight : function(safe){
34055         var height = 0;
34056         if(this.header){
34057            height += this.header.getHeight();
34058         }
34059         if(this.footer){
34060            var fm = this.footer.getMargins();
34061             height += (this.footer.getHeight()+fm.top+fm.bottom);
34062         }
34063         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
34064         height += this.centerBg.getPadding("tb");
34065         return height;
34066     },
34067
34068     // private
34069     syncBodyHeight : function()
34070     {
34071         var bd = this.body, // the text
34072             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
34073             bw = this.bwrap;
34074         var height = this.size.height - this.getHeaderFooterHeight(false);
34075         bd.setHeight(height-bd.getMargins("tb"));
34076         var hh = this.header.getHeight();
34077         var h = this.size.height-hh;
34078         cb.setHeight(h);
34079         
34080         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
34081         bw.setHeight(h-cb.getPadding("tb"));
34082         
34083         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
34084         bd.setWidth(bw.getWidth(true));
34085         if(this.tabs){
34086             this.tabs.syncHeight();
34087             if(Roo.isIE){
34088                 this.tabs.el.repaint();
34089             }
34090         }
34091     },
34092
34093     /**
34094      * Restores the previous state of the dialog if Roo.state is configured.
34095      * @return {Roo.BasicDialog} this
34096      */
34097     restoreState : function(){
34098         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
34099         if(box && box.width){
34100             this.xy = [box.x, box.y];
34101             this.resizeTo(box.width, box.height);
34102         }
34103         return this;
34104     },
34105
34106     // private
34107     beforeShow : function(){
34108         this.expand();
34109         if(this.fixedcenter){
34110             this.xy = this.el.getCenterXY(true);
34111         }
34112         if(this.modal){
34113             Roo.get(document.body).addClass("x-body-masked");
34114             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34115             this.mask.show();
34116         }
34117         this.constrainXY();
34118     },
34119
34120     // private
34121     animShow : function(){
34122         var b = Roo.get(this.animateTarget).getBox();
34123         this.proxy.setSize(b.width, b.height);
34124         this.proxy.setLocation(b.x, b.y);
34125         this.proxy.show();
34126         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
34127                     true, .35, this.showEl.createDelegate(this));
34128     },
34129
34130     /**
34131      * Shows the dialog.
34132      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
34133      * @return {Roo.BasicDialog} this
34134      */
34135     show : function(animateTarget){
34136         if (this.fireEvent("beforeshow", this) === false){
34137             return;
34138         }
34139         if(this.syncHeightBeforeShow){
34140             this.syncBodyHeight();
34141         }else if(this.firstShow){
34142             this.firstShow = false;
34143             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
34144         }
34145         this.animateTarget = animateTarget || this.animateTarget;
34146         if(!this.el.isVisible()){
34147             this.beforeShow();
34148             if(this.animateTarget && Roo.get(this.animateTarget)){
34149                 this.animShow();
34150             }else{
34151                 this.showEl();
34152             }
34153         }
34154         return this;
34155     },
34156
34157     // private
34158     showEl : function(){
34159         this.proxy.hide();
34160         this.el.setXY(this.xy);
34161         this.el.show();
34162         this.adjustAssets(true);
34163         this.toFront();
34164         this.focus();
34165         // IE peekaboo bug - fix found by Dave Fenwick
34166         if(Roo.isIE){
34167             this.el.repaint();
34168         }
34169         this.fireEvent("show", this);
34170     },
34171
34172     /**
34173      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
34174      * dialog itself will receive focus.
34175      */
34176     focus : function(){
34177         if(this.defaultButton){
34178             this.defaultButton.focus();
34179         }else{
34180             this.focusEl.focus();
34181         }
34182     },
34183
34184     // private
34185     constrainXY : function(){
34186         if(this.constraintoviewport !== false){
34187             if(!this.viewSize){
34188                 if(this.container){
34189                     var s = this.container.getSize();
34190                     this.viewSize = [s.width, s.height];
34191                 }else{
34192                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
34193                 }
34194             }
34195             var s = Roo.get(this.container||document).getScroll();
34196
34197             var x = this.xy[0], y = this.xy[1];
34198             var w = this.size.width, h = this.size.height;
34199             var vw = this.viewSize[0], vh = this.viewSize[1];
34200             // only move it if it needs it
34201             var moved = false;
34202             // first validate right/bottom
34203             if(x + w > vw+s.left){
34204                 x = vw - w;
34205                 moved = true;
34206             }
34207             if(y + h > vh+s.top){
34208                 y = vh - h;
34209                 moved = true;
34210             }
34211             // then make sure top/left isn't negative
34212             if(x < s.left){
34213                 x = s.left;
34214                 moved = true;
34215             }
34216             if(y < s.top){
34217                 y = s.top;
34218                 moved = true;
34219             }
34220             if(moved){
34221                 // cache xy
34222                 this.xy = [x, y];
34223                 if(this.isVisible()){
34224                     this.el.setLocation(x, y);
34225                     this.adjustAssets();
34226                 }
34227             }
34228         }
34229     },
34230
34231     // private
34232     onDrag : function(){
34233         if(!this.proxyDrag){
34234             this.xy = this.el.getXY();
34235             this.adjustAssets();
34236         }
34237     },
34238
34239     // private
34240     adjustAssets : function(doShow){
34241         var x = this.xy[0], y = this.xy[1];
34242         var w = this.size.width, h = this.size.height;
34243         if(doShow === true){
34244             if(this.shadow){
34245                 this.shadow.show(this.el);
34246             }
34247             if(this.shim){
34248                 this.shim.show();
34249             }
34250         }
34251         if(this.shadow && this.shadow.isVisible()){
34252             this.shadow.show(this.el);
34253         }
34254         if(this.shim && this.shim.isVisible()){
34255             this.shim.setBounds(x, y, w, h);
34256         }
34257     },
34258
34259     // private
34260     adjustViewport : function(w, h){
34261         if(!w || !h){
34262             w = Roo.lib.Dom.getViewWidth();
34263             h = Roo.lib.Dom.getViewHeight();
34264         }
34265         // cache the size
34266         this.viewSize = [w, h];
34267         if(this.modal && this.mask.isVisible()){
34268             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34269             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34270         }
34271         if(this.isVisible()){
34272             this.constrainXY();
34273         }
34274     },
34275
34276     /**
34277      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34278      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34279      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34280      */
34281     destroy : function(removeEl){
34282         if(this.isVisible()){
34283             this.animateTarget = null;
34284             this.hide();
34285         }
34286         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34287         if(this.tabs){
34288             this.tabs.destroy(removeEl);
34289         }
34290         Roo.destroy(
34291              this.shim,
34292              this.proxy,
34293              this.resizer,
34294              this.close,
34295              this.mask
34296         );
34297         if(this.dd){
34298             this.dd.unreg();
34299         }
34300         if(this.buttons){
34301            for(var i = 0, len = this.buttons.length; i < len; i++){
34302                this.buttons[i].destroy();
34303            }
34304         }
34305         this.el.removeAllListeners();
34306         if(removeEl === true){
34307             this.el.update("");
34308             this.el.remove();
34309         }
34310         Roo.DialogManager.unregister(this);
34311     },
34312
34313     // private
34314     startMove : function(){
34315         if(this.proxyDrag){
34316             this.proxy.show();
34317         }
34318         if(this.constraintoviewport !== false){
34319             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34320         }
34321     },
34322
34323     // private
34324     endMove : function(){
34325         if(!this.proxyDrag){
34326             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34327         }else{
34328             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34329             this.proxy.hide();
34330         }
34331         this.refreshSize();
34332         this.adjustAssets();
34333         this.focus();
34334         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34335     },
34336
34337     /**
34338      * Brings this dialog to the front of any other visible dialogs
34339      * @return {Roo.BasicDialog} this
34340      */
34341     toFront : function(){
34342         Roo.DialogManager.bringToFront(this);
34343         return this;
34344     },
34345
34346     /**
34347      * Sends this dialog to the back (under) of any other visible dialogs
34348      * @return {Roo.BasicDialog} this
34349      */
34350     toBack : function(){
34351         Roo.DialogManager.sendToBack(this);
34352         return this;
34353     },
34354
34355     /**
34356      * Centers this dialog in the viewport
34357      * @return {Roo.BasicDialog} this
34358      */
34359     center : function(){
34360         var xy = this.el.getCenterXY(true);
34361         this.moveTo(xy[0], xy[1]);
34362         return this;
34363     },
34364
34365     /**
34366      * Moves the dialog's top-left corner to the specified point
34367      * @param {Number} x
34368      * @param {Number} y
34369      * @return {Roo.BasicDialog} this
34370      */
34371     moveTo : function(x, y){
34372         this.xy = [x,y];
34373         if(this.isVisible()){
34374             this.el.setXY(this.xy);
34375             this.adjustAssets();
34376         }
34377         return this;
34378     },
34379
34380     /**
34381      * Aligns the dialog to the specified element
34382      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34383      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34384      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34385      * @return {Roo.BasicDialog} this
34386      */
34387     alignTo : function(element, position, offsets){
34388         this.xy = this.el.getAlignToXY(element, position, offsets);
34389         if(this.isVisible()){
34390             this.el.setXY(this.xy);
34391             this.adjustAssets();
34392         }
34393         return this;
34394     },
34395
34396     /**
34397      * Anchors an element to another element and realigns it when the window is resized.
34398      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34399      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34400      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34401      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34402      * is a number, it is used as the buffer delay (defaults to 50ms).
34403      * @return {Roo.BasicDialog} this
34404      */
34405     anchorTo : function(el, alignment, offsets, monitorScroll){
34406         var action = function(){
34407             this.alignTo(el, alignment, offsets);
34408         };
34409         Roo.EventManager.onWindowResize(action, this);
34410         var tm = typeof monitorScroll;
34411         if(tm != 'undefined'){
34412             Roo.EventManager.on(window, 'scroll', action, this,
34413                 {buffer: tm == 'number' ? monitorScroll : 50});
34414         }
34415         action.call(this);
34416         return this;
34417     },
34418
34419     /**
34420      * Returns true if the dialog is visible
34421      * @return {Boolean}
34422      */
34423     isVisible : function(){
34424         return this.el.isVisible();
34425     },
34426
34427     // private
34428     animHide : function(callback){
34429         var b = Roo.get(this.animateTarget).getBox();
34430         this.proxy.show();
34431         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34432         this.el.hide();
34433         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34434                     this.hideEl.createDelegate(this, [callback]));
34435     },
34436
34437     /**
34438      * Hides the dialog.
34439      * @param {Function} callback (optional) Function to call when the dialog is hidden
34440      * @return {Roo.BasicDialog} this
34441      */
34442     hide : function(callback){
34443         if (this.fireEvent("beforehide", this) === false){
34444             return;
34445         }
34446         if(this.shadow){
34447             this.shadow.hide();
34448         }
34449         if(this.shim) {
34450           this.shim.hide();
34451         }
34452         // sometimes animateTarget seems to get set.. causing problems...
34453         // this just double checks..
34454         if(this.animateTarget && Roo.get(this.animateTarget)) {
34455            this.animHide(callback);
34456         }else{
34457             this.el.hide();
34458             this.hideEl(callback);
34459         }
34460         return this;
34461     },
34462
34463     // private
34464     hideEl : function(callback){
34465         this.proxy.hide();
34466         if(this.modal){
34467             this.mask.hide();
34468             Roo.get(document.body).removeClass("x-body-masked");
34469         }
34470         this.fireEvent("hide", this);
34471         if(typeof callback == "function"){
34472             callback();
34473         }
34474     },
34475
34476     // private
34477     hideAction : function(){
34478         this.setLeft("-10000px");
34479         this.setTop("-10000px");
34480         this.setStyle("visibility", "hidden");
34481     },
34482
34483     // private
34484     refreshSize : function(){
34485         this.size = this.el.getSize();
34486         this.xy = this.el.getXY();
34487         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34488     },
34489
34490     // private
34491     // z-index is managed by the DialogManager and may be overwritten at any time
34492     setZIndex : function(index){
34493         if(this.modal){
34494             this.mask.setStyle("z-index", index);
34495         }
34496         if(this.shim){
34497             this.shim.setStyle("z-index", ++index);
34498         }
34499         if(this.shadow){
34500             this.shadow.setZIndex(++index);
34501         }
34502         this.el.setStyle("z-index", ++index);
34503         if(this.proxy){
34504             this.proxy.setStyle("z-index", ++index);
34505         }
34506         if(this.resizer){
34507             this.resizer.proxy.setStyle("z-index", ++index);
34508         }
34509
34510         this.lastZIndex = index;
34511     },
34512
34513     /**
34514      * Returns the element for this dialog
34515      * @return {Roo.Element} The underlying dialog Element
34516      */
34517     getEl : function(){
34518         return this.el;
34519     }
34520 });
34521
34522 /**
34523  * @class Roo.DialogManager
34524  * Provides global access to BasicDialogs that have been created and
34525  * support for z-indexing (layering) multiple open dialogs.
34526  */
34527 Roo.DialogManager = function(){
34528     var list = {};
34529     var accessList = [];
34530     var front = null;
34531
34532     // private
34533     var sortDialogs = function(d1, d2){
34534         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34535     };
34536
34537     // private
34538     var orderDialogs = function(){
34539         accessList.sort(sortDialogs);
34540         var seed = Roo.DialogManager.zseed;
34541         for(var i = 0, len = accessList.length; i < len; i++){
34542             var dlg = accessList[i];
34543             if(dlg){
34544                 dlg.setZIndex(seed + (i*10));
34545             }
34546         }
34547     };
34548
34549     return {
34550         /**
34551          * The starting z-index for BasicDialogs (defaults to 9000)
34552          * @type Number The z-index value
34553          */
34554         zseed : 9000,
34555
34556         // private
34557         register : function(dlg){
34558             list[dlg.id] = dlg;
34559             accessList.push(dlg);
34560         },
34561
34562         // private
34563         unregister : function(dlg){
34564             delete list[dlg.id];
34565             var i=0;
34566             var len=0;
34567             if(!accessList.indexOf){
34568                 for(  i = 0, len = accessList.length; i < len; i++){
34569                     if(accessList[i] == dlg){
34570                         accessList.splice(i, 1);
34571                         return;
34572                     }
34573                 }
34574             }else{
34575                  i = accessList.indexOf(dlg);
34576                 if(i != -1){
34577                     accessList.splice(i, 1);
34578                 }
34579             }
34580         },
34581
34582         /**
34583          * Gets a registered dialog by id
34584          * @param {String/Object} id The id of the dialog or a dialog
34585          * @return {Roo.BasicDialog} this
34586          */
34587         get : function(id){
34588             return typeof id == "object" ? id : list[id];
34589         },
34590
34591         /**
34592          * Brings the specified dialog to the front
34593          * @param {String/Object} dlg The id of the dialog or a dialog
34594          * @return {Roo.BasicDialog} this
34595          */
34596         bringToFront : function(dlg){
34597             dlg = this.get(dlg);
34598             if(dlg != front){
34599                 front = dlg;
34600                 dlg._lastAccess = new Date().getTime();
34601                 orderDialogs();
34602             }
34603             return dlg;
34604         },
34605
34606         /**
34607          * Sends the specified dialog to the back
34608          * @param {String/Object} dlg The id of the dialog or a dialog
34609          * @return {Roo.BasicDialog} this
34610          */
34611         sendToBack : function(dlg){
34612             dlg = this.get(dlg);
34613             dlg._lastAccess = -(new Date().getTime());
34614             orderDialogs();
34615             return dlg;
34616         },
34617
34618         /**
34619          * Hides all dialogs
34620          */
34621         hideAll : function(){
34622             for(var id in list){
34623                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34624                     list[id].hide();
34625                 }
34626             }
34627         }
34628     };
34629 }();
34630
34631 /**
34632  * @class Roo.LayoutDialog
34633  * @extends Roo.BasicDialog
34634  * @children Roo.ContentPanel
34635  * @parent builder none
34636  * Dialog which provides adjustments for working with a layout in a Dialog.
34637  * Add your necessary layout config options to the dialog's config.<br>
34638  * Example usage (including a nested layout):
34639  * <pre><code>
34640 if(!dialog){
34641     dialog = new Roo.LayoutDialog("download-dlg", {
34642         modal: true,
34643         width:600,
34644         height:450,
34645         shadow:true,
34646         minWidth:500,
34647         minHeight:350,
34648         autoTabs:true,
34649         proxyDrag:true,
34650         // layout config merges with the dialog config
34651         center:{
34652             tabPosition: "top",
34653             alwaysShowTabs: true
34654         }
34655     });
34656     dialog.addKeyListener(27, dialog.hide, dialog);
34657     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34658     dialog.addButton("Build It!", this.getDownload, this);
34659
34660     // we can even add nested layouts
34661     var innerLayout = new Roo.BorderLayout("dl-inner", {
34662         east: {
34663             initialSize: 200,
34664             autoScroll:true,
34665             split:true
34666         },
34667         center: {
34668             autoScroll:true
34669         }
34670     });
34671     innerLayout.beginUpdate();
34672     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34673     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34674     innerLayout.endUpdate(true);
34675
34676     var layout = dialog.getLayout();
34677     layout.beginUpdate();
34678     layout.add("center", new Roo.ContentPanel("standard-panel",
34679                         {title: "Download the Source", fitToFrame:true}));
34680     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34681                {title: "Build your own roo.js"}));
34682     layout.getRegion("center").showPanel(sp);
34683     layout.endUpdate();
34684 }
34685 </code></pre>
34686     * @constructor
34687     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34688     * @param {Object} config configuration options
34689   */
34690 Roo.LayoutDialog = function(el, cfg){
34691     
34692     var config=  cfg;
34693     if (typeof(cfg) == 'undefined') {
34694         config = Roo.apply({}, el);
34695         // not sure why we use documentElement here.. - it should always be body.
34696         // IE7 borks horribly if we use documentElement.
34697         // webkit also does not like documentElement - it creates a body element...
34698         el = Roo.get( document.body || document.documentElement ).createChild();
34699         //config.autoCreate = true;
34700     }
34701     
34702     
34703     config.autoTabs = false;
34704     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34705     this.body.setStyle({overflow:"hidden", position:"relative"});
34706     this.layout = new Roo.BorderLayout(this.body.dom, config);
34707     this.layout.monitorWindowResize = false;
34708     this.el.addClass("x-dlg-auto-layout");
34709     // fix case when center region overwrites center function
34710     this.center = Roo.BasicDialog.prototype.center;
34711     this.on("show", this.layout.layout, this.layout, true);
34712     if (config.items) {
34713         var xitems = config.items;
34714         delete config.items;
34715         Roo.each(xitems, this.addxtype, this);
34716     }
34717     
34718     
34719 };
34720 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34721     
34722     
34723     /**
34724      * @cfg {Roo.LayoutRegion} east  
34725      */
34726     /**
34727      * @cfg {Roo.LayoutRegion} west
34728      */
34729     /**
34730      * @cfg {Roo.LayoutRegion} south
34731      */
34732     /**
34733      * @cfg {Roo.LayoutRegion} north
34734      */
34735     /**
34736      * @cfg {Roo.LayoutRegion} center
34737      */
34738     /**
34739      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34740      */
34741     
34742     
34743     /**
34744      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34745      * @deprecated
34746      */
34747     endUpdate : function(){
34748         this.layout.endUpdate();
34749     },
34750
34751     /**
34752      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34753      *  @deprecated
34754      */
34755     beginUpdate : function(){
34756         this.layout.beginUpdate();
34757     },
34758
34759     /**
34760      * Get the BorderLayout for this dialog
34761      * @return {Roo.BorderLayout}
34762      */
34763     getLayout : function(){
34764         return this.layout;
34765     },
34766
34767     showEl : function(){
34768         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34769         if(Roo.isIE7){
34770             this.layout.layout();
34771         }
34772     },
34773
34774     // private
34775     // Use the syncHeightBeforeShow config option to control this automatically
34776     syncBodyHeight : function(){
34777         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34778         if(this.layout){this.layout.layout();}
34779     },
34780     
34781       /**
34782      * Add an xtype element (actually adds to the layout.)
34783      * @return {Object} xdata xtype object data.
34784      */
34785     
34786     addxtype : function(c) {
34787         return this.layout.addxtype(c);
34788     }
34789 });/*
34790  * Based on:
34791  * Ext JS Library 1.1.1
34792  * Copyright(c) 2006-2007, Ext JS, LLC.
34793  *
34794  * Originally Released Under LGPL - original licence link has changed is not relivant.
34795  *
34796  * Fork - LGPL
34797  * <script type="text/javascript">
34798  */
34799  
34800 /**
34801  * @class Roo.MessageBox
34802  * @static
34803  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34804  * Example usage:
34805  *<pre><code>
34806 // Basic alert:
34807 Roo.Msg.alert('Status', 'Changes saved successfully.');
34808
34809 // Prompt for user data:
34810 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34811     if (btn == 'ok'){
34812         // process text value...
34813     }
34814 });
34815
34816 // Show a dialog using config options:
34817 Roo.Msg.show({
34818    title:'Save Changes?',
34819    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34820    buttons: Roo.Msg.YESNOCANCEL,
34821    fn: processResult,
34822    animEl: 'elId'
34823 });
34824 </code></pre>
34825  * @static
34826  */
34827 Roo.MessageBox = function(){
34828     var dlg, opt, mask, waitTimer;
34829     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34830     var buttons, activeTextEl, bwidth;
34831
34832     // private
34833     var handleButton = function(button){
34834         dlg.hide();
34835         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34836     };
34837
34838     // private
34839     var handleHide = function(){
34840         if(opt && opt.cls){
34841             dlg.el.removeClass(opt.cls);
34842         }
34843         if(waitTimer){
34844             Roo.TaskMgr.stop(waitTimer);
34845             waitTimer = null;
34846         }
34847     };
34848
34849     // private
34850     var updateButtons = function(b){
34851         var width = 0;
34852         if(!b){
34853             buttons["ok"].hide();
34854             buttons["cancel"].hide();
34855             buttons["yes"].hide();
34856             buttons["no"].hide();
34857             dlg.footer.dom.style.display = 'none';
34858             return width;
34859         }
34860         dlg.footer.dom.style.display = '';
34861         for(var k in buttons){
34862             if(typeof buttons[k] != "function"){
34863                 if(b[k]){
34864                     buttons[k].show();
34865                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34866                     width += buttons[k].el.getWidth()+15;
34867                 }else{
34868                     buttons[k].hide();
34869                 }
34870             }
34871         }
34872         return width;
34873     };
34874
34875     // private
34876     var handleEsc = function(d, k, e){
34877         if(opt && opt.closable !== false){
34878             dlg.hide();
34879         }
34880         if(e){
34881             e.stopEvent();
34882         }
34883     };
34884
34885     return {
34886         /**
34887          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34888          * @return {Roo.BasicDialog} The BasicDialog element
34889          */
34890         getDialog : function(){
34891            if(!dlg){
34892                 dlg = new Roo.BasicDialog("x-msg-box", {
34893                     autoCreate : true,
34894                     shadow: true,
34895                     draggable: true,
34896                     resizable:false,
34897                     constraintoviewport:false,
34898                     fixedcenter:true,
34899                     collapsible : false,
34900                     shim:true,
34901                     modal: true,
34902                     width:400, height:100,
34903                     buttonAlign:"center",
34904                     closeClick : function(){
34905                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34906                             handleButton("no");
34907                         }else{
34908                             handleButton("cancel");
34909                         }
34910                     }
34911                 });
34912               
34913                 dlg.on("hide", handleHide);
34914                 mask = dlg.mask;
34915                 dlg.addKeyListener(27, handleEsc);
34916                 buttons = {};
34917                 var bt = this.buttonText;
34918                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34919                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34920                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34921                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34922                 bodyEl = dlg.body.createChild({
34923
34924                     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>'
34925                 });
34926                 msgEl = bodyEl.dom.firstChild;
34927                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34928                 textboxEl.enableDisplayMode();
34929                 textboxEl.addKeyListener([10,13], function(){
34930                     if(dlg.isVisible() && opt && opt.buttons){
34931                         if(opt.buttons.ok){
34932                             handleButton("ok");
34933                         }else if(opt.buttons.yes){
34934                             handleButton("yes");
34935                         }
34936                     }
34937                 });
34938                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34939                 textareaEl.enableDisplayMode();
34940                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34941                 progressEl.enableDisplayMode();
34942                 var pf = progressEl.dom.firstChild;
34943                 if (pf) {
34944                     pp = Roo.get(pf.firstChild);
34945                     pp.setHeight(pf.offsetHeight);
34946                 }
34947                 
34948             }
34949             return dlg;
34950         },
34951
34952         /**
34953          * Updates the message box body text
34954          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34955          * the XHTML-compliant non-breaking space character '&amp;#160;')
34956          * @return {Roo.MessageBox} This message box
34957          */
34958         updateText : function(text){
34959             if(!dlg.isVisible() && !opt.width){
34960                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34961             }
34962             msgEl.innerHTML = text || '&#160;';
34963       
34964             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34965             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34966             var w = Math.max(
34967                     Math.min(opt.width || cw , this.maxWidth), 
34968                     Math.max(opt.minWidth || this.minWidth, bwidth)
34969             );
34970             if(opt.prompt){
34971                 activeTextEl.setWidth(w);
34972             }
34973             if(dlg.isVisible()){
34974                 dlg.fixedcenter = false;
34975             }
34976             // to big, make it scroll. = But as usual stupid IE does not support
34977             // !important..
34978             
34979             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34980                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34981                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34982             } else {
34983                 bodyEl.dom.style.height = '';
34984                 bodyEl.dom.style.overflowY = '';
34985             }
34986             if (cw > w) {
34987                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34988             } else {
34989                 bodyEl.dom.style.overflowX = '';
34990             }
34991             
34992             dlg.setContentSize(w, bodyEl.getHeight());
34993             if(dlg.isVisible()){
34994                 dlg.fixedcenter = true;
34995             }
34996             return this;
34997         },
34998
34999         /**
35000          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
35001          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
35002          * @param {Number} value Any number between 0 and 1 (e.g., .5)
35003          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
35004          * @return {Roo.MessageBox} This message box
35005          */
35006         updateProgress : function(value, text){
35007             if(text){
35008                 this.updateText(text);
35009             }
35010             if (pp) { // weird bug on my firefox - for some reason this is not defined
35011                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
35012             }
35013             return this;
35014         },        
35015
35016         /**
35017          * Returns true if the message box is currently displayed
35018          * @return {Boolean} True if the message box is visible, else false
35019          */
35020         isVisible : function(){
35021             return dlg && dlg.isVisible();  
35022         },
35023
35024         /**
35025          * Hides the message box if it is displayed
35026          */
35027         hide : function(){
35028             if(this.isVisible()){
35029                 dlg.hide();
35030             }  
35031         },
35032
35033         /**
35034          * Displays a new message box, or reinitializes an existing message box, based on the config options
35035          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
35036          * The following config object properties are supported:
35037          * <pre>
35038 Property    Type             Description
35039 ----------  ---------------  ------------------------------------------------------------------------------------
35040 animEl            String/Element   An id or Element from which the message box should animate as it opens and
35041                                    closes (defaults to undefined)
35042 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
35043                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
35044 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
35045                                    progress and wait dialogs will ignore this property and always hide the
35046                                    close button as they can only be closed programmatically.
35047 cls               String           A custom CSS class to apply to the message box element
35048 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
35049                                    displayed (defaults to 75)
35050 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
35051                                    function will be btn (the name of the button that was clicked, if applicable,
35052                                    e.g. "ok"), and text (the value of the active text field, if applicable).
35053                                    Progress and wait dialogs will ignore this option since they do not respond to
35054                                    user actions and can only be closed programmatically, so any required function
35055                                    should be called by the same code after it closes the dialog.
35056 icon              String           A CSS class that provides a background image to be used as an icon for
35057                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
35058 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
35059 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
35060 modal             Boolean          False to allow user interaction with the page while the message box is
35061                                    displayed (defaults to true)
35062 msg               String           A string that will replace the existing message box body text (defaults
35063                                    to the XHTML-compliant non-breaking space character '&#160;')
35064 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
35065 progress          Boolean          True to display a progress bar (defaults to false)
35066 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
35067 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
35068 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
35069 title             String           The title text
35070 value             String           The string value to set into the active textbox element if displayed
35071 wait              Boolean          True to display a progress bar (defaults to false)
35072 width             Number           The width of the dialog in pixels
35073 </pre>
35074          *
35075          * Example usage:
35076          * <pre><code>
35077 Roo.Msg.show({
35078    title: 'Address',
35079    msg: 'Please enter your address:',
35080    width: 300,
35081    buttons: Roo.MessageBox.OKCANCEL,
35082    multiline: true,
35083    fn: saveAddress,
35084    animEl: 'addAddressBtn'
35085 });
35086 </code></pre>
35087          * @param {Object} config Configuration options
35088          * @return {Roo.MessageBox} This message box
35089          */
35090         show : function(options)
35091         {
35092             
35093             // this causes nightmares if you show one dialog after another
35094             // especially on callbacks..
35095              
35096             if(this.isVisible()){
35097                 
35098                 this.hide();
35099                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
35100                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
35101                 Roo.log("New Dialog Message:" +  options.msg )
35102                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
35103                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
35104                 
35105             }
35106             var d = this.getDialog();
35107             opt = options;
35108             d.setTitle(opt.title || "&#160;");
35109             d.close.setDisplayed(opt.closable !== false);
35110             activeTextEl = textboxEl;
35111             opt.prompt = opt.prompt || (opt.multiline ? true : false);
35112             if(opt.prompt){
35113                 if(opt.multiline){
35114                     textboxEl.hide();
35115                     textareaEl.show();
35116                     textareaEl.setHeight(typeof opt.multiline == "number" ?
35117                         opt.multiline : this.defaultTextHeight);
35118                     activeTextEl = textareaEl;
35119                 }else{
35120                     textboxEl.show();
35121                     textareaEl.hide();
35122                 }
35123             }else{
35124                 textboxEl.hide();
35125                 textareaEl.hide();
35126             }
35127             progressEl.setDisplayed(opt.progress === true);
35128             this.updateProgress(0);
35129             activeTextEl.dom.value = opt.value || "";
35130             if(opt.prompt){
35131                 dlg.setDefaultButton(activeTextEl);
35132             }else{
35133                 var bs = opt.buttons;
35134                 var db = null;
35135                 if(bs && bs.ok){
35136                     db = buttons["ok"];
35137                 }else if(bs && bs.yes){
35138                     db = buttons["yes"];
35139                 }
35140                 dlg.setDefaultButton(db);
35141             }
35142             bwidth = updateButtons(opt.buttons);
35143             this.updateText(opt.msg);
35144             if(opt.cls){
35145                 d.el.addClass(opt.cls);
35146             }
35147             d.proxyDrag = opt.proxyDrag === true;
35148             d.modal = opt.modal !== false;
35149             d.mask = opt.modal !== false ? mask : false;
35150             if(!d.isVisible()){
35151                 // force it to the end of the z-index stack so it gets a cursor in FF
35152                 document.body.appendChild(dlg.el.dom);
35153                 d.animateTarget = null;
35154                 d.show(options.animEl);
35155             }
35156             dlg.toFront();
35157             return this;
35158         },
35159
35160         /**
35161          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
35162          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
35163          * and closing the message box when the process is complete.
35164          * @param {String} title The title bar text
35165          * @param {String} msg The message box body text
35166          * @return {Roo.MessageBox} This message box
35167          */
35168         progress : function(title, msg){
35169             this.show({
35170                 title : title,
35171                 msg : msg,
35172                 buttons: false,
35173                 progress:true,
35174                 closable:false,
35175                 minWidth: this.minProgressWidth,
35176                 modal : true
35177             });
35178             return this;
35179         },
35180
35181         /**
35182          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
35183          * If a callback function is passed it will be called after the user clicks the button, and the
35184          * id of the button that was clicked will be passed as the only parameter to the callback
35185          * (could also be the top-right close button).
35186          * @param {String} title The title bar text
35187          * @param {String} msg The message box body text
35188          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35189          * @param {Object} scope (optional) The scope of the callback function
35190          * @return {Roo.MessageBox} This message box
35191          */
35192         alert : function(title, msg, fn, scope){
35193             this.show({
35194                 title : title,
35195                 msg : msg,
35196                 buttons: this.OK,
35197                 fn: fn,
35198                 scope : scope,
35199                 modal : true
35200             });
35201             return this;
35202         },
35203
35204         /**
35205          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
35206          * interaction while waiting for a long-running process to complete that does not have defined intervals.
35207          * You are responsible for closing the message box when the process is complete.
35208          * @param {String} msg The message box body text
35209          * @param {String} title (optional) The title bar text
35210          * @return {Roo.MessageBox} This message box
35211          */
35212         wait : function(msg, title){
35213             this.show({
35214                 title : title,
35215                 msg : msg,
35216                 buttons: false,
35217                 closable:false,
35218                 progress:true,
35219                 modal:true,
35220                 width:300,
35221                 wait:true
35222             });
35223             waitTimer = Roo.TaskMgr.start({
35224                 run: function(i){
35225                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35226                 },
35227                 interval: 1000
35228             });
35229             return this;
35230         },
35231
35232         /**
35233          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35234          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35235          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35236          * @param {String} title The title bar text
35237          * @param {String} msg The message box body text
35238          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35239          * @param {Object} scope (optional) The scope of the callback function
35240          * @return {Roo.MessageBox} This message box
35241          */
35242         confirm : function(title, msg, fn, scope){
35243             this.show({
35244                 title : title,
35245                 msg : msg,
35246                 buttons: this.YESNO,
35247                 fn: fn,
35248                 scope : scope,
35249                 modal : true
35250             });
35251             return this;
35252         },
35253
35254         /**
35255          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35256          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35257          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35258          * (could also be the top-right close button) and the text that was entered will be passed as the two
35259          * parameters to the callback.
35260          * @param {String} title The title bar text
35261          * @param {String} msg The message box body text
35262          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35263          * @param {Object} scope (optional) The scope of the callback function
35264          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35265          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35266          * @return {Roo.MessageBox} This message box
35267          */
35268         prompt : function(title, msg, fn, scope, multiline){
35269             this.show({
35270                 title : title,
35271                 msg : msg,
35272                 buttons: this.OKCANCEL,
35273                 fn: fn,
35274                 minWidth:250,
35275                 scope : scope,
35276                 prompt:true,
35277                 multiline: multiline,
35278                 modal : true
35279             });
35280             return this;
35281         },
35282
35283         /**
35284          * Button config that displays a single OK button
35285          * @type Object
35286          */
35287         OK : {ok:true},
35288         /**
35289          * Button config that displays Yes and No buttons
35290          * @type Object
35291          */
35292         YESNO : {yes:true, no:true},
35293         /**
35294          * Button config that displays OK and Cancel buttons
35295          * @type Object
35296          */
35297         OKCANCEL : {ok:true, cancel:true},
35298         /**
35299          * Button config that displays Yes, No and Cancel buttons
35300          * @type Object
35301          */
35302         YESNOCANCEL : {yes:true, no:true, cancel:true},
35303
35304         /**
35305          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35306          * @type Number
35307          */
35308         defaultTextHeight : 75,
35309         /**
35310          * The maximum width in pixels of the message box (defaults to 600)
35311          * @type Number
35312          */
35313         maxWidth : 600,
35314         /**
35315          * The minimum width in pixels of the message box (defaults to 100)
35316          * @type Number
35317          */
35318         minWidth : 100,
35319         /**
35320          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35321          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35322          * @type Number
35323          */
35324         minProgressWidth : 250,
35325         /**
35326          * An object containing the default button text strings that can be overriden for localized language support.
35327          * Supported properties are: ok, cancel, yes and no.
35328          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35329          * @type Object
35330          */
35331         buttonText : {
35332             ok : "OK",
35333             cancel : "Cancel",
35334             yes : "Yes",
35335             no : "No"
35336         }
35337     };
35338 }();
35339
35340 /**
35341  * Shorthand for {@link Roo.MessageBox}
35342  */
35343 Roo.Msg = Roo.MessageBox;/*
35344  * Based on:
35345  * Ext JS Library 1.1.1
35346  * Copyright(c) 2006-2007, Ext JS, LLC.
35347  *
35348  * Originally Released Under LGPL - original licence link has changed is not relivant.
35349  *
35350  * Fork - LGPL
35351  * <script type="text/javascript">
35352  */
35353 /**
35354  * @class Roo.QuickTips
35355  * Provides attractive and customizable tooltips for any element.
35356  * @static
35357  */
35358 Roo.QuickTips = function(){
35359     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35360     var ce, bd, xy, dd;
35361     var visible = false, disabled = true, inited = false;
35362     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35363     
35364     var onOver = function(e){
35365         if(disabled){
35366             return;
35367         }
35368         var t = e.getTarget();
35369         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35370             return;
35371         }
35372         if(ce && t == ce.el){
35373             clearTimeout(hideProc);
35374             return;
35375         }
35376         if(t && tagEls[t.id]){
35377             tagEls[t.id].el = t;
35378             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35379             return;
35380         }
35381         var ttp, et = Roo.fly(t);
35382         var ns = cfg.namespace;
35383         if(tm.interceptTitles && t.title){
35384             ttp = t.title;
35385             t.qtip = ttp;
35386             t.removeAttribute("title");
35387             e.preventDefault();
35388         }else{
35389             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35390         }
35391         if(ttp){
35392             showProc = show.defer(tm.showDelay, tm, [{
35393                 el: t, 
35394                 text: ttp.replace(/\\n/g,'<br/>'),
35395                 width: et.getAttributeNS(ns, cfg.width),
35396                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35397                 title: et.getAttributeNS(ns, cfg.title),
35398                     cls: et.getAttributeNS(ns, cfg.cls)
35399             }]);
35400         }
35401     };
35402     
35403     var onOut = function(e){
35404         clearTimeout(showProc);
35405         var t = e.getTarget();
35406         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35407             hideProc = setTimeout(hide, tm.hideDelay);
35408         }
35409     };
35410     
35411     var onMove = function(e){
35412         if(disabled){
35413             return;
35414         }
35415         xy = e.getXY();
35416         xy[1] += 18;
35417         if(tm.trackMouse && ce){
35418             el.setXY(xy);
35419         }
35420     };
35421     
35422     var onDown = function(e){
35423         clearTimeout(showProc);
35424         clearTimeout(hideProc);
35425         if(!e.within(el)){
35426             if(tm.hideOnClick){
35427                 hide();
35428                 tm.disable();
35429                 tm.enable.defer(100, tm);
35430             }
35431         }
35432     };
35433     
35434     var getPad = function(){
35435         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35436     };
35437
35438     var show = function(o){
35439         if(disabled){
35440             return;
35441         }
35442         clearTimeout(dismissProc);
35443         ce = o;
35444         if(removeCls){ // in case manually hidden
35445             el.removeClass(removeCls);
35446             removeCls = null;
35447         }
35448         if(ce.cls){
35449             el.addClass(ce.cls);
35450             removeCls = ce.cls;
35451         }
35452         if(ce.title){
35453             tipTitle.update(ce.title);
35454             tipTitle.show();
35455         }else{
35456             tipTitle.update('');
35457             tipTitle.hide();
35458         }
35459         el.dom.style.width  = tm.maxWidth+'px';
35460         //tipBody.dom.style.width = '';
35461         tipBodyText.update(o.text);
35462         var p = getPad(), w = ce.width;
35463         if(!w){
35464             var td = tipBodyText.dom;
35465             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35466             if(aw > tm.maxWidth){
35467                 w = tm.maxWidth;
35468             }else if(aw < tm.minWidth){
35469                 w = tm.minWidth;
35470             }else{
35471                 w = aw;
35472             }
35473         }
35474         //tipBody.setWidth(w);
35475         el.setWidth(parseInt(w, 10) + p);
35476         if(ce.autoHide === false){
35477             close.setDisplayed(true);
35478             if(dd){
35479                 dd.unlock();
35480             }
35481         }else{
35482             close.setDisplayed(false);
35483             if(dd){
35484                 dd.lock();
35485             }
35486         }
35487         if(xy){
35488             el.avoidY = xy[1]-18;
35489             el.setXY(xy);
35490         }
35491         if(tm.animate){
35492             el.setOpacity(.1);
35493             el.setStyle("visibility", "visible");
35494             el.fadeIn({callback: afterShow});
35495         }else{
35496             afterShow();
35497         }
35498     };
35499     
35500     var afterShow = function(){
35501         if(ce){
35502             el.show();
35503             esc.enable();
35504             if(tm.autoDismiss && ce.autoHide !== false){
35505                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35506             }
35507         }
35508     };
35509     
35510     var hide = function(noanim){
35511         clearTimeout(dismissProc);
35512         clearTimeout(hideProc);
35513         ce = null;
35514         if(el.isVisible()){
35515             esc.disable();
35516             if(noanim !== true && tm.animate){
35517                 el.fadeOut({callback: afterHide});
35518             }else{
35519                 afterHide();
35520             } 
35521         }
35522     };
35523     
35524     var afterHide = function(){
35525         el.hide();
35526         if(removeCls){
35527             el.removeClass(removeCls);
35528             removeCls = null;
35529         }
35530     };
35531     
35532     return {
35533         /**
35534         * @cfg {Number} minWidth
35535         * The minimum width of the quick tip (defaults to 40)
35536         */
35537        minWidth : 40,
35538         /**
35539         * @cfg {Number} maxWidth
35540         * The maximum width of the quick tip (defaults to 300)
35541         */
35542        maxWidth : 300,
35543         /**
35544         * @cfg {Boolean} interceptTitles
35545         * True to automatically use the element's DOM title value if available (defaults to false)
35546         */
35547        interceptTitles : false,
35548         /**
35549         * @cfg {Boolean} trackMouse
35550         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35551         */
35552        trackMouse : false,
35553         /**
35554         * @cfg {Boolean} hideOnClick
35555         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35556         */
35557        hideOnClick : true,
35558         /**
35559         * @cfg {Number} showDelay
35560         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35561         */
35562        showDelay : 500,
35563         /**
35564         * @cfg {Number} hideDelay
35565         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35566         */
35567        hideDelay : 200,
35568         /**
35569         * @cfg {Boolean} autoHide
35570         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35571         * Used in conjunction with hideDelay.
35572         */
35573        autoHide : true,
35574         /**
35575         * @cfg {Boolean}
35576         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35577         * (defaults to true).  Used in conjunction with autoDismissDelay.
35578         */
35579        autoDismiss : true,
35580         /**
35581         * @cfg {Number}
35582         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35583         */
35584        autoDismissDelay : 5000,
35585        /**
35586         * @cfg {Boolean} animate
35587         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35588         */
35589        animate : false,
35590
35591        /**
35592         * @cfg {String} title
35593         * Title text to display (defaults to '').  This can be any valid HTML markup.
35594         */
35595         title: '',
35596        /**
35597         * @cfg {String} text
35598         * Body text to display (defaults to '').  This can be any valid HTML markup.
35599         */
35600         text : '',
35601        /**
35602         * @cfg {String} cls
35603         * A CSS class to apply to the base quick tip element (defaults to '').
35604         */
35605         cls : '',
35606        /**
35607         * @cfg {Number} width
35608         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35609         * minWidth or maxWidth.
35610         */
35611         width : null,
35612
35613     /**
35614      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35615      * or display QuickTips in a page.
35616      */
35617        init : function(){
35618           tm = Roo.QuickTips;
35619           cfg = tm.tagConfig;
35620           if(!inited){
35621               if(!Roo.isReady){ // allow calling of init() before onReady
35622                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35623                   return;
35624               }
35625               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35626               el.fxDefaults = {stopFx: true};
35627               // maximum custom styling
35628               //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>');
35629               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>');              
35630               tipTitle = el.child('h3');
35631               tipTitle.enableDisplayMode("block");
35632               tipBody = el.child('div.x-tip-bd');
35633               tipBodyText = el.child('div.x-tip-bd-inner');
35634               //bdLeft = el.child('div.x-tip-bd-left');
35635               //bdRight = el.child('div.x-tip-bd-right');
35636               close = el.child('div.x-tip-close');
35637               close.enableDisplayMode("block");
35638               close.on("click", hide);
35639               var d = Roo.get(document);
35640               d.on("mousedown", onDown);
35641               d.on("mouseover", onOver);
35642               d.on("mouseout", onOut);
35643               d.on("mousemove", onMove);
35644               esc = d.addKeyListener(27, hide);
35645               esc.disable();
35646               if(Roo.dd.DD){
35647                   dd = el.initDD("default", null, {
35648                       onDrag : function(){
35649                           el.sync();  
35650                       }
35651                   });
35652                   dd.setHandleElId(tipTitle.id);
35653                   dd.lock();
35654               }
35655               inited = true;
35656           }
35657           this.enable(); 
35658        },
35659
35660     /**
35661      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35662      * are supported:
35663      * <pre>
35664 Property    Type                   Description
35665 ----------  ---------------------  ------------------------------------------------------------------------
35666 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35667      * </ul>
35668      * @param {Object} config The config object
35669      */
35670        register : function(config){
35671            var cs = config instanceof Array ? config : arguments;
35672            for(var i = 0, len = cs.length; i < len; i++) {
35673                var c = cs[i];
35674                var target = c.target;
35675                if(target){
35676                    if(target instanceof Array){
35677                        for(var j = 0, jlen = target.length; j < jlen; j++){
35678                            tagEls[target[j]] = c;
35679                        }
35680                    }else{
35681                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35682                    }
35683                }
35684            }
35685        },
35686
35687     /**
35688      * Removes this quick tip from its element and destroys it.
35689      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35690      */
35691        unregister : function(el){
35692            delete tagEls[Roo.id(el)];
35693        },
35694
35695     /**
35696      * Enable this quick tip.
35697      */
35698        enable : function(){
35699            if(inited && disabled){
35700                locks.pop();
35701                if(locks.length < 1){
35702                    disabled = false;
35703                }
35704            }
35705        },
35706
35707     /**
35708      * Disable this quick tip.
35709      */
35710        disable : function(){
35711           disabled = true;
35712           clearTimeout(showProc);
35713           clearTimeout(hideProc);
35714           clearTimeout(dismissProc);
35715           if(ce){
35716               hide(true);
35717           }
35718           locks.push(1);
35719        },
35720
35721     /**
35722      * Returns true if the quick tip is enabled, else false.
35723      */
35724        isEnabled : function(){
35725             return !disabled;
35726        },
35727
35728         // private
35729        tagConfig : {
35730            namespace : "roo", // was ext?? this may break..
35731            alt_namespace : "ext",
35732            attribute : "qtip",
35733            width : "width",
35734            target : "target",
35735            title : "qtitle",
35736            hide : "hide",
35737            cls : "qclass"
35738        }
35739    };
35740 }();
35741
35742 // backwards compat
35743 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35744  * Based on:
35745  * Ext JS Library 1.1.1
35746  * Copyright(c) 2006-2007, Ext JS, LLC.
35747  *
35748  * Originally Released Under LGPL - original licence link has changed is not relivant.
35749  *
35750  * Fork - LGPL
35751  * <script type="text/javascript">
35752  */
35753  
35754
35755 /**
35756  * @class Roo.tree.TreePanel
35757  * @extends Roo.data.Tree
35758  * @cfg {Roo.tree.TreeNode} root The root node
35759  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35760  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35761  * @cfg {Boolean} enableDD true to enable drag and drop
35762  * @cfg {Boolean} enableDrag true to enable just drag
35763  * @cfg {Boolean} enableDrop true to enable just drop
35764  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35765  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35766  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35767  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35768  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35769  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35770  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35771  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35772  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35773  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35774  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35775  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35776  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35777  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35778  * @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>
35779  * @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>
35780  * 
35781  * @constructor
35782  * @param {String/HTMLElement/Element} el The container element
35783  * @param {Object} config
35784  */
35785 Roo.tree.TreePanel = function(el, config){
35786     var root = false;
35787     var loader = false;
35788     if (config.root) {
35789         root = config.root;
35790         delete config.root;
35791     }
35792     if (config.loader) {
35793         loader = config.loader;
35794         delete config.loader;
35795     }
35796     
35797     Roo.apply(this, config);
35798     Roo.tree.TreePanel.superclass.constructor.call(this);
35799     this.el = Roo.get(el);
35800     this.el.addClass('x-tree');
35801     //console.log(root);
35802     if (root) {
35803         this.setRootNode( Roo.factory(root, Roo.tree));
35804     }
35805     if (loader) {
35806         this.loader = Roo.factory(loader, Roo.tree);
35807     }
35808    /**
35809     * Read-only. The id of the container element becomes this TreePanel's id.
35810     */
35811     this.id = this.el.id;
35812     this.addEvents({
35813         /**
35814         * @event beforeload
35815         * Fires before a node is loaded, return false to cancel
35816         * @param {Node} node The node being loaded
35817         */
35818         "beforeload" : true,
35819         /**
35820         * @event load
35821         * Fires when a node is loaded
35822         * @param {Node} node The node that was loaded
35823         */
35824         "load" : true,
35825         /**
35826         * @event textchange
35827         * Fires when the text for a node is changed
35828         * @param {Node} node The node
35829         * @param {String} text The new text
35830         * @param {String} oldText The old text
35831         */
35832         "textchange" : true,
35833         /**
35834         * @event beforeexpand
35835         * Fires before a node is expanded, return false to cancel.
35836         * @param {Node} node The node
35837         * @param {Boolean} deep
35838         * @param {Boolean} anim
35839         */
35840         "beforeexpand" : true,
35841         /**
35842         * @event beforecollapse
35843         * Fires before a node is collapsed, return false to cancel.
35844         * @param {Node} node The node
35845         * @param {Boolean} deep
35846         * @param {Boolean} anim
35847         */
35848         "beforecollapse" : true,
35849         /**
35850         * @event expand
35851         * Fires when a node is expanded
35852         * @param {Node} node The node
35853         */
35854         "expand" : true,
35855         /**
35856         * @event disabledchange
35857         * Fires when the disabled status of a node changes
35858         * @param {Node} node The node
35859         * @param {Boolean} disabled
35860         */
35861         "disabledchange" : true,
35862         /**
35863         * @event collapse
35864         * Fires when a node is collapsed
35865         * @param {Node} node The node
35866         */
35867         "collapse" : true,
35868         /**
35869         * @event beforeclick
35870         * Fires before click processing on a node. Return false to cancel the default action.
35871         * @param {Node} node The node
35872         * @param {Roo.EventObject} e The event object
35873         */
35874         "beforeclick":true,
35875         /**
35876         * @event checkchange
35877         * Fires when a node with a checkbox's checked property changes
35878         * @param {Node} this This node
35879         * @param {Boolean} checked
35880         */
35881         "checkchange":true,
35882         /**
35883         * @event click
35884         * Fires when a node is clicked
35885         * @param {Node} node The node
35886         * @param {Roo.EventObject} e The event object
35887         */
35888         "click":true,
35889         /**
35890         * @event dblclick
35891         * Fires when a node is double clicked
35892         * @param {Node} node The node
35893         * @param {Roo.EventObject} e The event object
35894         */
35895         "dblclick":true,
35896         /**
35897         * @event contextmenu
35898         * Fires when a node is right clicked
35899         * @param {Node} node The node
35900         * @param {Roo.EventObject} e The event object
35901         */
35902         "contextmenu":true,
35903         /**
35904         * @event beforechildrenrendered
35905         * Fires right before the child nodes for a node are rendered
35906         * @param {Node} node The node
35907         */
35908         "beforechildrenrendered":true,
35909         /**
35910         * @event startdrag
35911         * Fires when a node starts being dragged
35912         * @param {Roo.tree.TreePanel} this
35913         * @param {Roo.tree.TreeNode} node
35914         * @param {event} e The raw browser event
35915         */ 
35916        "startdrag" : true,
35917        /**
35918         * @event enddrag
35919         * Fires when a drag operation is complete
35920         * @param {Roo.tree.TreePanel} this
35921         * @param {Roo.tree.TreeNode} node
35922         * @param {event} e The raw browser event
35923         */
35924        "enddrag" : true,
35925        /**
35926         * @event dragdrop
35927         * Fires when a dragged node is dropped on a valid DD target
35928         * @param {Roo.tree.TreePanel} this
35929         * @param {Roo.tree.TreeNode} node
35930         * @param {DD} dd The dd it was dropped on
35931         * @param {event} e The raw browser event
35932         */
35933        "dragdrop" : true,
35934        /**
35935         * @event beforenodedrop
35936         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35937         * passed to handlers has the following properties:<br />
35938         * <ul style="padding:5px;padding-left:16px;">
35939         * <li>tree - The TreePanel</li>
35940         * <li>target - The node being targeted for the drop</li>
35941         * <li>data - The drag data from the drag source</li>
35942         * <li>point - The point of the drop - append, above or below</li>
35943         * <li>source - The drag source</li>
35944         * <li>rawEvent - Raw mouse event</li>
35945         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35946         * to be inserted by setting them on this object.</li>
35947         * <li>cancel - Set this to true to cancel the drop.</li>
35948         * </ul>
35949         * @param {Object} dropEvent
35950         */
35951        "beforenodedrop" : true,
35952        /**
35953         * @event nodedrop
35954         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35955         * passed to handlers has the following properties:<br />
35956         * <ul style="padding:5px;padding-left:16px;">
35957         * <li>tree - The TreePanel</li>
35958         * <li>target - The node being targeted for the drop</li>
35959         * <li>data - The drag data from the drag source</li>
35960         * <li>point - The point of the drop - append, above or below</li>
35961         * <li>source - The drag source</li>
35962         * <li>rawEvent - Raw mouse event</li>
35963         * <li>dropNode - Dropped node(s).</li>
35964         * </ul>
35965         * @param {Object} dropEvent
35966         */
35967        "nodedrop" : true,
35968         /**
35969         * @event nodedragover
35970         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35971         * passed to handlers has the following properties:<br />
35972         * <ul style="padding:5px;padding-left:16px;">
35973         * <li>tree - The TreePanel</li>
35974         * <li>target - The node being targeted for the drop</li>
35975         * <li>data - The drag data from the drag source</li>
35976         * <li>point - The point of the drop - append, above or below</li>
35977         * <li>source - The drag source</li>
35978         * <li>rawEvent - Raw mouse event</li>
35979         * <li>dropNode - Drop node(s) provided by the source.</li>
35980         * <li>cancel - Set this to true to signal drop not allowed.</li>
35981         * </ul>
35982         * @param {Object} dragOverEvent
35983         */
35984        "nodedragover" : true,
35985        /**
35986         * @event appendnode
35987         * Fires when append node to the tree
35988         * @param {Roo.tree.TreePanel} this
35989         * @param {Roo.tree.TreeNode} node
35990         * @param {Number} index The index of the newly appended node
35991         */
35992        "appendnode" : true
35993         
35994     });
35995     if(this.singleExpand){
35996        this.on("beforeexpand", this.restrictExpand, this);
35997     }
35998     if (this.editor) {
35999         this.editor.tree = this;
36000         this.editor = Roo.factory(this.editor, Roo.tree);
36001     }
36002     
36003     if (this.selModel) {
36004         this.selModel = Roo.factory(this.selModel, Roo.tree);
36005     }
36006    
36007 };
36008 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
36009     rootVisible : true,
36010     animate: Roo.enableFx,
36011     lines : true,
36012     enableDD : false,
36013     hlDrop : Roo.enableFx,
36014   
36015     renderer: false,
36016     
36017     rendererTip: false,
36018     // private
36019     restrictExpand : function(node){
36020         var p = node.parentNode;
36021         if(p){
36022             if(p.expandedChild && p.expandedChild.parentNode == p){
36023                 p.expandedChild.collapse();
36024             }
36025             p.expandedChild = node;
36026         }
36027     },
36028
36029     // private override
36030     setRootNode : function(node){
36031         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
36032         if(!this.rootVisible){
36033             node.ui = new Roo.tree.RootTreeNodeUI(node);
36034         }
36035         return node;
36036     },
36037
36038     /**
36039      * Returns the container element for this TreePanel
36040      */
36041     getEl : function(){
36042         return this.el;
36043     },
36044
36045     /**
36046      * Returns the default TreeLoader for this TreePanel
36047      */
36048     getLoader : function(){
36049         return this.loader;
36050     },
36051
36052     /**
36053      * Expand all nodes
36054      */
36055     expandAll : function(){
36056         this.root.expand(true);
36057     },
36058
36059     /**
36060      * Collapse all nodes
36061      */
36062     collapseAll : function(){
36063         this.root.collapse(true);
36064     },
36065
36066     /**
36067      * Returns the selection model used by this TreePanel
36068      */
36069     getSelectionModel : function(){
36070         if(!this.selModel){
36071             this.selModel = new Roo.tree.DefaultSelectionModel();
36072         }
36073         return this.selModel;
36074     },
36075
36076     /**
36077      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
36078      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
36079      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
36080      * @return {Array}
36081      */
36082     getChecked : function(a, startNode){
36083         startNode = startNode || this.root;
36084         var r = [];
36085         var f = function(){
36086             if(this.attributes.checked){
36087                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
36088             }
36089         }
36090         startNode.cascade(f);
36091         return r;
36092     },
36093
36094     /**
36095      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36096      * @param {String} path
36097      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36098      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
36099      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
36100      */
36101     expandPath : function(path, attr, callback){
36102         attr = attr || "id";
36103         var keys = path.split(this.pathSeparator);
36104         var curNode = this.root;
36105         if(curNode.attributes[attr] != keys[1]){ // invalid root
36106             if(callback){
36107                 callback(false, null);
36108             }
36109             return;
36110         }
36111         var index = 1;
36112         var f = function(){
36113             if(++index == keys.length){
36114                 if(callback){
36115                     callback(true, curNode);
36116                 }
36117                 return;
36118             }
36119             var c = curNode.findChild(attr, keys[index]);
36120             if(!c){
36121                 if(callback){
36122                     callback(false, curNode);
36123                 }
36124                 return;
36125             }
36126             curNode = c;
36127             c.expand(false, false, f);
36128         };
36129         curNode.expand(false, false, f);
36130     },
36131
36132     /**
36133      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36134      * @param {String} path
36135      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36136      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
36137      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
36138      */
36139     selectPath : function(path, attr, callback){
36140         attr = attr || "id";
36141         var keys = path.split(this.pathSeparator);
36142         var v = keys.pop();
36143         if(keys.length > 0){
36144             var f = function(success, node){
36145                 if(success && node){
36146                     var n = node.findChild(attr, v);
36147                     if(n){
36148                         n.select();
36149                         if(callback){
36150                             callback(true, n);
36151                         }
36152                     }else if(callback){
36153                         callback(false, n);
36154                     }
36155                 }else{
36156                     if(callback){
36157                         callback(false, n);
36158                     }
36159                 }
36160             };
36161             this.expandPath(keys.join(this.pathSeparator), attr, f);
36162         }else{
36163             this.root.select();
36164             if(callback){
36165                 callback(true, this.root);
36166             }
36167         }
36168     },
36169
36170     getTreeEl : function(){
36171         return this.el;
36172     },
36173
36174     /**
36175      * Trigger rendering of this TreePanel
36176      */
36177     render : function(){
36178         if (this.innerCt) {
36179             return this; // stop it rendering more than once!!
36180         }
36181         
36182         this.innerCt = this.el.createChild({tag:"ul",
36183                cls:"x-tree-root-ct " +
36184                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
36185
36186         if(this.containerScroll){
36187             Roo.dd.ScrollManager.register(this.el);
36188         }
36189         if((this.enableDD || this.enableDrop) && !this.dropZone){
36190            /**
36191             * The dropZone used by this tree if drop is enabled
36192             * @type Roo.tree.TreeDropZone
36193             */
36194              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
36195                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
36196            });
36197         }
36198         if((this.enableDD || this.enableDrag) && !this.dragZone){
36199            /**
36200             * The dragZone used by this tree if drag is enabled
36201             * @type Roo.tree.TreeDragZone
36202             */
36203             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
36204                ddGroup: this.ddGroup || "TreeDD",
36205                scroll: this.ddScroll
36206            });
36207         }
36208         this.getSelectionModel().init(this);
36209         if (!this.root) {
36210             Roo.log("ROOT not set in tree");
36211             return this;
36212         }
36213         this.root.render();
36214         if(!this.rootVisible){
36215             this.root.renderChildren();
36216         }
36217         return this;
36218     }
36219 });/*
36220  * Based on:
36221  * Ext JS Library 1.1.1
36222  * Copyright(c) 2006-2007, Ext JS, LLC.
36223  *
36224  * Originally Released Under LGPL - original licence link has changed is not relivant.
36225  *
36226  * Fork - LGPL
36227  * <script type="text/javascript">
36228  */
36229  
36230
36231 /**
36232  * @class Roo.tree.DefaultSelectionModel
36233  * @extends Roo.util.Observable
36234  * The default single selection for a TreePanel.
36235  * @param {Object} cfg Configuration
36236  */
36237 Roo.tree.DefaultSelectionModel = function(cfg){
36238    this.selNode = null;
36239    
36240    
36241    
36242    this.addEvents({
36243        /**
36244         * @event selectionchange
36245         * Fires when the selected node changes
36246         * @param {DefaultSelectionModel} this
36247         * @param {TreeNode} node the new selection
36248         */
36249        "selectionchange" : true,
36250
36251        /**
36252         * @event beforeselect
36253         * Fires before the selected node changes, return false to cancel the change
36254         * @param {DefaultSelectionModel} this
36255         * @param {TreeNode} node the new selection
36256         * @param {TreeNode} node the old selection
36257         */
36258        "beforeselect" : true
36259    });
36260    
36261     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36262 };
36263
36264 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36265     init : function(tree){
36266         this.tree = tree;
36267         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36268         tree.on("click", this.onNodeClick, this);
36269     },
36270     
36271     onNodeClick : function(node, e){
36272         if (e.ctrlKey && this.selNode == node)  {
36273             this.unselect(node);
36274             return;
36275         }
36276         this.select(node);
36277     },
36278     
36279     /**
36280      * Select a node.
36281      * @param {TreeNode} node The node to select
36282      * @return {TreeNode} The selected node
36283      */
36284     select : function(node){
36285         var last = this.selNode;
36286         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36287             if(last){
36288                 last.ui.onSelectedChange(false);
36289             }
36290             this.selNode = node;
36291             node.ui.onSelectedChange(true);
36292             this.fireEvent("selectionchange", this, node, last);
36293         }
36294         return node;
36295     },
36296     
36297     /**
36298      * Deselect a node.
36299      * @param {TreeNode} node The node to unselect
36300      */
36301     unselect : function(node){
36302         if(this.selNode == node){
36303             this.clearSelections();
36304         }    
36305     },
36306     
36307     /**
36308      * Clear all selections
36309      */
36310     clearSelections : function(){
36311         var n = this.selNode;
36312         if(n){
36313             n.ui.onSelectedChange(false);
36314             this.selNode = null;
36315             this.fireEvent("selectionchange", this, null);
36316         }
36317         return n;
36318     },
36319     
36320     /**
36321      * Get the selected node
36322      * @return {TreeNode} The selected node
36323      */
36324     getSelectedNode : function(){
36325         return this.selNode;    
36326     },
36327     
36328     /**
36329      * Returns true if the node is selected
36330      * @param {TreeNode} node The node to check
36331      * @return {Boolean}
36332      */
36333     isSelected : function(node){
36334         return this.selNode == node;  
36335     },
36336
36337     /**
36338      * Selects the node above the selected node in the tree, intelligently walking the nodes
36339      * @return TreeNode The new selection
36340      */
36341     selectPrevious : function(){
36342         var s = this.selNode || this.lastSelNode;
36343         if(!s){
36344             return null;
36345         }
36346         var ps = s.previousSibling;
36347         if(ps){
36348             if(!ps.isExpanded() || ps.childNodes.length < 1){
36349                 return this.select(ps);
36350             } else{
36351                 var lc = ps.lastChild;
36352                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36353                     lc = lc.lastChild;
36354                 }
36355                 return this.select(lc);
36356             }
36357         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36358             return this.select(s.parentNode);
36359         }
36360         return null;
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     selectNext : function(){
36368         var s = this.selNode || this.lastSelNode;
36369         if(!s){
36370             return null;
36371         }
36372         if(s.firstChild && s.isExpanded()){
36373              return this.select(s.firstChild);
36374          }else if(s.nextSibling){
36375              return this.select(s.nextSibling);
36376          }else if(s.parentNode){
36377             var newS = null;
36378             s.parentNode.bubble(function(){
36379                 if(this.nextSibling){
36380                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36381                     return false;
36382                 }
36383             });
36384             return newS;
36385          }
36386         return null;
36387     },
36388
36389     onKeyDown : function(e){
36390         var s = this.selNode || this.lastSelNode;
36391         // undesirable, but required
36392         var sm = this;
36393         if(!s){
36394             return;
36395         }
36396         var k = e.getKey();
36397         switch(k){
36398              case e.DOWN:
36399                  e.stopEvent();
36400                  this.selectNext();
36401              break;
36402              case e.UP:
36403                  e.stopEvent();
36404                  this.selectPrevious();
36405              break;
36406              case e.RIGHT:
36407                  e.preventDefault();
36408                  if(s.hasChildNodes()){
36409                      if(!s.isExpanded()){
36410                          s.expand();
36411                      }else if(s.firstChild){
36412                          this.select(s.firstChild, e);
36413                      }
36414                  }
36415              break;
36416              case e.LEFT:
36417                  e.preventDefault();
36418                  if(s.hasChildNodes() && s.isExpanded()){
36419                      s.collapse();
36420                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36421                      this.select(s.parentNode, e);
36422                  }
36423              break;
36424         };
36425     }
36426 });
36427
36428 /**
36429  * @class Roo.tree.MultiSelectionModel
36430  * @extends Roo.util.Observable
36431  * Multi selection for a TreePanel.
36432  * @param {Object} cfg Configuration
36433  */
36434 Roo.tree.MultiSelectionModel = function(){
36435    this.selNodes = [];
36436    this.selMap = {};
36437    this.addEvents({
36438        /**
36439         * @event selectionchange
36440         * Fires when the selected nodes change
36441         * @param {MultiSelectionModel} this
36442         * @param {Array} nodes Array of the selected nodes
36443         */
36444        "selectionchange" : true
36445    });
36446    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36447    
36448 };
36449
36450 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36451     init : function(tree){
36452         this.tree = tree;
36453         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36454         tree.on("click", this.onNodeClick, this);
36455     },
36456     
36457     onNodeClick : function(node, e){
36458         this.select(node, e, e.ctrlKey);
36459     },
36460     
36461     /**
36462      * Select a node.
36463      * @param {TreeNode} node The node to select
36464      * @param {EventObject} e (optional) An event associated with the selection
36465      * @param {Boolean} keepExisting True to retain existing selections
36466      * @return {TreeNode} The selected node
36467      */
36468     select : function(node, e, keepExisting){
36469         if(keepExisting !== true){
36470             this.clearSelections(true);
36471         }
36472         if(this.isSelected(node)){
36473             this.lastSelNode = node;
36474             return node;
36475         }
36476         this.selNodes.push(node);
36477         this.selMap[node.id] = node;
36478         this.lastSelNode = node;
36479         node.ui.onSelectedChange(true);
36480         this.fireEvent("selectionchange", this, this.selNodes);
36481         return node;
36482     },
36483     
36484     /**
36485      * Deselect a node.
36486      * @param {TreeNode} node The node to unselect
36487      */
36488     unselect : function(node){
36489         if(this.selMap[node.id]){
36490             node.ui.onSelectedChange(false);
36491             var sn = this.selNodes;
36492             var index = -1;
36493             if(sn.indexOf){
36494                 index = sn.indexOf(node);
36495             }else{
36496                 for(var i = 0, len = sn.length; i < len; i++){
36497                     if(sn[i] == node){
36498                         index = i;
36499                         break;
36500                     }
36501                 }
36502             }
36503             if(index != -1){
36504                 this.selNodes.splice(index, 1);
36505             }
36506             delete this.selMap[node.id];
36507             this.fireEvent("selectionchange", this, this.selNodes);
36508         }
36509     },
36510     
36511     /**
36512      * Clear all selections
36513      */
36514     clearSelections : function(suppressEvent){
36515         var sn = this.selNodes;
36516         if(sn.length > 0){
36517             for(var i = 0, len = sn.length; i < len; i++){
36518                 sn[i].ui.onSelectedChange(false);
36519             }
36520             this.selNodes = [];
36521             this.selMap = {};
36522             if(suppressEvent !== true){
36523                 this.fireEvent("selectionchange", this, this.selNodes);
36524             }
36525         }
36526     },
36527     
36528     /**
36529      * Returns true if the node is selected
36530      * @param {TreeNode} node The node to check
36531      * @return {Boolean}
36532      */
36533     isSelected : function(node){
36534         return this.selMap[node.id] ? true : false;  
36535     },
36536     
36537     /**
36538      * Returns an array of the selected nodes
36539      * @return {Array}
36540      */
36541     getSelectedNodes : function(){
36542         return this.selNodes;    
36543     },
36544
36545     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36546
36547     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36548
36549     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36550 });/*
36551  * Based on:
36552  * Ext JS Library 1.1.1
36553  * Copyright(c) 2006-2007, Ext JS, LLC.
36554  *
36555  * Originally Released Under LGPL - original licence link has changed is not relivant.
36556  *
36557  * Fork - LGPL
36558  * <script type="text/javascript">
36559  */
36560  
36561 /**
36562  * @class Roo.tree.TreeNode
36563  * @extends Roo.data.Node
36564  * @cfg {String} text The text for this node
36565  * @cfg {Boolean} expanded true to start the node expanded
36566  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36567  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36568  * @cfg {Boolean} disabled true to start the node disabled
36569  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36570  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36571  * @cfg {String} cls A css class to be added to the node
36572  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36573  * @cfg {String} href URL of the link used for the node (defaults to #)
36574  * @cfg {String} hrefTarget target frame for the link
36575  * @cfg {String} qtip An Ext QuickTip for the node
36576  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36577  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36578  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36579  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36580  * (defaults to undefined with no checkbox rendered)
36581  * @constructor
36582  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36583  */
36584 Roo.tree.TreeNode = function(attributes){
36585     attributes = attributes || {};
36586     if(typeof attributes == "string"){
36587         attributes = {text: attributes};
36588     }
36589     this.childrenRendered = false;
36590     this.rendered = false;
36591     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36592     this.expanded = attributes.expanded === true;
36593     this.isTarget = attributes.isTarget !== false;
36594     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36595     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36596
36597     /**
36598      * Read-only. The text for this node. To change it use setText().
36599      * @type String
36600      */
36601     this.text = attributes.text;
36602     /**
36603      * True if this node is disabled.
36604      * @type Boolean
36605      */
36606     this.disabled = attributes.disabled === true;
36607
36608     this.addEvents({
36609         /**
36610         * @event textchange
36611         * Fires when the text for this node is changed
36612         * @param {Node} this This node
36613         * @param {String} text The new text
36614         * @param {String} oldText The old text
36615         */
36616         "textchange" : true,
36617         /**
36618         * @event beforeexpand
36619         * Fires before this node is expanded, return false to cancel.
36620         * @param {Node} this This node
36621         * @param {Boolean} deep
36622         * @param {Boolean} anim
36623         */
36624         "beforeexpand" : true,
36625         /**
36626         * @event beforecollapse
36627         * Fires before this node is collapsed, return false to cancel.
36628         * @param {Node} this This node
36629         * @param {Boolean} deep
36630         * @param {Boolean} anim
36631         */
36632         "beforecollapse" : true,
36633         /**
36634         * @event expand
36635         * Fires when this node is expanded
36636         * @param {Node} this This node
36637         */
36638         "expand" : true,
36639         /**
36640         * @event disabledchange
36641         * Fires when the disabled status of this node changes
36642         * @param {Node} this This node
36643         * @param {Boolean} disabled
36644         */
36645         "disabledchange" : true,
36646         /**
36647         * @event collapse
36648         * Fires when this node is collapsed
36649         * @param {Node} this This node
36650         */
36651         "collapse" : true,
36652         /**
36653         * @event beforeclick
36654         * Fires before click processing. Return false to cancel the default action.
36655         * @param {Node} this This node
36656         * @param {Roo.EventObject} e The event object
36657         */
36658         "beforeclick":true,
36659         /**
36660         * @event checkchange
36661         * Fires when a node with a checkbox's checked property changes
36662         * @param {Node} this This node
36663         * @param {Boolean} checked
36664         */
36665         "checkchange":true,
36666         /**
36667         * @event click
36668         * Fires when this node is clicked
36669         * @param {Node} this This node
36670         * @param {Roo.EventObject} e The event object
36671         */
36672         "click":true,
36673         /**
36674         * @event dblclick
36675         * Fires when this node is double clicked
36676         * @param {Node} this This node
36677         * @param {Roo.EventObject} e The event object
36678         */
36679         "dblclick":true,
36680         /**
36681         * @event contextmenu
36682         * Fires when this node is right clicked
36683         * @param {Node} this This node
36684         * @param {Roo.EventObject} e The event object
36685         */
36686         "contextmenu":true,
36687         /**
36688         * @event beforechildrenrendered
36689         * Fires right before the child nodes for this node are rendered
36690         * @param {Node} this This node
36691         */
36692         "beforechildrenrendered":true
36693     });
36694
36695     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36696
36697     /**
36698      * Read-only. The UI for this node
36699      * @type TreeNodeUI
36700      */
36701     this.ui = new uiClass(this);
36702     
36703     // finally support items[]
36704     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36705         return;
36706     }
36707     
36708     
36709     Roo.each(this.attributes.items, function(c) {
36710         this.appendChild(Roo.factory(c,Roo.Tree));
36711     }, this);
36712     delete this.attributes.items;
36713     
36714     
36715     
36716 };
36717 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36718     preventHScroll: true,
36719     /**
36720      * Returns true if this node is expanded
36721      * @return {Boolean}
36722      */
36723     isExpanded : function(){
36724         return this.expanded;
36725     },
36726
36727     /**
36728      * Returns the UI object for this node
36729      * @return {TreeNodeUI}
36730      */
36731     getUI : function(){
36732         return this.ui;
36733     },
36734
36735     // private override
36736     setFirstChild : function(node){
36737         var of = this.firstChild;
36738         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36739         if(this.childrenRendered && of && node != of){
36740             of.renderIndent(true, true);
36741         }
36742         if(this.rendered){
36743             this.renderIndent(true, true);
36744         }
36745     },
36746
36747     // private override
36748     setLastChild : function(node){
36749         var ol = this.lastChild;
36750         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36751         if(this.childrenRendered && ol && node != ol){
36752             ol.renderIndent(true, true);
36753         }
36754         if(this.rendered){
36755             this.renderIndent(true, true);
36756         }
36757     },
36758
36759     // these methods are overridden to provide lazy rendering support
36760     // private override
36761     appendChild : function()
36762     {
36763         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36764         if(node && this.childrenRendered){
36765             node.render();
36766         }
36767         this.ui.updateExpandIcon();
36768         return node;
36769     },
36770
36771     // private override
36772     removeChild : function(node){
36773         this.ownerTree.getSelectionModel().unselect(node);
36774         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36775         // if it's been rendered remove dom node
36776         if(this.childrenRendered){
36777             node.ui.remove();
36778         }
36779         if(this.childNodes.length < 1){
36780             this.collapse(false, false);
36781         }else{
36782             this.ui.updateExpandIcon();
36783         }
36784         if(!this.firstChild) {
36785             this.childrenRendered = false;
36786         }
36787         return node;
36788     },
36789
36790     // private override
36791     insertBefore : function(node, refNode){
36792         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36793         if(newNode && refNode && this.childrenRendered){
36794             node.render();
36795         }
36796         this.ui.updateExpandIcon();
36797         return newNode;
36798     },
36799
36800     /**
36801      * Sets the text for this node
36802      * @param {String} text
36803      */
36804     setText : function(text){
36805         var oldText = this.text;
36806         this.text = text;
36807         this.attributes.text = text;
36808         if(this.rendered){ // event without subscribing
36809             this.ui.onTextChange(this, text, oldText);
36810         }
36811         this.fireEvent("textchange", this, text, oldText);
36812     },
36813
36814     /**
36815      * Triggers selection of this node
36816      */
36817     select : function(){
36818         this.getOwnerTree().getSelectionModel().select(this);
36819     },
36820
36821     /**
36822      * Triggers deselection of this node
36823      */
36824     unselect : function(){
36825         this.getOwnerTree().getSelectionModel().unselect(this);
36826     },
36827
36828     /**
36829      * Returns true if this node is selected
36830      * @return {Boolean}
36831      */
36832     isSelected : function(){
36833         return this.getOwnerTree().getSelectionModel().isSelected(this);
36834     },
36835
36836     /**
36837      * Expand this node.
36838      * @param {Boolean} deep (optional) True to expand all children as well
36839      * @param {Boolean} anim (optional) false to cancel the default animation
36840      * @param {Function} callback (optional) A callback to be called when
36841      * expanding this node completes (does not wait for deep expand to complete).
36842      * Called with 1 parameter, this node.
36843      */
36844     expand : function(deep, anim, callback){
36845         if(!this.expanded){
36846             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36847                 return;
36848             }
36849             if(!this.childrenRendered){
36850                 this.renderChildren();
36851             }
36852             this.expanded = true;
36853             
36854             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36855                 this.ui.animExpand(function(){
36856                     this.fireEvent("expand", this);
36857                     if(typeof callback == "function"){
36858                         callback(this);
36859                     }
36860                     if(deep === true){
36861                         this.expandChildNodes(true);
36862                     }
36863                 }.createDelegate(this));
36864                 return;
36865             }else{
36866                 this.ui.expand();
36867                 this.fireEvent("expand", this);
36868                 if(typeof callback == "function"){
36869                     callback(this);
36870                 }
36871             }
36872         }else{
36873            if(typeof callback == "function"){
36874                callback(this);
36875            }
36876         }
36877         if(deep === true){
36878             this.expandChildNodes(true);
36879         }
36880     },
36881
36882     isHiddenRoot : function(){
36883         return this.isRoot && !this.getOwnerTree().rootVisible;
36884     },
36885
36886     /**
36887      * Collapse this node.
36888      * @param {Boolean} deep (optional) True to collapse all children as well
36889      * @param {Boolean} anim (optional) false to cancel the default animation
36890      */
36891     collapse : function(deep, anim){
36892         if(this.expanded && !this.isHiddenRoot()){
36893             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36894                 return;
36895             }
36896             this.expanded = false;
36897             if((this.getOwnerTree().animate && anim !== false) || anim){
36898                 this.ui.animCollapse(function(){
36899                     this.fireEvent("collapse", this);
36900                     if(deep === true){
36901                         this.collapseChildNodes(true);
36902                     }
36903                 }.createDelegate(this));
36904                 return;
36905             }else{
36906                 this.ui.collapse();
36907                 this.fireEvent("collapse", this);
36908             }
36909         }
36910         if(deep === true){
36911             var cs = this.childNodes;
36912             for(var i = 0, len = cs.length; i < len; i++) {
36913                 cs[i].collapse(true, false);
36914             }
36915         }
36916     },
36917
36918     // private
36919     delayedExpand : function(delay){
36920         if(!this.expandProcId){
36921             this.expandProcId = this.expand.defer(delay, this);
36922         }
36923     },
36924
36925     // private
36926     cancelExpand : function(){
36927         if(this.expandProcId){
36928             clearTimeout(this.expandProcId);
36929         }
36930         this.expandProcId = false;
36931     },
36932
36933     /**
36934      * Toggles expanded/collapsed state of the node
36935      */
36936     toggle : function(){
36937         if(this.expanded){
36938             this.collapse();
36939         }else{
36940             this.expand();
36941         }
36942     },
36943
36944     /**
36945      * Ensures all parent nodes are expanded
36946      */
36947     ensureVisible : function(callback){
36948         var tree = this.getOwnerTree();
36949         tree.expandPath(this.parentNode.getPath(), false, function(){
36950             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36951             Roo.callback(callback);
36952         }.createDelegate(this));
36953     },
36954
36955     /**
36956      * Expand all child nodes
36957      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36958      */
36959     expandChildNodes : function(deep){
36960         var cs = this.childNodes;
36961         for(var i = 0, len = cs.length; i < len; i++) {
36962                 cs[i].expand(deep);
36963         }
36964     },
36965
36966     /**
36967      * Collapse all child nodes
36968      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36969      */
36970     collapseChildNodes : function(deep){
36971         var cs = this.childNodes;
36972         for(var i = 0, len = cs.length; i < len; i++) {
36973                 cs[i].collapse(deep);
36974         }
36975     },
36976
36977     /**
36978      * Disables this node
36979      */
36980     disable : function(){
36981         this.disabled = true;
36982         this.unselect();
36983         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36984             this.ui.onDisableChange(this, true);
36985         }
36986         this.fireEvent("disabledchange", this, true);
36987     },
36988
36989     /**
36990      * Enables this node
36991      */
36992     enable : function(){
36993         this.disabled = false;
36994         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36995             this.ui.onDisableChange(this, false);
36996         }
36997         this.fireEvent("disabledchange", this, false);
36998     },
36999
37000     // private
37001     renderChildren : function(suppressEvent){
37002         if(suppressEvent !== false){
37003             this.fireEvent("beforechildrenrendered", this);
37004         }
37005         var cs = this.childNodes;
37006         for(var i = 0, len = cs.length; i < len; i++){
37007             cs[i].render(true);
37008         }
37009         this.childrenRendered = true;
37010     },
37011
37012     // private
37013     sort : function(fn, scope){
37014         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
37015         if(this.childrenRendered){
37016             var cs = this.childNodes;
37017             for(var i = 0, len = cs.length; i < len; i++){
37018                 cs[i].render(true);
37019             }
37020         }
37021     },
37022
37023     // private
37024     render : function(bulkRender){
37025         this.ui.render(bulkRender);
37026         if(!this.rendered){
37027             this.rendered = true;
37028             if(this.expanded){
37029                 this.expanded = false;
37030                 this.expand(false, false);
37031             }
37032         }
37033     },
37034
37035     // private
37036     renderIndent : function(deep, refresh){
37037         if(refresh){
37038             this.ui.childIndent = null;
37039         }
37040         this.ui.renderIndent();
37041         if(deep === true && this.childrenRendered){
37042             var cs = this.childNodes;
37043             for(var i = 0, len = cs.length; i < len; i++){
37044                 cs[i].renderIndent(true, refresh);
37045             }
37046         }
37047     }
37048 });/*
37049  * Based on:
37050  * Ext JS Library 1.1.1
37051  * Copyright(c) 2006-2007, Ext JS, LLC.
37052  *
37053  * Originally Released Under LGPL - original licence link has changed is not relivant.
37054  *
37055  * Fork - LGPL
37056  * <script type="text/javascript">
37057  */
37058  
37059 /**
37060  * @class Roo.tree.AsyncTreeNode
37061  * @extends Roo.tree.TreeNode
37062  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
37063  * @constructor
37064  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
37065  */
37066  Roo.tree.AsyncTreeNode = function(config){
37067     this.loaded = false;
37068     this.loading = false;
37069     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
37070     /**
37071     * @event beforeload
37072     * Fires before this node is loaded, return false to cancel
37073     * @param {Node} this This node
37074     */
37075     this.addEvents({'beforeload':true, 'load': true});
37076     /**
37077     * @event load
37078     * Fires when this node is loaded
37079     * @param {Node} this This node
37080     */
37081     /**
37082      * The loader used by this node (defaults to using the tree's defined loader)
37083      * @type TreeLoader
37084      * @property loader
37085      */
37086 };
37087 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
37088     expand : function(deep, anim, callback){
37089         if(this.loading){ // if an async load is already running, waiting til it's done
37090             var timer;
37091             var f = function(){
37092                 if(!this.loading){ // done loading
37093                     clearInterval(timer);
37094                     this.expand(deep, anim, callback);
37095                 }
37096             }.createDelegate(this);
37097             timer = setInterval(f, 200);
37098             return;
37099         }
37100         if(!this.loaded){
37101             if(this.fireEvent("beforeload", this) === false){
37102                 return;
37103             }
37104             this.loading = true;
37105             this.ui.beforeLoad(this);
37106             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
37107             if(loader){
37108                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
37109                 return;
37110             }
37111         }
37112         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
37113     },
37114     
37115     /**
37116      * Returns true if this node is currently loading
37117      * @return {Boolean}
37118      */
37119     isLoading : function(){
37120         return this.loading;  
37121     },
37122     
37123     loadComplete : function(deep, anim, callback){
37124         this.loading = false;
37125         this.loaded = true;
37126         this.ui.afterLoad(this);
37127         this.fireEvent("load", this);
37128         this.expand(deep, anim, callback);
37129     },
37130     
37131     /**
37132      * Returns true if this node has been loaded
37133      * @return {Boolean}
37134      */
37135     isLoaded : function(){
37136         return this.loaded;
37137     },
37138     
37139     hasChildNodes : function(){
37140         if(!this.isLeaf() && !this.loaded){
37141             return true;
37142         }else{
37143             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
37144         }
37145     },
37146
37147     /**
37148      * Trigger a reload for this node
37149      * @param {Function} callback
37150      */
37151     reload : function(callback){
37152         this.collapse(false, false);
37153         while(this.firstChild){
37154             this.removeChild(this.firstChild);
37155         }
37156         this.childrenRendered = false;
37157         this.loaded = false;
37158         if(this.isHiddenRoot()){
37159             this.expanded = false;
37160         }
37161         this.expand(false, false, callback);
37162     }
37163 });/*
37164  * Based on:
37165  * Ext JS Library 1.1.1
37166  * Copyright(c) 2006-2007, Ext JS, LLC.
37167  *
37168  * Originally Released Under LGPL - original licence link has changed is not relivant.
37169  *
37170  * Fork - LGPL
37171  * <script type="text/javascript">
37172  */
37173  
37174 /**
37175  * @class Roo.tree.TreeNodeUI
37176  * @constructor
37177  * @param {Object} node The node to render
37178  * The TreeNode UI implementation is separate from the
37179  * tree implementation. Unless you are customizing the tree UI,
37180  * you should never have to use this directly.
37181  */
37182 Roo.tree.TreeNodeUI = function(node){
37183     this.node = node;
37184     this.rendered = false;
37185     this.animating = false;
37186     this.emptyIcon = Roo.BLANK_IMAGE_URL;
37187 };
37188
37189 Roo.tree.TreeNodeUI.prototype = {
37190     removeChild : function(node){
37191         if(this.rendered){
37192             this.ctNode.removeChild(node.ui.getEl());
37193         }
37194     },
37195
37196     beforeLoad : function(){
37197          this.addClass("x-tree-node-loading");
37198     },
37199
37200     afterLoad : function(){
37201          this.removeClass("x-tree-node-loading");
37202     },
37203
37204     onTextChange : function(node, text, oldText){
37205         if(this.rendered){
37206             this.textNode.innerHTML = text;
37207         }
37208     },
37209
37210     onDisableChange : function(node, state){
37211         this.disabled = state;
37212         if(state){
37213             this.addClass("x-tree-node-disabled");
37214         }else{
37215             this.removeClass("x-tree-node-disabled");
37216         }
37217     },
37218
37219     onSelectedChange : function(state){
37220         if(state){
37221             this.focus();
37222             this.addClass("x-tree-selected");
37223         }else{
37224             //this.blur();
37225             this.removeClass("x-tree-selected");
37226         }
37227     },
37228
37229     onMove : function(tree, node, oldParent, newParent, index, refNode){
37230         this.childIndent = null;
37231         if(this.rendered){
37232             var targetNode = newParent.ui.getContainer();
37233             if(!targetNode){//target not rendered
37234                 this.holder = document.createElement("div");
37235                 this.holder.appendChild(this.wrap);
37236                 return;
37237             }
37238             var insertBefore = refNode ? refNode.ui.getEl() : null;
37239             if(insertBefore){
37240                 targetNode.insertBefore(this.wrap, insertBefore);
37241             }else{
37242                 targetNode.appendChild(this.wrap);
37243             }
37244             this.node.renderIndent(true);
37245         }
37246     },
37247
37248     addClass : function(cls){
37249         if(this.elNode){
37250             Roo.fly(this.elNode).addClass(cls);
37251         }
37252     },
37253
37254     removeClass : function(cls){
37255         if(this.elNode){
37256             Roo.fly(this.elNode).removeClass(cls);
37257         }
37258     },
37259
37260     remove : function(){
37261         if(this.rendered){
37262             this.holder = document.createElement("div");
37263             this.holder.appendChild(this.wrap);
37264         }
37265     },
37266
37267     fireEvent : function(){
37268         return this.node.fireEvent.apply(this.node, arguments);
37269     },
37270
37271     initEvents : function(){
37272         this.node.on("move", this.onMove, this);
37273         var E = Roo.EventManager;
37274         var a = this.anchor;
37275
37276         var el = Roo.fly(a, '_treeui');
37277
37278         if(Roo.isOpera){ // opera render bug ignores the CSS
37279             el.setStyle("text-decoration", "none");
37280         }
37281
37282         el.on("click", this.onClick, this);
37283         el.on("dblclick", this.onDblClick, this);
37284
37285         if(this.checkbox){
37286             Roo.EventManager.on(this.checkbox,
37287                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37288         }
37289
37290         el.on("contextmenu", this.onContextMenu, this);
37291
37292         var icon = Roo.fly(this.iconNode);
37293         icon.on("click", this.onClick, this);
37294         icon.on("dblclick", this.onDblClick, this);
37295         icon.on("contextmenu", this.onContextMenu, this);
37296         E.on(this.ecNode, "click", this.ecClick, this, true);
37297
37298         if(this.node.disabled){
37299             this.addClass("x-tree-node-disabled");
37300         }
37301         if(this.node.hidden){
37302             this.addClass("x-tree-node-disabled");
37303         }
37304         var ot = this.node.getOwnerTree();
37305         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37306         if(dd && (!this.node.isRoot || ot.rootVisible)){
37307             Roo.dd.Registry.register(this.elNode, {
37308                 node: this.node,
37309                 handles: this.getDDHandles(),
37310                 isHandle: false
37311             });
37312         }
37313     },
37314
37315     getDDHandles : function(){
37316         return [this.iconNode, this.textNode];
37317     },
37318
37319     hide : function(){
37320         if(this.rendered){
37321             this.wrap.style.display = "none";
37322         }
37323     },
37324
37325     show : function(){
37326         if(this.rendered){
37327             this.wrap.style.display = "";
37328         }
37329     },
37330
37331     onContextMenu : function(e){
37332         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37333             e.preventDefault();
37334             this.focus();
37335             this.fireEvent("contextmenu", this.node, e);
37336         }
37337     },
37338
37339     onClick : function(e){
37340         if(this.dropping){
37341             e.stopEvent();
37342             return;
37343         }
37344         if(this.fireEvent("beforeclick", this.node, e) !== false){
37345             if(!this.disabled && this.node.attributes.href){
37346                 this.fireEvent("click", this.node, e);
37347                 return;
37348             }
37349             e.preventDefault();
37350             if(this.disabled){
37351                 return;
37352             }
37353
37354             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37355                 this.node.toggle();
37356             }
37357
37358             this.fireEvent("click", this.node, e);
37359         }else{
37360             e.stopEvent();
37361         }
37362     },
37363
37364     onDblClick : function(e){
37365         e.preventDefault();
37366         if(this.disabled){
37367             return;
37368         }
37369         if(this.checkbox){
37370             this.toggleCheck();
37371         }
37372         if(!this.animating && this.node.hasChildNodes()){
37373             this.node.toggle();
37374         }
37375         this.fireEvent("dblclick", this.node, e);
37376     },
37377
37378     onCheckChange : function(){
37379         var checked = this.checkbox.checked;
37380         this.node.attributes.checked = checked;
37381         this.fireEvent('checkchange', this.node, checked);
37382     },
37383
37384     ecClick : function(e){
37385         if(!this.animating && this.node.hasChildNodes()){
37386             this.node.toggle();
37387         }
37388     },
37389
37390     startDrop : function(){
37391         this.dropping = true;
37392     },
37393
37394     // delayed drop so the click event doesn't get fired on a drop
37395     endDrop : function(){
37396        setTimeout(function(){
37397            this.dropping = false;
37398        }.createDelegate(this), 50);
37399     },
37400
37401     expand : function(){
37402         this.updateExpandIcon();
37403         this.ctNode.style.display = "";
37404     },
37405
37406     focus : function(){
37407         if(!this.node.preventHScroll){
37408             try{this.anchor.focus();
37409             }catch(e){}
37410         }else if(!Roo.isIE){
37411             try{
37412                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37413                 var l = noscroll.scrollLeft;
37414                 this.anchor.focus();
37415                 noscroll.scrollLeft = l;
37416             }catch(e){}
37417         }
37418     },
37419
37420     toggleCheck : function(value){
37421         var cb = this.checkbox;
37422         if(cb){
37423             cb.checked = (value === undefined ? !cb.checked : value);
37424         }
37425     },
37426
37427     blur : function(){
37428         try{
37429             this.anchor.blur();
37430         }catch(e){}
37431     },
37432
37433     animExpand : function(callback){
37434         var ct = Roo.get(this.ctNode);
37435         ct.stopFx();
37436         if(!this.node.hasChildNodes()){
37437             this.updateExpandIcon();
37438             this.ctNode.style.display = "";
37439             Roo.callback(callback);
37440             return;
37441         }
37442         this.animating = true;
37443         this.updateExpandIcon();
37444
37445         ct.slideIn('t', {
37446            callback : function(){
37447                this.animating = false;
37448                Roo.callback(callback);
37449             },
37450             scope: this,
37451             duration: this.node.ownerTree.duration || .25
37452         });
37453     },
37454
37455     highlight : function(){
37456         var tree = this.node.getOwnerTree();
37457         Roo.fly(this.wrap).highlight(
37458             tree.hlColor || "C3DAF9",
37459             {endColor: tree.hlBaseColor}
37460         );
37461     },
37462
37463     collapse : function(){
37464         this.updateExpandIcon();
37465         this.ctNode.style.display = "none";
37466     },
37467
37468     animCollapse : function(callback){
37469         var ct = Roo.get(this.ctNode);
37470         ct.enableDisplayMode('block');
37471         ct.stopFx();
37472
37473         this.animating = true;
37474         this.updateExpandIcon();
37475
37476         ct.slideOut('t', {
37477             callback : function(){
37478                this.animating = false;
37479                Roo.callback(callback);
37480             },
37481             scope: this,
37482             duration: this.node.ownerTree.duration || .25
37483         });
37484     },
37485
37486     getContainer : function(){
37487         return this.ctNode;
37488     },
37489
37490     getEl : function(){
37491         return this.wrap;
37492     },
37493
37494     appendDDGhost : function(ghostNode){
37495         ghostNode.appendChild(this.elNode.cloneNode(true));
37496     },
37497
37498     getDDRepairXY : function(){
37499         return Roo.lib.Dom.getXY(this.iconNode);
37500     },
37501
37502     onRender : function(){
37503         this.render();
37504     },
37505
37506     render : function(bulkRender){
37507         var n = this.node, a = n.attributes;
37508         var targetNode = n.parentNode ?
37509               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37510
37511         if(!this.rendered){
37512             this.rendered = true;
37513
37514             this.renderElements(n, a, targetNode, bulkRender);
37515
37516             if(a.qtip){
37517                if(this.textNode.setAttributeNS){
37518                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37519                    if(a.qtipTitle){
37520                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37521                    }
37522                }else{
37523                    this.textNode.setAttribute("ext:qtip", a.qtip);
37524                    if(a.qtipTitle){
37525                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37526                    }
37527                }
37528             }else if(a.qtipCfg){
37529                 a.qtipCfg.target = Roo.id(this.textNode);
37530                 Roo.QuickTips.register(a.qtipCfg);
37531             }
37532             this.initEvents();
37533             if(!this.node.expanded){
37534                 this.updateExpandIcon();
37535             }
37536         }else{
37537             if(bulkRender === true) {
37538                 targetNode.appendChild(this.wrap);
37539             }
37540         }
37541     },
37542
37543     renderElements : function(n, a, targetNode, bulkRender)
37544     {
37545         // add some indent caching, this helps performance when rendering a large tree
37546         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37547         var t = n.getOwnerTree();
37548         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37549         if (typeof(n.attributes.html) != 'undefined') {
37550             txt = n.attributes.html;
37551         }
37552         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37553         var cb = typeof a.checked == 'boolean';
37554         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37555         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37556             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37557             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37558             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37559             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37560             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37561              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37562                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37563             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37564             "</li>"];
37565
37566         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37567             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37568                                 n.nextSibling.ui.getEl(), buf.join(""));
37569         }else{
37570             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37571         }
37572
37573         this.elNode = this.wrap.childNodes[0];
37574         this.ctNode = this.wrap.childNodes[1];
37575         var cs = this.elNode.childNodes;
37576         this.indentNode = cs[0];
37577         this.ecNode = cs[1];
37578         this.iconNode = cs[2];
37579         var index = 3;
37580         if(cb){
37581             this.checkbox = cs[3];
37582             index++;
37583         }
37584         this.anchor = cs[index];
37585         this.textNode = cs[index].firstChild;
37586     },
37587
37588     getAnchor : function(){
37589         return this.anchor;
37590     },
37591
37592     getTextEl : function(){
37593         return this.textNode;
37594     },
37595
37596     getIconEl : function(){
37597         return this.iconNode;
37598     },
37599
37600     isChecked : function(){
37601         return this.checkbox ? this.checkbox.checked : false;
37602     },
37603
37604     updateExpandIcon : function(){
37605         if(this.rendered){
37606             var n = this.node, c1, c2;
37607             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37608             var hasChild = n.hasChildNodes();
37609             if(hasChild){
37610                 if(n.expanded){
37611                     cls += "-minus";
37612                     c1 = "x-tree-node-collapsed";
37613                     c2 = "x-tree-node-expanded";
37614                 }else{
37615                     cls += "-plus";
37616                     c1 = "x-tree-node-expanded";
37617                     c2 = "x-tree-node-collapsed";
37618                 }
37619                 if(this.wasLeaf){
37620                     this.removeClass("x-tree-node-leaf");
37621                     this.wasLeaf = false;
37622                 }
37623                 if(this.c1 != c1 || this.c2 != c2){
37624                     Roo.fly(this.elNode).replaceClass(c1, c2);
37625                     this.c1 = c1; this.c2 = c2;
37626                 }
37627             }else{
37628                 // this changes non-leafs into leafs if they have no children.
37629                 // it's not very rational behaviour..
37630                 
37631                 if(!this.wasLeaf && this.node.leaf){
37632                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37633                     delete this.c1;
37634                     delete this.c2;
37635                     this.wasLeaf = true;
37636                 }
37637             }
37638             var ecc = "x-tree-ec-icon "+cls;
37639             if(this.ecc != ecc){
37640                 this.ecNode.className = ecc;
37641                 this.ecc = ecc;
37642             }
37643         }
37644     },
37645
37646     getChildIndent : function(){
37647         if(!this.childIndent){
37648             var buf = [];
37649             var p = this.node;
37650             while(p){
37651                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37652                     if(!p.isLast()) {
37653                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37654                     } else {
37655                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37656                     }
37657                 }
37658                 p = p.parentNode;
37659             }
37660             this.childIndent = buf.join("");
37661         }
37662         return this.childIndent;
37663     },
37664
37665     renderIndent : function(){
37666         if(this.rendered){
37667             var indent = "";
37668             var p = this.node.parentNode;
37669             if(p){
37670                 indent = p.ui.getChildIndent();
37671             }
37672             if(this.indentMarkup != indent){ // don't rerender if not required
37673                 this.indentNode.innerHTML = indent;
37674                 this.indentMarkup = indent;
37675             }
37676             this.updateExpandIcon();
37677         }
37678     }
37679 };
37680
37681 Roo.tree.RootTreeNodeUI = function(){
37682     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37683 };
37684 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37685     render : function(){
37686         if(!this.rendered){
37687             var targetNode = this.node.ownerTree.innerCt.dom;
37688             this.node.expanded = true;
37689             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37690             this.wrap = this.ctNode = targetNode.firstChild;
37691         }
37692     },
37693     collapse : function(){
37694     },
37695     expand : function(){
37696     }
37697 });/*
37698  * Based on:
37699  * Ext JS Library 1.1.1
37700  * Copyright(c) 2006-2007, Ext JS, LLC.
37701  *
37702  * Originally Released Under LGPL - original licence link has changed is not relivant.
37703  *
37704  * Fork - LGPL
37705  * <script type="text/javascript">
37706  */
37707 /**
37708  * @class Roo.tree.TreeLoader
37709  * @extends Roo.util.Observable
37710  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37711  * nodes from a specified URL. The response must be a javascript Array definition
37712  * who's elements are node definition objects. eg:
37713  * <pre><code>
37714 {  success : true,
37715    data :      [
37716    
37717     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37718     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37719     ]
37720 }
37721
37722
37723 </code></pre>
37724  * <br><br>
37725  * The old style respose with just an array is still supported, but not recommended.
37726  * <br><br>
37727  *
37728  * A server request is sent, and child nodes are loaded only when a node is expanded.
37729  * The loading node's id is passed to the server under the parameter name "node" to
37730  * enable the server to produce the correct child nodes.
37731  * <br><br>
37732  * To pass extra parameters, an event handler may be attached to the "beforeload"
37733  * event, and the parameters specified in the TreeLoader's baseParams property:
37734  * <pre><code>
37735     myTreeLoader.on("beforeload", function(treeLoader, node) {
37736         this.baseParams.category = node.attributes.category;
37737     }, this);
37738     
37739 </code></pre>
37740  *
37741  * This would pass an HTTP parameter called "category" to the server containing
37742  * the value of the Node's "category" attribute.
37743  * @constructor
37744  * Creates a new Treeloader.
37745  * @param {Object} config A config object containing config properties.
37746  */
37747 Roo.tree.TreeLoader = function(config){
37748     this.baseParams = {};
37749     this.requestMethod = "POST";
37750     Roo.apply(this, config);
37751
37752     this.addEvents({
37753     
37754         /**
37755          * @event beforeload
37756          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37757          * @param {Object} This TreeLoader object.
37758          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37759          * @param {Object} callback The callback function specified in the {@link #load} call.
37760          */
37761         beforeload : true,
37762         /**
37763          * @event load
37764          * Fires when the node has been successfuly loaded.
37765          * @param {Object} This TreeLoader object.
37766          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37767          * @param {Object} response The response object containing the data from the server.
37768          */
37769         load : true,
37770         /**
37771          * @event loadexception
37772          * Fires if the network request failed.
37773          * @param {Object} This TreeLoader object.
37774          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37775          * @param {Object} response The response object containing the data from the server.
37776          */
37777         loadexception : true,
37778         /**
37779          * @event create
37780          * Fires before a node is created, enabling you to return custom Node types 
37781          * @param {Object} This TreeLoader object.
37782          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37783          */
37784         create : true
37785     });
37786
37787     Roo.tree.TreeLoader.superclass.constructor.call(this);
37788 };
37789
37790 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37791     /**
37792     * @cfg {String} dataUrl The URL from which to request a Json string which
37793     * specifies an array of node definition object representing the child nodes
37794     * to be loaded.
37795     */
37796     /**
37797     * @cfg {String} requestMethod either GET or POST
37798     * defaults to POST (due to BC)
37799     * to be loaded.
37800     */
37801     /**
37802     * @cfg {Object} baseParams (optional) An object containing properties which
37803     * specify HTTP parameters to be passed to each request for child nodes.
37804     */
37805     /**
37806     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37807     * created by this loader. If the attributes sent by the server have an attribute in this object,
37808     * they take priority.
37809     */
37810     /**
37811     * @cfg {Object} uiProviders (optional) An object containing properties which
37812     * 
37813     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37814     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37815     * <i>uiProvider</i> attribute of a returned child node is a string rather
37816     * than a reference to a TreeNodeUI implementation, this that string value
37817     * is used as a property name in the uiProviders object. You can define the provider named
37818     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37819     */
37820     uiProviders : {},
37821
37822     /**
37823     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37824     * child nodes before loading.
37825     */
37826     clearOnLoad : true,
37827
37828     /**
37829     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37830     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37831     * Grid query { data : [ .....] }
37832     */
37833     
37834     root : false,
37835      /**
37836     * @cfg {String} queryParam (optional) 
37837     * Name of the query as it will be passed on the querystring (defaults to 'node')
37838     * eg. the request will be ?node=[id]
37839     */
37840     
37841     
37842     queryParam: false,
37843     
37844     /**
37845      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37846      * This is called automatically when a node is expanded, but may be used to reload
37847      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37848      * @param {Roo.tree.TreeNode} node
37849      * @param {Function} callback
37850      */
37851     load : function(node, callback){
37852         if(this.clearOnLoad){
37853             while(node.firstChild){
37854                 node.removeChild(node.firstChild);
37855             }
37856         }
37857         if(node.attributes.children){ // preloaded json children
37858             var cs = node.attributes.children;
37859             for(var i = 0, len = cs.length; i < len; i++){
37860                 node.appendChild(this.createNode(cs[i]));
37861             }
37862             if(typeof callback == "function"){
37863                 callback();
37864             }
37865         }else if(this.dataUrl){
37866             this.requestData(node, callback);
37867         }
37868     },
37869
37870     getParams: function(node){
37871         var buf = [], bp = this.baseParams;
37872         for(var key in bp){
37873             if(typeof bp[key] != "function"){
37874                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37875             }
37876         }
37877         var n = this.queryParam === false ? 'node' : this.queryParam;
37878         buf.push(n + "=", encodeURIComponent(node.id));
37879         return buf.join("");
37880     },
37881
37882     requestData : function(node, callback){
37883         if(this.fireEvent("beforeload", this, node, callback) !== false){
37884             this.transId = Roo.Ajax.request({
37885                 method:this.requestMethod,
37886                 url: this.dataUrl||this.url,
37887                 success: this.handleResponse,
37888                 failure: this.handleFailure,
37889                 scope: this,
37890                 argument: {callback: callback, node: node},
37891                 params: this.getParams(node)
37892             });
37893         }else{
37894             // if the load is cancelled, make sure we notify
37895             // the node that we are done
37896             if(typeof callback == "function"){
37897                 callback();
37898             }
37899         }
37900     },
37901
37902     isLoading : function(){
37903         return this.transId ? true : false;
37904     },
37905
37906     abort : function(){
37907         if(this.isLoading()){
37908             Roo.Ajax.abort(this.transId);
37909         }
37910     },
37911
37912     // private
37913     createNode : function(attr)
37914     {
37915         // apply baseAttrs, nice idea Corey!
37916         if(this.baseAttrs){
37917             Roo.applyIf(attr, this.baseAttrs);
37918         }
37919         if(this.applyLoader !== false){
37920             attr.loader = this;
37921         }
37922         // uiProvider = depreciated..
37923         
37924         if(typeof(attr.uiProvider) == 'string'){
37925            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37926                 /**  eval:var:attr */ eval(attr.uiProvider);
37927         }
37928         if(typeof(this.uiProviders['default']) != 'undefined') {
37929             attr.uiProvider = this.uiProviders['default'];
37930         }
37931         
37932         this.fireEvent('create', this, attr);
37933         
37934         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37935         return(attr.leaf ?
37936                         new Roo.tree.TreeNode(attr) :
37937                         new Roo.tree.AsyncTreeNode(attr));
37938     },
37939
37940     processResponse : function(response, node, callback)
37941     {
37942         var json = response.responseText;
37943         try {
37944             
37945             var o = Roo.decode(json);
37946             
37947             if (this.root === false && typeof(o.success) != undefined) {
37948                 this.root = 'data'; // the default behaviour for list like data..
37949                 }
37950                 
37951             if (this.root !== false &&  !o.success) {
37952                 // it's a failure condition.
37953                 var a = response.argument;
37954                 this.fireEvent("loadexception", this, a.node, response);
37955                 Roo.log("Load failed - should have a handler really");
37956                 return;
37957             }
37958             
37959             
37960             
37961             if (this.root !== false) {
37962                  o = o[this.root];
37963             }
37964             
37965             for(var i = 0, len = o.length; i < len; i++){
37966                 var n = this.createNode(o[i]);
37967                 if(n){
37968                     node.appendChild(n);
37969                 }
37970             }
37971             if(typeof callback == "function"){
37972                 callback(this, node);
37973             }
37974         }catch(e){
37975             this.handleFailure(response);
37976         }
37977     },
37978
37979     handleResponse : function(response){
37980         this.transId = false;
37981         var a = response.argument;
37982         this.processResponse(response, a.node, a.callback);
37983         this.fireEvent("load", this, a.node, response);
37984     },
37985
37986     handleFailure : function(response)
37987     {
37988         // should handle failure better..
37989         this.transId = false;
37990         var a = response.argument;
37991         this.fireEvent("loadexception", this, a.node, response);
37992         if(typeof a.callback == "function"){
37993             a.callback(this, a.node);
37994         }
37995     }
37996 });/*
37997  * Based on:
37998  * Ext JS Library 1.1.1
37999  * Copyright(c) 2006-2007, Ext JS, LLC.
38000  *
38001  * Originally Released Under LGPL - original licence link has changed is not relivant.
38002  *
38003  * Fork - LGPL
38004  * <script type="text/javascript">
38005  */
38006
38007 /**
38008 * @class Roo.tree.TreeFilter
38009 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
38010 * @param {TreePanel} tree
38011 * @param {Object} config (optional)
38012  */
38013 Roo.tree.TreeFilter = function(tree, config){
38014     this.tree = tree;
38015     this.filtered = {};
38016     Roo.apply(this, config);
38017 };
38018
38019 Roo.tree.TreeFilter.prototype = {
38020     clearBlank:false,
38021     reverse:false,
38022     autoClear:false,
38023     remove:false,
38024
38025      /**
38026      * Filter the data by a specific attribute.
38027      * @param {String/RegExp} value Either string that the attribute value
38028      * should start with or a RegExp to test against the attribute
38029      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
38030      * @param {TreeNode} startNode (optional) The node to start the filter at.
38031      */
38032     filter : function(value, attr, startNode){
38033         attr = attr || "text";
38034         var f;
38035         if(typeof value == "string"){
38036             var vlen = value.length;
38037             // auto clear empty filter
38038             if(vlen == 0 && this.clearBlank){
38039                 this.clear();
38040                 return;
38041             }
38042             value = value.toLowerCase();
38043             f = function(n){
38044                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
38045             };
38046         }else if(value.exec){ // regex?
38047             f = function(n){
38048                 return value.test(n.attributes[attr]);
38049             };
38050         }else{
38051             throw 'Illegal filter type, must be string or regex';
38052         }
38053         this.filterBy(f, null, startNode);
38054         },
38055
38056     /**
38057      * Filter by a function. The passed function will be called with each
38058      * node in the tree (or from the startNode). If the function returns true, the node is kept
38059      * otherwise it is filtered. If a node is filtered, its children are also filtered.
38060      * @param {Function} fn The filter function
38061      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
38062      */
38063     filterBy : function(fn, scope, startNode){
38064         startNode = startNode || this.tree.root;
38065         if(this.autoClear){
38066             this.clear();
38067         }
38068         var af = this.filtered, rv = this.reverse;
38069         var f = function(n){
38070             if(n == startNode){
38071                 return true;
38072             }
38073             if(af[n.id]){
38074                 return false;
38075             }
38076             var m = fn.call(scope || n, n);
38077             if(!m || rv){
38078                 af[n.id] = n;
38079                 n.ui.hide();
38080                 return false;
38081             }
38082             return true;
38083         };
38084         startNode.cascade(f);
38085         if(this.remove){
38086            for(var id in af){
38087                if(typeof id != "function"){
38088                    var n = af[id];
38089                    if(n && n.parentNode){
38090                        n.parentNode.removeChild(n);
38091                    }
38092                }
38093            }
38094         }
38095     },
38096
38097     /**
38098      * Clears the current filter. Note: with the "remove" option
38099      * set a filter cannot be cleared.
38100      */
38101     clear : function(){
38102         var t = this.tree;
38103         var af = this.filtered;
38104         for(var id in af){
38105             if(typeof id != "function"){
38106                 var n = af[id];
38107                 if(n){
38108                     n.ui.show();
38109                 }
38110             }
38111         }
38112         this.filtered = {};
38113     }
38114 };
38115 /*
38116  * Based on:
38117  * Ext JS Library 1.1.1
38118  * Copyright(c) 2006-2007, Ext JS, LLC.
38119  *
38120  * Originally Released Under LGPL - original licence link has changed is not relivant.
38121  *
38122  * Fork - LGPL
38123  * <script type="text/javascript">
38124  */
38125  
38126
38127 /**
38128  * @class Roo.tree.TreeSorter
38129  * Provides sorting of nodes in a TreePanel
38130  * 
38131  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
38132  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
38133  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
38134  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
38135  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
38136  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
38137  * @constructor
38138  * @param {TreePanel} tree
38139  * @param {Object} config
38140  */
38141 Roo.tree.TreeSorter = function(tree, config){
38142     Roo.apply(this, config);
38143     tree.on("beforechildrenrendered", this.doSort, this);
38144     tree.on("append", this.updateSort, this);
38145     tree.on("insert", this.updateSort, this);
38146     
38147     var dsc = this.dir && this.dir.toLowerCase() == "desc";
38148     var p = this.property || "text";
38149     var sortType = this.sortType;
38150     var fs = this.folderSort;
38151     var cs = this.caseSensitive === true;
38152     var leafAttr = this.leafAttr || 'leaf';
38153
38154     this.sortFn = function(n1, n2){
38155         if(fs){
38156             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
38157                 return 1;
38158             }
38159             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
38160                 return -1;
38161             }
38162         }
38163         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
38164         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
38165         if(v1 < v2){
38166                         return dsc ? +1 : -1;
38167                 }else if(v1 > v2){
38168                         return dsc ? -1 : +1;
38169         }else{
38170                 return 0;
38171         }
38172     };
38173 };
38174
38175 Roo.tree.TreeSorter.prototype = {
38176     doSort : function(node){
38177         node.sort(this.sortFn);
38178     },
38179     
38180     compareNodes : function(n1, n2){
38181         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
38182     },
38183     
38184     updateSort : function(tree, node){
38185         if(node.childrenRendered){
38186             this.doSort.defer(1, this, [node]);
38187         }
38188     }
38189 };/*
38190  * Based on:
38191  * Ext JS Library 1.1.1
38192  * Copyright(c) 2006-2007, Ext JS, LLC.
38193  *
38194  * Originally Released Under LGPL - original licence link has changed is not relivant.
38195  *
38196  * Fork - LGPL
38197  * <script type="text/javascript">
38198  */
38199
38200 if(Roo.dd.DropZone){
38201     
38202 Roo.tree.TreeDropZone = function(tree, config){
38203     this.allowParentInsert = false;
38204     this.allowContainerDrop = false;
38205     this.appendOnly = false;
38206     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38207     this.tree = tree;
38208     this.lastInsertClass = "x-tree-no-status";
38209     this.dragOverData = {};
38210 };
38211
38212 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38213     ddGroup : "TreeDD",
38214     scroll:  true,
38215     
38216     expandDelay : 1000,
38217     
38218     expandNode : function(node){
38219         if(node.hasChildNodes() && !node.isExpanded()){
38220             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38221         }
38222     },
38223     
38224     queueExpand : function(node){
38225         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38226     },
38227     
38228     cancelExpand : function(){
38229         if(this.expandProcId){
38230             clearTimeout(this.expandProcId);
38231             this.expandProcId = false;
38232         }
38233     },
38234     
38235     isValidDropPoint : function(n, pt, dd, e, data){
38236         if(!n || !data){ return false; }
38237         var targetNode = n.node;
38238         var dropNode = data.node;
38239         // default drop rules
38240         if(!(targetNode && targetNode.isTarget && pt)){
38241             return false;
38242         }
38243         if(pt == "append" && targetNode.allowChildren === false){
38244             return false;
38245         }
38246         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38247             return false;
38248         }
38249         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38250             return false;
38251         }
38252         // reuse the object
38253         var overEvent = this.dragOverData;
38254         overEvent.tree = this.tree;
38255         overEvent.target = targetNode;
38256         overEvent.data = data;
38257         overEvent.point = pt;
38258         overEvent.source = dd;
38259         overEvent.rawEvent = e;
38260         overEvent.dropNode = dropNode;
38261         overEvent.cancel = false;  
38262         var result = this.tree.fireEvent("nodedragover", overEvent);
38263         return overEvent.cancel === false && result !== false;
38264     },
38265     
38266     getDropPoint : function(e, n, dd)
38267     {
38268         var tn = n.node;
38269         if(tn.isRoot){
38270             return tn.allowChildren !== false ? "append" : false; // always append for root
38271         }
38272         var dragEl = n.ddel;
38273         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38274         var y = Roo.lib.Event.getPageY(e);
38275         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38276         
38277         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38278         var noAppend = tn.allowChildren === false;
38279         if(this.appendOnly || tn.parentNode.allowChildren === false){
38280             return noAppend ? false : "append";
38281         }
38282         var noBelow = false;
38283         if(!this.allowParentInsert){
38284             noBelow = tn.hasChildNodes() && tn.isExpanded();
38285         }
38286         var q = (b - t) / (noAppend ? 2 : 3);
38287         if(y >= t && y < (t + q)){
38288             return "above";
38289         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38290             return "below";
38291         }else{
38292             return "append";
38293         }
38294     },
38295     
38296     onNodeEnter : function(n, dd, e, data)
38297     {
38298         this.cancelExpand();
38299     },
38300     
38301     onNodeOver : function(n, dd, e, data)
38302     {
38303        
38304         var pt = this.getDropPoint(e, n, dd);
38305         var node = n.node;
38306         
38307         // auto node expand check
38308         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38309             this.queueExpand(node);
38310         }else if(pt != "append"){
38311             this.cancelExpand();
38312         }
38313         
38314         // set the insert point style on the target node
38315         var returnCls = this.dropNotAllowed;
38316         if(this.isValidDropPoint(n, pt, dd, e, data)){
38317            if(pt){
38318                var el = n.ddel;
38319                var cls;
38320                if(pt == "above"){
38321                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38322                    cls = "x-tree-drag-insert-above";
38323                }else if(pt == "below"){
38324                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38325                    cls = "x-tree-drag-insert-below";
38326                }else{
38327                    returnCls = "x-tree-drop-ok-append";
38328                    cls = "x-tree-drag-append";
38329                }
38330                if(this.lastInsertClass != cls){
38331                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38332                    this.lastInsertClass = cls;
38333                }
38334            }
38335        }
38336        return returnCls;
38337     },
38338     
38339     onNodeOut : function(n, dd, e, data){
38340         
38341         this.cancelExpand();
38342         this.removeDropIndicators(n);
38343     },
38344     
38345     onNodeDrop : function(n, dd, e, data){
38346         var point = this.getDropPoint(e, n, dd);
38347         var targetNode = n.node;
38348         targetNode.ui.startDrop();
38349         if(!this.isValidDropPoint(n, point, dd, e, data)){
38350             targetNode.ui.endDrop();
38351             return false;
38352         }
38353         // first try to find the drop node
38354         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38355         var dropEvent = {
38356             tree : this.tree,
38357             target: targetNode,
38358             data: data,
38359             point: point,
38360             source: dd,
38361             rawEvent: e,
38362             dropNode: dropNode,
38363             cancel: !dropNode   
38364         };
38365         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38366         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38367             targetNode.ui.endDrop();
38368             return false;
38369         }
38370         // allow target changing
38371         targetNode = dropEvent.target;
38372         if(point == "append" && !targetNode.isExpanded()){
38373             targetNode.expand(false, null, function(){
38374                 this.completeDrop(dropEvent);
38375             }.createDelegate(this));
38376         }else{
38377             this.completeDrop(dropEvent);
38378         }
38379         return true;
38380     },
38381     
38382     completeDrop : function(de){
38383         var ns = de.dropNode, p = de.point, t = de.target;
38384         if(!(ns instanceof Array)){
38385             ns = [ns];
38386         }
38387         var n;
38388         for(var i = 0, len = ns.length; i < len; i++){
38389             n = ns[i];
38390             if(p == "above"){
38391                 t.parentNode.insertBefore(n, t);
38392             }else if(p == "below"){
38393                 t.parentNode.insertBefore(n, t.nextSibling);
38394             }else{
38395                 t.appendChild(n);
38396             }
38397         }
38398         n.ui.focus();
38399         if(this.tree.hlDrop){
38400             n.ui.highlight();
38401         }
38402         t.ui.endDrop();
38403         this.tree.fireEvent("nodedrop", de);
38404     },
38405     
38406     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38407         if(this.tree.hlDrop){
38408             dropNode.ui.focus();
38409             dropNode.ui.highlight();
38410         }
38411         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38412     },
38413     
38414     getTree : function(){
38415         return this.tree;
38416     },
38417     
38418     removeDropIndicators : function(n){
38419         if(n && n.ddel){
38420             var el = n.ddel;
38421             Roo.fly(el).removeClass([
38422                     "x-tree-drag-insert-above",
38423                     "x-tree-drag-insert-below",
38424                     "x-tree-drag-append"]);
38425             this.lastInsertClass = "_noclass";
38426         }
38427     },
38428     
38429     beforeDragDrop : function(target, e, id){
38430         this.cancelExpand();
38431         return true;
38432     },
38433     
38434     afterRepair : function(data){
38435         if(data && Roo.enableFx){
38436             data.node.ui.highlight();
38437         }
38438         this.hideProxy();
38439     } 
38440     
38441 });
38442
38443 }
38444 /*
38445  * Based on:
38446  * Ext JS Library 1.1.1
38447  * Copyright(c) 2006-2007, Ext JS, LLC.
38448  *
38449  * Originally Released Under LGPL - original licence link has changed is not relivant.
38450  *
38451  * Fork - LGPL
38452  * <script type="text/javascript">
38453  */
38454  
38455
38456 if(Roo.dd.DragZone){
38457 Roo.tree.TreeDragZone = function(tree, config){
38458     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38459     this.tree = tree;
38460 };
38461
38462 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38463     ddGroup : "TreeDD",
38464    
38465     onBeforeDrag : function(data, e){
38466         var n = data.node;
38467         return n && n.draggable && !n.disabled;
38468     },
38469      
38470     
38471     onInitDrag : function(e){
38472         var data = this.dragData;
38473         this.tree.getSelectionModel().select(data.node);
38474         this.proxy.update("");
38475         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38476         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38477     },
38478     
38479     getRepairXY : function(e, data){
38480         return data.node.ui.getDDRepairXY();
38481     },
38482     
38483     onEndDrag : function(data, e){
38484         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38485         
38486         
38487     },
38488     
38489     onValidDrop : function(dd, e, id){
38490         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38491         this.hideProxy();
38492     },
38493     
38494     beforeInvalidDrop : function(e, id){
38495         // this scrolls the original position back into view
38496         var sm = this.tree.getSelectionModel();
38497         sm.clearSelections();
38498         sm.select(this.dragData.node);
38499     }
38500 });
38501 }/*
38502  * Based on:
38503  * Ext JS Library 1.1.1
38504  * Copyright(c) 2006-2007, Ext JS, LLC.
38505  *
38506  * Originally Released Under LGPL - original licence link has changed is not relivant.
38507  *
38508  * Fork - LGPL
38509  * <script type="text/javascript">
38510  */
38511 /**
38512  * @class Roo.tree.TreeEditor
38513  * @extends Roo.Editor
38514  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38515  * as the editor field.
38516  * @constructor
38517  * @param {Object} config (used to be the tree panel.)
38518  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38519  * 
38520  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38521  * @cfg {Roo.form.TextField} field [required] The field configuration
38522  *
38523  * 
38524  */
38525 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38526     var tree = config;
38527     var field;
38528     if (oldconfig) { // old style..
38529         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38530     } else {
38531         // new style..
38532         tree = config.tree;
38533         config.field = config.field  || {};
38534         config.field.xtype = 'TextField';
38535         field = Roo.factory(config.field, Roo.form);
38536     }
38537     config = config || {};
38538     
38539     
38540     this.addEvents({
38541         /**
38542          * @event beforenodeedit
38543          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38544          * false from the handler of this event.
38545          * @param {Editor} this
38546          * @param {Roo.tree.Node} node 
38547          */
38548         "beforenodeedit" : true
38549     });
38550     
38551     //Roo.log(config);
38552     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38553
38554     this.tree = tree;
38555
38556     tree.on('beforeclick', this.beforeNodeClick, this);
38557     tree.getTreeEl().on('mousedown', this.hide, this);
38558     this.on('complete', this.updateNode, this);
38559     this.on('beforestartedit', this.fitToTree, this);
38560     this.on('startedit', this.bindScroll, this, {delay:10});
38561     this.on('specialkey', this.onSpecialKey, this);
38562 };
38563
38564 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38565     /**
38566      * @cfg {String} alignment
38567      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38568      */
38569     alignment: "l-l",
38570     // inherit
38571     autoSize: false,
38572     /**
38573      * @cfg {Boolean} hideEl
38574      * True to hide the bound element while the editor is displayed (defaults to false)
38575      */
38576     hideEl : false,
38577     /**
38578      * @cfg {String} cls
38579      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38580      */
38581     cls: "x-small-editor x-tree-editor",
38582     /**
38583      * @cfg {Boolean} shim
38584      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38585      */
38586     shim:false,
38587     // inherit
38588     shadow:"frame",
38589     /**
38590      * @cfg {Number} maxWidth
38591      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38592      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38593      * scroll and client offsets into account prior to each edit.
38594      */
38595     maxWidth: 250,
38596
38597     editDelay : 350,
38598
38599     // private
38600     fitToTree : function(ed, el){
38601         var td = this.tree.getTreeEl().dom, nd = el.dom;
38602         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38603             td.scrollLeft = nd.offsetLeft;
38604         }
38605         var w = Math.min(
38606                 this.maxWidth,
38607                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38608         this.setSize(w, '');
38609         
38610         return this.fireEvent('beforenodeedit', this, this.editNode);
38611         
38612     },
38613
38614     // private
38615     triggerEdit : function(node){
38616         this.completeEdit();
38617         this.editNode = node;
38618         this.startEdit(node.ui.textNode, node.text);
38619     },
38620
38621     // private
38622     bindScroll : function(){
38623         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38624     },
38625
38626     // private
38627     beforeNodeClick : function(node, e){
38628         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38629         this.lastClick = new Date();
38630         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38631             e.stopEvent();
38632             this.triggerEdit(node);
38633             return false;
38634         }
38635         return true;
38636     },
38637
38638     // private
38639     updateNode : function(ed, value){
38640         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38641         this.editNode.setText(value);
38642     },
38643
38644     // private
38645     onHide : function(){
38646         Roo.tree.TreeEditor.superclass.onHide.call(this);
38647         if(this.editNode){
38648             this.editNode.ui.focus();
38649         }
38650     },
38651
38652     // private
38653     onSpecialKey : function(field, e){
38654         var k = e.getKey();
38655         if(k == e.ESC){
38656             e.stopEvent();
38657             this.cancelEdit();
38658         }else if(k == e.ENTER && !e.hasModifier()){
38659             e.stopEvent();
38660             this.completeEdit();
38661         }
38662     }
38663 });//<Script type="text/javascript">
38664 /*
38665  * Based on:
38666  * Ext JS Library 1.1.1
38667  * Copyright(c) 2006-2007, Ext JS, LLC.
38668  *
38669  * Originally Released Under LGPL - original licence link has changed is not relivant.
38670  *
38671  * Fork - LGPL
38672  * <script type="text/javascript">
38673  */
38674  
38675 /**
38676  * Not documented??? - probably should be...
38677  */
38678
38679 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38680     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38681     
38682     renderElements : function(n, a, targetNode, bulkRender){
38683         //consel.log("renderElements?");
38684         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38685
38686         var t = n.getOwnerTree();
38687         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38688         
38689         var cols = t.columns;
38690         var bw = t.borderWidth;
38691         var c = cols[0];
38692         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38693          var cb = typeof a.checked == "boolean";
38694         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38695         var colcls = 'x-t-' + tid + '-c0';
38696         var buf = [
38697             '<li class="x-tree-node">',
38698             
38699                 
38700                 '<div class="x-tree-node-el ', a.cls,'">',
38701                     // extran...
38702                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38703                 
38704                 
38705                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38706                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38707                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38708                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38709                            (a.iconCls ? ' '+a.iconCls : ''),
38710                            '" unselectable="on" />',
38711                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38712                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38713                              
38714                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38715                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38716                             '<span unselectable="on" qtip="' + tx + '">',
38717                              tx,
38718                              '</span></a>' ,
38719                     '</div>',
38720                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38721                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38722                  ];
38723         for(var i = 1, len = cols.length; i < len; i++){
38724             c = cols[i];
38725             colcls = 'x-t-' + tid + '-c' +i;
38726             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38727             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38728                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38729                       "</div>");
38730          }
38731          
38732          buf.push(
38733             '</a>',
38734             '<div class="x-clear"></div></div>',
38735             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38736             "</li>");
38737         
38738         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38739             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38740                                 n.nextSibling.ui.getEl(), buf.join(""));
38741         }else{
38742             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38743         }
38744         var el = this.wrap.firstChild;
38745         this.elRow = el;
38746         this.elNode = el.firstChild;
38747         this.ranchor = el.childNodes[1];
38748         this.ctNode = this.wrap.childNodes[1];
38749         var cs = el.firstChild.childNodes;
38750         this.indentNode = cs[0];
38751         this.ecNode = cs[1];
38752         this.iconNode = cs[2];
38753         var index = 3;
38754         if(cb){
38755             this.checkbox = cs[3];
38756             index++;
38757         }
38758         this.anchor = cs[index];
38759         
38760         this.textNode = cs[index].firstChild;
38761         
38762         //el.on("click", this.onClick, this);
38763         //el.on("dblclick", this.onDblClick, this);
38764         
38765         
38766        // console.log(this);
38767     },
38768     initEvents : function(){
38769         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38770         
38771             
38772         var a = this.ranchor;
38773
38774         var el = Roo.get(a);
38775
38776         if(Roo.isOpera){ // opera render bug ignores the CSS
38777             el.setStyle("text-decoration", "none");
38778         }
38779
38780         el.on("click", this.onClick, this);
38781         el.on("dblclick", this.onDblClick, this);
38782         el.on("contextmenu", this.onContextMenu, this);
38783         
38784     },
38785     
38786     /*onSelectedChange : function(state){
38787         if(state){
38788             this.focus();
38789             this.addClass("x-tree-selected");
38790         }else{
38791             //this.blur();
38792             this.removeClass("x-tree-selected");
38793         }
38794     },*/
38795     addClass : function(cls){
38796         if(this.elRow){
38797             Roo.fly(this.elRow).addClass(cls);
38798         }
38799         
38800     },
38801     
38802     
38803     removeClass : function(cls){
38804         if(this.elRow){
38805             Roo.fly(this.elRow).removeClass(cls);
38806         }
38807     }
38808
38809     
38810     
38811 });//<Script type="text/javascript">
38812
38813 /*
38814  * Based on:
38815  * Ext JS Library 1.1.1
38816  * Copyright(c) 2006-2007, Ext JS, LLC.
38817  *
38818  * Originally Released Under LGPL - original licence link has changed is not relivant.
38819  *
38820  * Fork - LGPL
38821  * <script type="text/javascript">
38822  */
38823  
38824
38825 /**
38826  * @class Roo.tree.ColumnTree
38827  * @extends Roo.tree.TreePanel
38828  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38829  * @cfg {int} borderWidth  compined right/left border allowance
38830  * @constructor
38831  * @param {String/HTMLElement/Element} el The container element
38832  * @param {Object} config
38833  */
38834 Roo.tree.ColumnTree =  function(el, config)
38835 {
38836    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38837    this.addEvents({
38838         /**
38839         * @event resize
38840         * Fire this event on a container when it resizes
38841         * @param {int} w Width
38842         * @param {int} h Height
38843         */
38844        "resize" : true
38845     });
38846     this.on('resize', this.onResize, this);
38847 };
38848
38849 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38850     //lines:false,
38851     
38852     
38853     borderWidth: Roo.isBorderBox ? 0 : 2, 
38854     headEls : false,
38855     
38856     render : function(){
38857         // add the header.....
38858        
38859         Roo.tree.ColumnTree.superclass.render.apply(this);
38860         
38861         this.el.addClass('x-column-tree');
38862         
38863         this.headers = this.el.createChild(
38864             {cls:'x-tree-headers'},this.innerCt.dom);
38865    
38866         var cols = this.columns, c;
38867         var totalWidth = 0;
38868         this.headEls = [];
38869         var  len = cols.length;
38870         for(var i = 0; i < len; i++){
38871              c = cols[i];
38872              totalWidth += c.width;
38873             this.headEls.push(this.headers.createChild({
38874                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38875                  cn: {
38876                      cls:'x-tree-hd-text',
38877                      html: c.header
38878                  },
38879                  style:'width:'+(c.width-this.borderWidth)+'px;'
38880              }));
38881         }
38882         this.headers.createChild({cls:'x-clear'});
38883         // prevent floats from wrapping when clipped
38884         this.headers.setWidth(totalWidth);
38885         //this.innerCt.setWidth(totalWidth);
38886         this.innerCt.setStyle({ overflow: 'auto' });
38887         this.onResize(this.width, this.height);
38888              
38889         
38890     },
38891     onResize : function(w,h)
38892     {
38893         this.height = h;
38894         this.width = w;
38895         // resize cols..
38896         this.innerCt.setWidth(this.width);
38897         this.innerCt.setHeight(this.height-20);
38898         
38899         // headers...
38900         var cols = this.columns, c;
38901         var totalWidth = 0;
38902         var expEl = false;
38903         var len = cols.length;
38904         for(var i = 0; i < len; i++){
38905             c = cols[i];
38906             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38907                 // it's the expander..
38908                 expEl  = this.headEls[i];
38909                 continue;
38910             }
38911             totalWidth += c.width;
38912             
38913         }
38914         if (expEl) {
38915             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38916         }
38917         this.headers.setWidth(w-20);
38918
38919         
38920         
38921         
38922     }
38923 });
38924 /*
38925  * Based on:
38926  * Ext JS Library 1.1.1
38927  * Copyright(c) 2006-2007, Ext JS, LLC.
38928  *
38929  * Originally Released Under LGPL - original licence link has changed is not relivant.
38930  *
38931  * Fork - LGPL
38932  * <script type="text/javascript">
38933  */
38934  
38935 /**
38936  * @class Roo.menu.Menu
38937  * @extends Roo.util.Observable
38938  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38939  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38940  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38941  * @constructor
38942  * Creates a new Menu
38943  * @param {Object} config Configuration options
38944  */
38945 Roo.menu.Menu = function(config){
38946     
38947     Roo.menu.Menu.superclass.constructor.call(this, config);
38948     
38949     this.id = this.id || Roo.id();
38950     this.addEvents({
38951         /**
38952          * @event beforeshow
38953          * Fires before this menu is displayed
38954          * @param {Roo.menu.Menu} this
38955          */
38956         beforeshow : true,
38957         /**
38958          * @event beforehide
38959          * Fires before this menu is hidden
38960          * @param {Roo.menu.Menu} this
38961          */
38962         beforehide : true,
38963         /**
38964          * @event show
38965          * Fires after this menu is displayed
38966          * @param {Roo.menu.Menu} this
38967          */
38968         show : true,
38969         /**
38970          * @event hide
38971          * Fires after this menu is hidden
38972          * @param {Roo.menu.Menu} this
38973          */
38974         hide : true,
38975         /**
38976          * @event click
38977          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38978          * @param {Roo.menu.Menu} this
38979          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38980          * @param {Roo.EventObject} e
38981          */
38982         click : true,
38983         /**
38984          * @event mouseover
38985          * Fires when the mouse is hovering over this menu
38986          * @param {Roo.menu.Menu} this
38987          * @param {Roo.EventObject} e
38988          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38989          */
38990         mouseover : true,
38991         /**
38992          * @event mouseout
38993          * Fires when the mouse exits this menu
38994          * @param {Roo.menu.Menu} this
38995          * @param {Roo.EventObject} e
38996          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38997          */
38998         mouseout : true,
38999         /**
39000          * @event itemclick
39001          * Fires when a menu item contained in this menu is clicked
39002          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
39003          * @param {Roo.EventObject} e
39004          */
39005         itemclick: true
39006     });
39007     if (this.registerMenu) {
39008         Roo.menu.MenuMgr.register(this);
39009     }
39010     
39011     var mis = this.items;
39012     this.items = new Roo.util.MixedCollection();
39013     if(mis){
39014         this.add.apply(this, mis);
39015     }
39016 };
39017
39018 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
39019     /**
39020      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
39021      */
39022     minWidth : 120,
39023     /**
39024      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
39025      * for bottom-right shadow (defaults to "sides")
39026      */
39027     shadow : "sides",
39028     /**
39029      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
39030      * this menu (defaults to "tl-tr?")
39031      */
39032     subMenuAlign : "tl-tr?",
39033     /**
39034      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
39035      * relative to its element of origin (defaults to "tl-bl?")
39036      */
39037     defaultAlign : "tl-bl?",
39038     /**
39039      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
39040      */
39041     allowOtherMenus : false,
39042     /**
39043      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
39044      */
39045     registerMenu : true,
39046
39047     hidden:true,
39048
39049     // private
39050     render : function(){
39051         if(this.el){
39052             return;
39053         }
39054         var el = this.el = new Roo.Layer({
39055             cls: "x-menu",
39056             shadow:this.shadow,
39057             constrain: false,
39058             parentEl: this.parentEl || document.body,
39059             zindex:15000
39060         });
39061
39062         this.keyNav = new Roo.menu.MenuNav(this);
39063
39064         if(this.plain){
39065             el.addClass("x-menu-plain");
39066         }
39067         if(this.cls){
39068             el.addClass(this.cls);
39069         }
39070         // generic focus element
39071         this.focusEl = el.createChild({
39072             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
39073         });
39074         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
39075         //disabling touch- as it's causing issues ..
39076         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
39077         ul.on('click'   , this.onClick, this);
39078         
39079         
39080         ul.on("mouseover", this.onMouseOver, this);
39081         ul.on("mouseout", this.onMouseOut, this);
39082         this.items.each(function(item){
39083             if (item.hidden) {
39084                 return;
39085             }
39086             
39087             var li = document.createElement("li");
39088             li.className = "x-menu-list-item";
39089             ul.dom.appendChild(li);
39090             item.render(li, this);
39091         }, this);
39092         this.ul = ul;
39093         this.autoWidth();
39094     },
39095
39096     // private
39097     autoWidth : function(){
39098         var el = this.el, ul = this.ul;
39099         if(!el){
39100             return;
39101         }
39102         var w = this.width;
39103         if(w){
39104             el.setWidth(w);
39105         }else if(Roo.isIE){
39106             el.setWidth(this.minWidth);
39107             var t = el.dom.offsetWidth; // force recalc
39108             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
39109         }
39110     },
39111
39112     // private
39113     delayAutoWidth : function(){
39114         if(this.rendered){
39115             if(!this.awTask){
39116                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
39117             }
39118             this.awTask.delay(20);
39119         }
39120     },
39121
39122     // private
39123     findTargetItem : function(e){
39124         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
39125         if(t && t.menuItemId){
39126             return this.items.get(t.menuItemId);
39127         }
39128     },
39129
39130     // private
39131     onClick : function(e){
39132         Roo.log("menu.onClick");
39133         var t = this.findTargetItem(e);
39134         if(!t){
39135             return;
39136         }
39137         Roo.log(e);
39138         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
39139             if(t == this.activeItem && t.shouldDeactivate(e)){
39140                 this.activeItem.deactivate();
39141                 delete this.activeItem;
39142                 return;
39143             }
39144             if(t.canActivate){
39145                 this.setActiveItem(t, true);
39146             }
39147             return;
39148             
39149             
39150         }
39151         
39152         t.onClick(e);
39153         this.fireEvent("click", this, t, e);
39154     },
39155
39156     // private
39157     setActiveItem : function(item, autoExpand){
39158         if(item != this.activeItem){
39159             if(this.activeItem){
39160                 this.activeItem.deactivate();
39161             }
39162             this.activeItem = item;
39163             item.activate(autoExpand);
39164         }else if(autoExpand){
39165             item.expandMenu();
39166         }
39167     },
39168
39169     // private
39170     tryActivate : function(start, step){
39171         var items = this.items;
39172         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
39173             var item = items.get(i);
39174             if(!item.disabled && item.canActivate){
39175                 this.setActiveItem(item, false);
39176                 return item;
39177             }
39178         }
39179         return false;
39180     },
39181
39182     // private
39183     onMouseOver : function(e){
39184         var t;
39185         if(t = this.findTargetItem(e)){
39186             if(t.canActivate && !t.disabled){
39187                 this.setActiveItem(t, true);
39188             }
39189         }
39190         this.fireEvent("mouseover", this, e, t);
39191     },
39192
39193     // private
39194     onMouseOut : function(e){
39195         var t;
39196         if(t = this.findTargetItem(e)){
39197             if(t == this.activeItem && t.shouldDeactivate(e)){
39198                 this.activeItem.deactivate();
39199                 delete this.activeItem;
39200             }
39201         }
39202         this.fireEvent("mouseout", this, e, t);
39203     },
39204
39205     /**
39206      * Read-only.  Returns true if the menu is currently displayed, else false.
39207      * @type Boolean
39208      */
39209     isVisible : function(){
39210         return this.el && !this.hidden;
39211     },
39212
39213     /**
39214      * Displays this menu relative to another element
39215      * @param {String/HTMLElement/Roo.Element} element The element to align to
39216      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39217      * the element (defaults to this.defaultAlign)
39218      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39219      */
39220     show : function(el, pos, parentMenu){
39221         this.parentMenu = parentMenu;
39222         if(!this.el){
39223             this.render();
39224         }
39225         this.fireEvent("beforeshow", this);
39226         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39227     },
39228
39229     /**
39230      * Displays this menu at a specific xy position
39231      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39232      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39233      */
39234     showAt : function(xy, parentMenu, /* private: */_e){
39235         this.parentMenu = parentMenu;
39236         if(!this.el){
39237             this.render();
39238         }
39239         if(_e !== false){
39240             this.fireEvent("beforeshow", this);
39241             xy = this.el.adjustForConstraints(xy);
39242         }
39243         this.el.setXY(xy);
39244         this.el.show();
39245         this.hidden = false;
39246         this.focus();
39247         this.fireEvent("show", this);
39248     },
39249
39250     focus : function(){
39251         if(!this.hidden){
39252             this.doFocus.defer(50, this);
39253         }
39254     },
39255
39256     doFocus : function(){
39257         if(!this.hidden){
39258             this.focusEl.focus();
39259         }
39260     },
39261
39262     /**
39263      * Hides this menu and optionally all parent menus
39264      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39265      */
39266     hide : function(deep){
39267         if(this.el && this.isVisible()){
39268             this.fireEvent("beforehide", this);
39269             if(this.activeItem){
39270                 this.activeItem.deactivate();
39271                 this.activeItem = null;
39272             }
39273             this.el.hide();
39274             this.hidden = true;
39275             this.fireEvent("hide", this);
39276         }
39277         if(deep === true && this.parentMenu){
39278             this.parentMenu.hide(true);
39279         }
39280     },
39281
39282     /**
39283      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39284      * Any of the following are valid:
39285      * <ul>
39286      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39287      * <li>An HTMLElement object which will be converted to a menu item</li>
39288      * <li>A menu item config object that will be created as a new menu item</li>
39289      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39290      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39291      * </ul>
39292      * Usage:
39293      * <pre><code>
39294 // Create the menu
39295 var menu = new Roo.menu.Menu();
39296
39297 // Create a menu item to add by reference
39298 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39299
39300 // Add a bunch of items at once using different methods.
39301 // Only the last item added will be returned.
39302 var item = menu.add(
39303     menuItem,                // add existing item by ref
39304     'Dynamic Item',          // new TextItem
39305     '-',                     // new separator
39306     { text: 'Config Item' }  // new item by config
39307 );
39308 </code></pre>
39309      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39310      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39311      */
39312     add : function(){
39313         var a = arguments, l = a.length, item;
39314         for(var i = 0; i < l; i++){
39315             var el = a[i];
39316             if ((typeof(el) == "object") && el.xtype && el.xns) {
39317                 el = Roo.factory(el, Roo.menu);
39318             }
39319             
39320             if(el.render){ // some kind of Item
39321                 item = this.addItem(el);
39322             }else if(typeof el == "string"){ // string
39323                 if(el == "separator" || el == "-"){
39324                     item = this.addSeparator();
39325                 }else{
39326                     item = this.addText(el);
39327                 }
39328             }else if(el.tagName || el.el){ // element
39329                 item = this.addElement(el);
39330             }else if(typeof el == "object"){ // must be menu item config?
39331                 item = this.addMenuItem(el);
39332             }
39333         }
39334         return item;
39335     },
39336
39337     /**
39338      * Returns this menu's underlying {@link Roo.Element} object
39339      * @return {Roo.Element} The element
39340      */
39341     getEl : function(){
39342         if(!this.el){
39343             this.render();
39344         }
39345         return this.el;
39346     },
39347
39348     /**
39349      * Adds a separator bar to the menu
39350      * @return {Roo.menu.Item} The menu item that was added
39351      */
39352     addSeparator : function(){
39353         return this.addItem(new Roo.menu.Separator());
39354     },
39355
39356     /**
39357      * Adds an {@link Roo.Element} object to the menu
39358      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39359      * @return {Roo.menu.Item} The menu item that was added
39360      */
39361     addElement : function(el){
39362         return this.addItem(new Roo.menu.BaseItem(el));
39363     },
39364
39365     /**
39366      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39367      * @param {Roo.menu.Item} item The menu item to add
39368      * @return {Roo.menu.Item} The menu item that was added
39369      */
39370     addItem : function(item){
39371         this.items.add(item);
39372         if(this.ul){
39373             var li = document.createElement("li");
39374             li.className = "x-menu-list-item";
39375             this.ul.dom.appendChild(li);
39376             item.render(li, this);
39377             this.delayAutoWidth();
39378         }
39379         return item;
39380     },
39381
39382     /**
39383      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39384      * @param {Object} config A MenuItem config object
39385      * @return {Roo.menu.Item} The menu item that was added
39386      */
39387     addMenuItem : function(config){
39388         if(!(config instanceof Roo.menu.Item)){
39389             if(typeof config.checked == "boolean"){ // must be check menu item config?
39390                 config = new Roo.menu.CheckItem(config);
39391             }else{
39392                 config = new Roo.menu.Item(config);
39393             }
39394         }
39395         return this.addItem(config);
39396     },
39397
39398     /**
39399      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39400      * @param {String} text The text to display in the menu item
39401      * @return {Roo.menu.Item} The menu item that was added
39402      */
39403     addText : function(text){
39404         return this.addItem(new Roo.menu.TextItem({ text : text }));
39405     },
39406
39407     /**
39408      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39409      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39410      * @param {Roo.menu.Item} item The menu item to add
39411      * @return {Roo.menu.Item} The menu item that was added
39412      */
39413     insert : function(index, item){
39414         this.items.insert(index, item);
39415         if(this.ul){
39416             var li = document.createElement("li");
39417             li.className = "x-menu-list-item";
39418             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39419             item.render(li, this);
39420             this.delayAutoWidth();
39421         }
39422         return item;
39423     },
39424
39425     /**
39426      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39427      * @param {Roo.menu.Item} item The menu item to remove
39428      */
39429     remove : function(item){
39430         this.items.removeKey(item.id);
39431         item.destroy();
39432     },
39433
39434     /**
39435      * Removes and destroys all items in the menu
39436      */
39437     removeAll : function(){
39438         var f;
39439         while(f = this.items.first()){
39440             this.remove(f);
39441         }
39442     }
39443 });
39444
39445 // MenuNav is a private utility class used internally by the Menu
39446 Roo.menu.MenuNav = function(menu){
39447     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39448     this.scope = this.menu = menu;
39449 };
39450
39451 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39452     doRelay : function(e, h){
39453         var k = e.getKey();
39454         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39455             this.menu.tryActivate(0, 1);
39456             return false;
39457         }
39458         return h.call(this.scope || this, e, this.menu);
39459     },
39460
39461     up : function(e, m){
39462         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39463             m.tryActivate(m.items.length-1, -1);
39464         }
39465     },
39466
39467     down : function(e, m){
39468         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39469             m.tryActivate(0, 1);
39470         }
39471     },
39472
39473     right : function(e, m){
39474         if(m.activeItem){
39475             m.activeItem.expandMenu(true);
39476         }
39477     },
39478
39479     left : function(e, m){
39480         m.hide();
39481         if(m.parentMenu && m.parentMenu.activeItem){
39482             m.parentMenu.activeItem.activate();
39483         }
39484     },
39485
39486     enter : function(e, m){
39487         if(m.activeItem){
39488             e.stopPropagation();
39489             m.activeItem.onClick(e);
39490             m.fireEvent("click", this, m.activeItem);
39491             return true;
39492         }
39493     }
39494 });/*
39495  * Based on:
39496  * Ext JS Library 1.1.1
39497  * Copyright(c) 2006-2007, Ext JS, LLC.
39498  *
39499  * Originally Released Under LGPL - original licence link has changed is not relivant.
39500  *
39501  * Fork - LGPL
39502  * <script type="text/javascript">
39503  */
39504  
39505 /**
39506  * @class Roo.menu.MenuMgr
39507  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39508  * @static
39509  */
39510 Roo.menu.MenuMgr = function(){
39511    var menus, active, groups = {}, attached = false, lastShow = new Date();
39512
39513    // private - called when first menu is created
39514    function init(){
39515        menus = {};
39516        active = new Roo.util.MixedCollection();
39517        Roo.get(document).addKeyListener(27, function(){
39518            if(active.length > 0){
39519                hideAll();
39520            }
39521        });
39522    }
39523
39524    // private
39525    function hideAll(){
39526        if(active && active.length > 0){
39527            var c = active.clone();
39528            c.each(function(m){
39529                m.hide();
39530            });
39531        }
39532    }
39533
39534    // private
39535    function onHide(m){
39536        active.remove(m);
39537        if(active.length < 1){
39538            Roo.get(document).un("mousedown", onMouseDown);
39539            attached = false;
39540        }
39541    }
39542
39543    // private
39544    function onShow(m){
39545        var last = active.last();
39546        lastShow = new Date();
39547        active.add(m);
39548        if(!attached){
39549            Roo.get(document).on("mousedown", onMouseDown);
39550            attached = true;
39551        }
39552        if(m.parentMenu){
39553           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39554           m.parentMenu.activeChild = m;
39555        }else if(last && last.isVisible()){
39556           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39557        }
39558    }
39559
39560    // private
39561    function onBeforeHide(m){
39562        if(m.activeChild){
39563            m.activeChild.hide();
39564        }
39565        if(m.autoHideTimer){
39566            clearTimeout(m.autoHideTimer);
39567            delete m.autoHideTimer;
39568        }
39569    }
39570
39571    // private
39572    function onBeforeShow(m){
39573        var pm = m.parentMenu;
39574        if(!pm && !m.allowOtherMenus){
39575            hideAll();
39576        }else if(pm && pm.activeChild && active != m){
39577            pm.activeChild.hide();
39578        }
39579    }
39580
39581    // private
39582    function onMouseDown(e){
39583        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39584            hideAll();
39585        }
39586    }
39587
39588    // private
39589    function onBeforeCheck(mi, state){
39590        if(state){
39591            var g = groups[mi.group];
39592            for(var i = 0, l = g.length; i < l; i++){
39593                if(g[i] != mi){
39594                    g[i].setChecked(false);
39595                }
39596            }
39597        }
39598    }
39599
39600    return {
39601
39602        /**
39603         * Hides all menus that are currently visible
39604         */
39605        hideAll : function(){
39606             hideAll();  
39607        },
39608
39609        // private
39610        register : function(menu){
39611            if(!menus){
39612                init();
39613            }
39614            menus[menu.id] = menu;
39615            menu.on("beforehide", onBeforeHide);
39616            menu.on("hide", onHide);
39617            menu.on("beforeshow", onBeforeShow);
39618            menu.on("show", onShow);
39619            var g = menu.group;
39620            if(g && menu.events["checkchange"]){
39621                if(!groups[g]){
39622                    groups[g] = [];
39623                }
39624                groups[g].push(menu);
39625                menu.on("checkchange", onCheck);
39626            }
39627        },
39628
39629         /**
39630          * Returns a {@link Roo.menu.Menu} object
39631          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39632          * be used to generate and return a new Menu instance.
39633          */
39634        get : function(menu){
39635            if(typeof menu == "string"){ // menu id
39636                return menus[menu];
39637            }else if(menu.events){  // menu instance
39638                return menu;
39639            }else if(typeof menu.length == 'number'){ // array of menu items?
39640                return new Roo.menu.Menu({items:menu});
39641            }else{ // otherwise, must be a config
39642                return new Roo.menu.Menu(menu);
39643            }
39644        },
39645
39646        // private
39647        unregister : function(menu){
39648            delete menus[menu.id];
39649            menu.un("beforehide", onBeforeHide);
39650            menu.un("hide", onHide);
39651            menu.un("beforeshow", onBeforeShow);
39652            menu.un("show", onShow);
39653            var g = menu.group;
39654            if(g && menu.events["checkchange"]){
39655                groups[g].remove(menu);
39656                menu.un("checkchange", onCheck);
39657            }
39658        },
39659
39660        // private
39661        registerCheckable : function(menuItem){
39662            var g = menuItem.group;
39663            if(g){
39664                if(!groups[g]){
39665                    groups[g] = [];
39666                }
39667                groups[g].push(menuItem);
39668                menuItem.on("beforecheckchange", onBeforeCheck);
39669            }
39670        },
39671
39672        // private
39673        unregisterCheckable : function(menuItem){
39674            var g = menuItem.group;
39675            if(g){
39676                groups[g].remove(menuItem);
39677                menuItem.un("beforecheckchange", onBeforeCheck);
39678            }
39679        }
39680    };
39681 }();/*
39682  * Based on:
39683  * Ext JS Library 1.1.1
39684  * Copyright(c) 2006-2007, Ext JS, LLC.
39685  *
39686  * Originally Released Under LGPL - original licence link has changed is not relivant.
39687  *
39688  * Fork - LGPL
39689  * <script type="text/javascript">
39690  */
39691  
39692
39693 /**
39694  * @class Roo.menu.BaseItem
39695  * @extends Roo.Component
39696  * @abstract
39697  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39698  * management and base configuration options shared by all menu components.
39699  * @constructor
39700  * Creates a new BaseItem
39701  * @param {Object} config Configuration options
39702  */
39703 Roo.menu.BaseItem = function(config){
39704     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39705
39706     this.addEvents({
39707         /**
39708          * @event click
39709          * Fires when this item is clicked
39710          * @param {Roo.menu.BaseItem} this
39711          * @param {Roo.EventObject} e
39712          */
39713         click: true,
39714         /**
39715          * @event activate
39716          * Fires when this item is activated
39717          * @param {Roo.menu.BaseItem} this
39718          */
39719         activate : true,
39720         /**
39721          * @event deactivate
39722          * Fires when this item is deactivated
39723          * @param {Roo.menu.BaseItem} this
39724          */
39725         deactivate : true
39726     });
39727
39728     if(this.handler){
39729         this.on("click", this.handler, this.scope, true);
39730     }
39731 };
39732
39733 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39734     /**
39735      * @cfg {Function} handler
39736      * A function that will handle the click event of this menu item (defaults to undefined)
39737      */
39738     /**
39739      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39740      */
39741     canActivate : false,
39742     
39743      /**
39744      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39745      */
39746     hidden: false,
39747     
39748     /**
39749      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39750      */
39751     activeClass : "x-menu-item-active",
39752     /**
39753      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39754      */
39755     hideOnClick : true,
39756     /**
39757      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39758      */
39759     hideDelay : 100,
39760
39761     // private
39762     ctype: "Roo.menu.BaseItem",
39763
39764     // private
39765     actionMode : "container",
39766
39767     // private
39768     render : function(container, parentMenu){
39769         this.parentMenu = parentMenu;
39770         Roo.menu.BaseItem.superclass.render.call(this, container);
39771         this.container.menuItemId = this.id;
39772     },
39773
39774     // private
39775     onRender : function(container, position){
39776         this.el = Roo.get(this.el);
39777         container.dom.appendChild(this.el.dom);
39778     },
39779
39780     // private
39781     onClick : function(e){
39782         if(!this.disabled && this.fireEvent("click", this, e) !== false
39783                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39784             this.handleClick(e);
39785         }else{
39786             e.stopEvent();
39787         }
39788     },
39789
39790     // private
39791     activate : function(){
39792         if(this.disabled){
39793             return false;
39794         }
39795         var li = this.container;
39796         li.addClass(this.activeClass);
39797         this.region = li.getRegion().adjust(2, 2, -2, -2);
39798         this.fireEvent("activate", this);
39799         return true;
39800     },
39801
39802     // private
39803     deactivate : function(){
39804         this.container.removeClass(this.activeClass);
39805         this.fireEvent("deactivate", this);
39806     },
39807
39808     // private
39809     shouldDeactivate : function(e){
39810         return !this.region || !this.region.contains(e.getPoint());
39811     },
39812
39813     // private
39814     handleClick : function(e){
39815         if(this.hideOnClick){
39816             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39817         }
39818     },
39819
39820     // private
39821     expandMenu : function(autoActivate){
39822         // do nothing
39823     },
39824
39825     // private
39826     hideMenu : function(){
39827         // do nothing
39828     }
39829 });/*
39830  * Based on:
39831  * Ext JS Library 1.1.1
39832  * Copyright(c) 2006-2007, Ext JS, LLC.
39833  *
39834  * Originally Released Under LGPL - original licence link has changed is not relivant.
39835  *
39836  * Fork - LGPL
39837  * <script type="text/javascript">
39838  */
39839  
39840 /**
39841  * @class Roo.menu.Adapter
39842  * @extends Roo.menu.BaseItem
39843  * @abstract
39844  * 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.
39845  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39846  * @constructor
39847  * Creates a new Adapter
39848  * @param {Object} config Configuration options
39849  */
39850 Roo.menu.Adapter = function(component, config){
39851     Roo.menu.Adapter.superclass.constructor.call(this, config);
39852     this.component = component;
39853 };
39854 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39855     // private
39856     canActivate : true,
39857
39858     // private
39859     onRender : function(container, position){
39860         this.component.render(container);
39861         this.el = this.component.getEl();
39862     },
39863
39864     // private
39865     activate : function(){
39866         if(this.disabled){
39867             return false;
39868         }
39869         this.component.focus();
39870         this.fireEvent("activate", this);
39871         return true;
39872     },
39873
39874     // private
39875     deactivate : function(){
39876         this.fireEvent("deactivate", this);
39877     },
39878
39879     // private
39880     disable : function(){
39881         this.component.disable();
39882         Roo.menu.Adapter.superclass.disable.call(this);
39883     },
39884
39885     // private
39886     enable : function(){
39887         this.component.enable();
39888         Roo.menu.Adapter.superclass.enable.call(this);
39889     }
39890 });/*
39891  * Based on:
39892  * Ext JS Library 1.1.1
39893  * Copyright(c) 2006-2007, Ext JS, LLC.
39894  *
39895  * Originally Released Under LGPL - original licence link has changed is not relivant.
39896  *
39897  * Fork - LGPL
39898  * <script type="text/javascript">
39899  */
39900
39901 /**
39902  * @class Roo.menu.TextItem
39903  * @extends Roo.menu.BaseItem
39904  * Adds a static text string to a menu, usually used as either a heading or group separator.
39905  * Note: old style constructor with text is still supported.
39906  * 
39907  * @constructor
39908  * Creates a new TextItem
39909  * @param {Object} cfg Configuration
39910  */
39911 Roo.menu.TextItem = function(cfg){
39912     if (typeof(cfg) == 'string') {
39913         this.text = cfg;
39914     } else {
39915         Roo.apply(this,cfg);
39916     }
39917     
39918     Roo.menu.TextItem.superclass.constructor.call(this);
39919 };
39920
39921 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39922     /**
39923      * @cfg {String} text Text to show on item.
39924      */
39925     text : '',
39926     
39927     /**
39928      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39929      */
39930     hideOnClick : false,
39931     /**
39932      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39933      */
39934     itemCls : "x-menu-text",
39935
39936     // private
39937     onRender : function(){
39938         var s = document.createElement("span");
39939         s.className = this.itemCls;
39940         s.innerHTML = this.text;
39941         this.el = s;
39942         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39943     }
39944 });/*
39945  * Based on:
39946  * Ext JS Library 1.1.1
39947  * Copyright(c) 2006-2007, Ext JS, LLC.
39948  *
39949  * Originally Released Under LGPL - original licence link has changed is not relivant.
39950  *
39951  * Fork - LGPL
39952  * <script type="text/javascript">
39953  */
39954
39955 /**
39956  * @class Roo.menu.Separator
39957  * @extends Roo.menu.BaseItem
39958  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39959  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39960  * @constructor
39961  * @param {Object} config Configuration options
39962  */
39963 Roo.menu.Separator = function(config){
39964     Roo.menu.Separator.superclass.constructor.call(this, config);
39965 };
39966
39967 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39968     /**
39969      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39970      */
39971     itemCls : "x-menu-sep",
39972     /**
39973      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39974      */
39975     hideOnClick : false,
39976
39977     // private
39978     onRender : function(li){
39979         var s = document.createElement("span");
39980         s.className = this.itemCls;
39981         s.innerHTML = "&#160;";
39982         this.el = s;
39983         li.addClass("x-menu-sep-li");
39984         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39985     }
39986 });/*
39987  * Based on:
39988  * Ext JS Library 1.1.1
39989  * Copyright(c) 2006-2007, Ext JS, LLC.
39990  *
39991  * Originally Released Under LGPL - original licence link has changed is not relivant.
39992  *
39993  * Fork - LGPL
39994  * <script type="text/javascript">
39995  */
39996 /**
39997  * @class Roo.menu.Item
39998  * @extends Roo.menu.BaseItem
39999  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
40000  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
40001  * activation and click handling.
40002  * @constructor
40003  * Creates a new Item
40004  * @param {Object} config Configuration options
40005  */
40006 Roo.menu.Item = function(config){
40007     Roo.menu.Item.superclass.constructor.call(this, config);
40008     if(this.menu){
40009         this.menu = Roo.menu.MenuMgr.get(this.menu);
40010     }
40011 };
40012 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
40013     /**
40014      * @cfg {Roo.menu.Menu} menu
40015      * A Sub menu
40016      */
40017     /**
40018      * @cfg {String} text
40019      * The text to show on the menu item.
40020      */
40021     text: '',
40022      /**
40023      * @cfg {String} html to render in menu
40024      * The text to show on the menu item (HTML version).
40025      */
40026     html: '',
40027     /**
40028      * @cfg {String} icon
40029      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
40030      */
40031     icon: undefined,
40032     /**
40033      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
40034      */
40035     itemCls : "x-menu-item",
40036     /**
40037      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
40038      */
40039     canActivate : true,
40040     /**
40041      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
40042      */
40043     showDelay: 200,
40044     // doc'd in BaseItem
40045     hideDelay: 200,
40046
40047     // private
40048     ctype: "Roo.menu.Item",
40049     
40050     // private
40051     onRender : function(container, position){
40052         var el = document.createElement("a");
40053         el.hideFocus = true;
40054         el.unselectable = "on";
40055         el.href = this.href || "#";
40056         if(this.hrefTarget){
40057             el.target = this.hrefTarget;
40058         }
40059         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
40060         
40061         var html = this.html.length ? this.html  : String.format('{0}',this.text);
40062         
40063         el.innerHTML = String.format(
40064                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
40065                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
40066         this.el = el;
40067         Roo.menu.Item.superclass.onRender.call(this, container, position);
40068     },
40069
40070     /**
40071      * Sets the text to display in this menu item
40072      * @param {String} text The text to display
40073      * @param {Boolean} isHTML true to indicate text is pure html.
40074      */
40075     setText : function(text, isHTML){
40076         if (isHTML) {
40077             this.html = text;
40078         } else {
40079             this.text = text;
40080             this.html = '';
40081         }
40082         if(this.rendered){
40083             var html = this.html.length ? this.html  : String.format('{0}',this.text);
40084      
40085             this.el.update(String.format(
40086                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
40087                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
40088             this.parentMenu.autoWidth();
40089         }
40090     },
40091
40092     // private
40093     handleClick : function(e){
40094         if(!this.href){ // if no link defined, stop the event automatically
40095             e.stopEvent();
40096         }
40097         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
40098     },
40099
40100     // private
40101     activate : function(autoExpand){
40102         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
40103             this.focus();
40104             if(autoExpand){
40105                 this.expandMenu();
40106             }
40107         }
40108         return true;
40109     },
40110
40111     // private
40112     shouldDeactivate : function(e){
40113         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
40114             if(this.menu && this.menu.isVisible()){
40115                 return !this.menu.getEl().getRegion().contains(e.getPoint());
40116             }
40117             return true;
40118         }
40119         return false;
40120     },
40121
40122     // private
40123     deactivate : function(){
40124         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
40125         this.hideMenu();
40126     },
40127
40128     // private
40129     expandMenu : function(autoActivate){
40130         if(!this.disabled && this.menu){
40131             clearTimeout(this.hideTimer);
40132             delete this.hideTimer;
40133             if(!this.menu.isVisible() && !this.showTimer){
40134                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
40135             }else if (this.menu.isVisible() && autoActivate){
40136                 this.menu.tryActivate(0, 1);
40137             }
40138         }
40139     },
40140
40141     // private
40142     deferExpand : function(autoActivate){
40143         delete this.showTimer;
40144         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
40145         if(autoActivate){
40146             this.menu.tryActivate(0, 1);
40147         }
40148     },
40149
40150     // private
40151     hideMenu : function(){
40152         clearTimeout(this.showTimer);
40153         delete this.showTimer;
40154         if(!this.hideTimer && this.menu && this.menu.isVisible()){
40155             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
40156         }
40157     },
40158
40159     // private
40160     deferHide : function(){
40161         delete this.hideTimer;
40162         this.menu.hide();
40163     }
40164 });/*
40165  * Based on:
40166  * Ext JS Library 1.1.1
40167  * Copyright(c) 2006-2007, Ext JS, LLC.
40168  *
40169  * Originally Released Under LGPL - original licence link has changed is not relivant.
40170  *
40171  * Fork - LGPL
40172  * <script type="text/javascript">
40173  */
40174  
40175 /**
40176  * @class Roo.menu.CheckItem
40177  * @extends Roo.menu.Item
40178  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
40179  * @constructor
40180  * Creates a new CheckItem
40181  * @param {Object} config Configuration options
40182  */
40183 Roo.menu.CheckItem = function(config){
40184     Roo.menu.CheckItem.superclass.constructor.call(this, config);
40185     this.addEvents({
40186         /**
40187          * @event beforecheckchange
40188          * Fires before the checked value is set, providing an opportunity to cancel if needed
40189          * @param {Roo.menu.CheckItem} this
40190          * @param {Boolean} checked The new checked value that will be set
40191          */
40192         "beforecheckchange" : true,
40193         /**
40194          * @event checkchange
40195          * Fires after the checked value has been set
40196          * @param {Roo.menu.CheckItem} this
40197          * @param {Boolean} checked The checked value that was set
40198          */
40199         "checkchange" : true
40200     });
40201     if(this.checkHandler){
40202         this.on('checkchange', this.checkHandler, this.scope);
40203     }
40204 };
40205 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40206     /**
40207      * @cfg {String} group
40208      * All check items with the same group name will automatically be grouped into a single-select
40209      * radio button group (defaults to '')
40210      */
40211     /**
40212      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40213      */
40214     itemCls : "x-menu-item x-menu-check-item",
40215     /**
40216      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40217      */
40218     groupClass : "x-menu-group-item",
40219
40220     /**
40221      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
40222      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40223      * initialized with checked = true will be rendered as checked.
40224      */
40225     checked: false,
40226
40227     // private
40228     ctype: "Roo.menu.CheckItem",
40229
40230     // private
40231     onRender : function(c){
40232         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40233         if(this.group){
40234             this.el.addClass(this.groupClass);
40235         }
40236         Roo.menu.MenuMgr.registerCheckable(this);
40237         if(this.checked){
40238             this.checked = false;
40239             this.setChecked(true, true);
40240         }
40241     },
40242
40243     // private
40244     destroy : function(){
40245         if(this.rendered){
40246             Roo.menu.MenuMgr.unregisterCheckable(this);
40247         }
40248         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40249     },
40250
40251     /**
40252      * Set the checked state of this item
40253      * @param {Boolean} checked The new checked value
40254      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40255      */
40256     setChecked : function(state, suppressEvent){
40257         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40258             if(this.container){
40259                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40260             }
40261             this.checked = state;
40262             if(suppressEvent !== true){
40263                 this.fireEvent("checkchange", this, state);
40264             }
40265         }
40266     },
40267
40268     // private
40269     handleClick : function(e){
40270        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40271            this.setChecked(!this.checked);
40272        }
40273        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40274     }
40275 });/*
40276  * Based on:
40277  * Ext JS Library 1.1.1
40278  * Copyright(c) 2006-2007, Ext JS, LLC.
40279  *
40280  * Originally Released Under LGPL - original licence link has changed is not relivant.
40281  *
40282  * Fork - LGPL
40283  * <script type="text/javascript">
40284  */
40285  
40286 /**
40287  * @class Roo.menu.DateItem
40288  * @extends Roo.menu.Adapter
40289  * A menu item that wraps the {@link Roo.DatPicker} component.
40290  * @constructor
40291  * Creates a new DateItem
40292  * @param {Object} config Configuration options
40293  */
40294 Roo.menu.DateItem = function(config){
40295     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40296     /** The Roo.DatePicker object @type Roo.DatePicker */
40297     this.picker = this.component;
40298     this.addEvents({select: true});
40299     
40300     this.picker.on("render", function(picker){
40301         picker.getEl().swallowEvent("click");
40302         picker.container.addClass("x-menu-date-item");
40303     });
40304
40305     this.picker.on("select", this.onSelect, this);
40306 };
40307
40308 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40309     // private
40310     onSelect : function(picker, date){
40311         this.fireEvent("select", this, date, picker);
40312         Roo.menu.DateItem.superclass.handleClick.call(this);
40313     }
40314 });/*
40315  * Based on:
40316  * Ext JS Library 1.1.1
40317  * Copyright(c) 2006-2007, Ext JS, LLC.
40318  *
40319  * Originally Released Under LGPL - original licence link has changed is not relivant.
40320  *
40321  * Fork - LGPL
40322  * <script type="text/javascript">
40323  */
40324  
40325 /**
40326  * @class Roo.menu.ColorItem
40327  * @extends Roo.menu.Adapter
40328  * A menu item that wraps the {@link Roo.ColorPalette} component.
40329  * @constructor
40330  * Creates a new ColorItem
40331  * @param {Object} config Configuration options
40332  */
40333 Roo.menu.ColorItem = function(config){
40334     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40335     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40336     this.palette = this.component;
40337     this.relayEvents(this.palette, ["select"]);
40338     if(this.selectHandler){
40339         this.on('select', this.selectHandler, this.scope);
40340     }
40341 };
40342 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40343  * Based on:
40344  * Ext JS Library 1.1.1
40345  * Copyright(c) 2006-2007, Ext JS, LLC.
40346  *
40347  * Originally Released Under LGPL - original licence link has changed is not relivant.
40348  *
40349  * Fork - LGPL
40350  * <script type="text/javascript">
40351  */
40352  
40353
40354 /**
40355  * @class Roo.menu.DateMenu
40356  * @extends Roo.menu.Menu
40357  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40358  * @constructor
40359  * Creates a new DateMenu
40360  * @param {Object} config Configuration options
40361  */
40362 Roo.menu.DateMenu = function(config){
40363     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40364     this.plain = true;
40365     var di = new Roo.menu.DateItem(config);
40366     this.add(di);
40367     /**
40368      * The {@link Roo.DatePicker} instance for this DateMenu
40369      * @type DatePicker
40370      */
40371     this.picker = di.picker;
40372     /**
40373      * @event select
40374      * @param {DatePicker} picker
40375      * @param {Date} date
40376      */
40377     this.relayEvents(di, ["select"]);
40378     this.on('beforeshow', function(){
40379         if(this.picker){
40380             this.picker.hideMonthPicker(false);
40381         }
40382     }, this);
40383 };
40384 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40385     cls:'x-date-menu'
40386 });/*
40387  * Based on:
40388  * Ext JS Library 1.1.1
40389  * Copyright(c) 2006-2007, Ext JS, LLC.
40390  *
40391  * Originally Released Under LGPL - original licence link has changed is not relivant.
40392  *
40393  * Fork - LGPL
40394  * <script type="text/javascript">
40395  */
40396  
40397
40398 /**
40399  * @class Roo.menu.ColorMenu
40400  * @extends Roo.menu.Menu
40401  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40402  * @constructor
40403  * Creates a new ColorMenu
40404  * @param {Object} config Configuration options
40405  */
40406 Roo.menu.ColorMenu = function(config){
40407     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40408     this.plain = true;
40409     var ci = new Roo.menu.ColorItem(config);
40410     this.add(ci);
40411     /**
40412      * The {@link Roo.ColorPalette} instance for this ColorMenu
40413      * @type ColorPalette
40414      */
40415     this.palette = ci.palette;
40416     /**
40417      * @event select
40418      * @param {ColorPalette} palette
40419      * @param {String} color
40420      */
40421     this.relayEvents(ci, ["select"]);
40422 };
40423 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40424  * Based on:
40425  * Ext JS Library 1.1.1
40426  * Copyright(c) 2006-2007, Ext JS, LLC.
40427  *
40428  * Originally Released Under LGPL - original licence link has changed is not relivant.
40429  *
40430  * Fork - LGPL
40431  * <script type="text/javascript">
40432  */
40433  
40434 /**
40435  * @class Roo.form.TextItem
40436  * @extends Roo.BoxComponent
40437  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40438  * @constructor
40439  * Creates a new TextItem
40440  * @param {Object} config Configuration options
40441  */
40442 Roo.form.TextItem = function(config){
40443     Roo.form.TextItem.superclass.constructor.call(this, config);
40444 };
40445
40446 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40447     
40448     /**
40449      * @cfg {String} tag the tag for this item (default div)
40450      */
40451     tag : 'div',
40452     /**
40453      * @cfg {String} html the content for this item
40454      */
40455     html : '',
40456     
40457     getAutoCreate : function()
40458     {
40459         var cfg = {
40460             id: this.id,
40461             tag: this.tag,
40462             html: this.html,
40463             cls: 'x-form-item'
40464         };
40465         
40466         return cfg;
40467         
40468     },
40469     
40470     onRender : function(ct, position)
40471     {
40472         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40473         
40474         if(!this.el){
40475             var cfg = this.getAutoCreate();
40476             if(!cfg.name){
40477                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40478             }
40479             if (!cfg.name.length) {
40480                 delete cfg.name;
40481             }
40482             this.el = ct.createChild(cfg, position);
40483         }
40484     },
40485     /*
40486      * setHTML
40487      * @param {String} html update the Contents of the element.
40488      */
40489     setHTML : function(html)
40490     {
40491         this.fieldEl.dom.innerHTML = html;
40492     }
40493     
40494 });/*
40495  * Based on:
40496  * Ext JS Library 1.1.1
40497  * Copyright(c) 2006-2007, Ext JS, LLC.
40498  *
40499  * Originally Released Under LGPL - original licence link has changed is not relivant.
40500  *
40501  * Fork - LGPL
40502  * <script type="text/javascript">
40503  */
40504  
40505 /**
40506  * @class Roo.form.Field
40507  * @extends Roo.BoxComponent
40508  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40509  * @constructor
40510  * Creates a new Field
40511  * @param {Object} config Configuration options
40512  */
40513 Roo.form.Field = function(config){
40514     Roo.form.Field.superclass.constructor.call(this, config);
40515 };
40516
40517 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40518     /**
40519      * @cfg {String} fieldLabel Label to use when rendering a form.
40520      */
40521        /**
40522      * @cfg {String} qtip Mouse over tip
40523      */
40524      
40525     /**
40526      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40527      */
40528     invalidClass : "x-form-invalid",
40529     /**
40530      * @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")
40531      */
40532     invalidText : "The value in this field is invalid",
40533     /**
40534      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40535      */
40536     focusClass : "x-form-focus",
40537     /**
40538      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40539       automatic validation (defaults to "keyup").
40540      */
40541     validationEvent : "keyup",
40542     /**
40543      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40544      */
40545     validateOnBlur : true,
40546     /**
40547      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40548      */
40549     validationDelay : 250,
40550     /**
40551      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40552      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40553      */
40554     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40555     /**
40556      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40557      */
40558     fieldClass : "x-form-field",
40559     /**
40560      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40561      *<pre>
40562 Value         Description
40563 -----------   ----------------------------------------------------------------------
40564 qtip          Display a quick tip when the user hovers over the field
40565 title         Display a default browser title attribute popup
40566 under         Add a block div beneath the field containing the error text
40567 side          Add an error icon to the right of the field with a popup on hover
40568 [element id]  Add the error text directly to the innerHTML of the specified element
40569 </pre>
40570      */
40571     msgTarget : 'qtip',
40572     /**
40573      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40574      */
40575     msgFx : 'normal',
40576
40577     /**
40578      * @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.
40579      */
40580     readOnly : false,
40581
40582     /**
40583      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40584      */
40585     disabled : false,
40586
40587     /**
40588      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40589      */
40590     inputType : undefined,
40591     
40592     /**
40593      * @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).
40594          */
40595         tabIndex : undefined,
40596         
40597     // private
40598     isFormField : true,
40599
40600     // private
40601     hasFocus : false,
40602     /**
40603      * @property {Roo.Element} fieldEl
40604      * Element Containing the rendered Field (with label etc.)
40605      */
40606     /**
40607      * @cfg {Mixed} value A value to initialize this field with.
40608      */
40609     value : undefined,
40610
40611     /**
40612      * @cfg {String} name The field's HTML name attribute.
40613      */
40614     /**
40615      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40616      */
40617     // private
40618     loadedValue : false,
40619      
40620      
40621         // private ??
40622         initComponent : function(){
40623         Roo.form.Field.superclass.initComponent.call(this);
40624         this.addEvents({
40625             /**
40626              * @event focus
40627              * Fires when this field receives input focus.
40628              * @param {Roo.form.Field} this
40629              */
40630             focus : true,
40631             /**
40632              * @event blur
40633              * Fires when this field loses input focus.
40634              * @param {Roo.form.Field} this
40635              */
40636             blur : true,
40637             /**
40638              * @event specialkey
40639              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40640              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40641              * @param {Roo.form.Field} this
40642              * @param {Roo.EventObject} e The event object
40643              */
40644             specialkey : true,
40645             /**
40646              * @event change
40647              * Fires just before the field blurs if the field value has changed.
40648              * @param {Roo.form.Field} this
40649              * @param {Mixed} newValue The new value
40650              * @param {Mixed} oldValue The original value
40651              */
40652             change : true,
40653             /**
40654              * @event invalid
40655              * Fires after the field has been marked as invalid.
40656              * @param {Roo.form.Field} this
40657              * @param {String} msg The validation message
40658              */
40659             invalid : true,
40660             /**
40661              * @event valid
40662              * Fires after the field has been validated with no errors.
40663              * @param {Roo.form.Field} this
40664              */
40665             valid : true,
40666              /**
40667              * @event keyup
40668              * Fires after the key up
40669              * @param {Roo.form.Field} this
40670              * @param {Roo.EventObject}  e The event Object
40671              */
40672             keyup : true
40673         });
40674     },
40675
40676     /**
40677      * Returns the name attribute of the field if available
40678      * @return {String} name The field name
40679      */
40680     getName: function(){
40681          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40682     },
40683
40684     // private
40685     onRender : function(ct, position){
40686         Roo.form.Field.superclass.onRender.call(this, ct, position);
40687         if(!this.el){
40688             var cfg = this.getAutoCreate();
40689             if(!cfg.name){
40690                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40691             }
40692             if (!cfg.name.length) {
40693                 delete cfg.name;
40694             }
40695             if(this.inputType){
40696                 cfg.type = this.inputType;
40697             }
40698             this.el = ct.createChild(cfg, position);
40699         }
40700         var type = this.el.dom.type;
40701         if(type){
40702             if(type == 'password'){
40703                 type = 'text';
40704             }
40705             this.el.addClass('x-form-'+type);
40706         }
40707         if(this.readOnly){
40708             this.el.dom.readOnly = true;
40709         }
40710         if(this.tabIndex !== undefined){
40711             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40712         }
40713
40714         this.el.addClass([this.fieldClass, this.cls]);
40715         this.initValue();
40716     },
40717
40718     /**
40719      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40720      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40721      * @return {Roo.form.Field} this
40722      */
40723     applyTo : function(target){
40724         this.allowDomMove = false;
40725         this.el = Roo.get(target);
40726         this.render(this.el.dom.parentNode);
40727         return this;
40728     },
40729
40730     // private
40731     initValue : function(){
40732         if(this.value !== undefined){
40733             this.setValue(this.value);
40734         }else if(this.el.dom.value.length > 0){
40735             this.setValue(this.el.dom.value);
40736         }
40737     },
40738
40739     /**
40740      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40741      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40742      */
40743     isDirty : function() {
40744         if(this.disabled) {
40745             return false;
40746         }
40747         return String(this.getValue()) !== String(this.originalValue);
40748     },
40749
40750     /**
40751      * stores the current value in loadedValue
40752      */
40753     resetHasChanged : function()
40754     {
40755         this.loadedValue = String(this.getValue());
40756     },
40757     /**
40758      * checks the current value against the 'loaded' value.
40759      * Note - will return false if 'resetHasChanged' has not been called first.
40760      */
40761     hasChanged : function()
40762     {
40763         if(this.disabled || this.readOnly) {
40764             return false;
40765         }
40766         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40767     },
40768     
40769     
40770     
40771     // private
40772     afterRender : function(){
40773         Roo.form.Field.superclass.afterRender.call(this);
40774         this.initEvents();
40775     },
40776
40777     // private
40778     fireKey : function(e){
40779         //Roo.log('field ' + e.getKey());
40780         if(e.isNavKeyPress()){
40781             this.fireEvent("specialkey", this, e);
40782         }
40783     },
40784
40785     /**
40786      * Resets the current field value to the originally loaded value and clears any validation messages
40787      */
40788     reset : function(){
40789         this.setValue(this.resetValue);
40790         this.originalValue = this.getValue();
40791         this.clearInvalid();
40792     },
40793
40794     // private
40795     initEvents : function(){
40796         // safari killled keypress - so keydown is now used..
40797         this.el.on("keydown" , this.fireKey,  this);
40798         this.el.on("focus", this.onFocus,  this);
40799         this.el.on("blur", this.onBlur,  this);
40800         this.el.relayEvent('keyup', this);
40801
40802         // reference to original value for reset
40803         this.originalValue = this.getValue();
40804         this.resetValue =  this.getValue();
40805     },
40806
40807     // private
40808     onFocus : function(){
40809         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40810             this.el.addClass(this.focusClass);
40811         }
40812         if(!this.hasFocus){
40813             this.hasFocus = true;
40814             this.startValue = this.getValue();
40815             this.fireEvent("focus", this);
40816         }
40817     },
40818
40819     beforeBlur : Roo.emptyFn,
40820
40821     // private
40822     onBlur : function(){
40823         this.beforeBlur();
40824         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40825             this.el.removeClass(this.focusClass);
40826         }
40827         this.hasFocus = false;
40828         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40829             this.validate();
40830         }
40831         var v = this.getValue();
40832         if(String(v) !== String(this.startValue)){
40833             this.fireEvent('change', this, v, this.startValue);
40834         }
40835         this.fireEvent("blur", this);
40836     },
40837
40838     /**
40839      * Returns whether or not the field value is currently valid
40840      * @param {Boolean} preventMark True to disable marking the field invalid
40841      * @return {Boolean} True if the value is valid, else false
40842      */
40843     isValid : function(preventMark){
40844         if(this.disabled){
40845             return true;
40846         }
40847         var restore = this.preventMark;
40848         this.preventMark = preventMark === true;
40849         var v = this.validateValue(this.processValue(this.getRawValue()));
40850         this.preventMark = restore;
40851         return v;
40852     },
40853
40854     /**
40855      * Validates the field value
40856      * @return {Boolean} True if the value is valid, else false
40857      */
40858     validate : function(){
40859         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40860             this.clearInvalid();
40861             return true;
40862         }
40863         return false;
40864     },
40865
40866     processValue : function(value){
40867         return value;
40868     },
40869
40870     // private
40871     // Subclasses should provide the validation implementation by overriding this
40872     validateValue : function(value){
40873         return true;
40874     },
40875
40876     /**
40877      * Mark this field as invalid
40878      * @param {String} msg The validation message
40879      */
40880     markInvalid : function(msg){
40881         if(!this.rendered || this.preventMark){ // not rendered
40882             return;
40883         }
40884         
40885         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40886         
40887         obj.el.addClass(this.invalidClass);
40888         msg = msg || this.invalidText;
40889         switch(this.msgTarget){
40890             case 'qtip':
40891                 obj.el.dom.qtip = msg;
40892                 obj.el.dom.qclass = 'x-form-invalid-tip';
40893                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40894                     Roo.QuickTips.enable();
40895                 }
40896                 break;
40897             case 'title':
40898                 this.el.dom.title = msg;
40899                 break;
40900             case 'under':
40901                 if(!this.errorEl){
40902                     var elp = this.el.findParent('.x-form-element', 5, true);
40903                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40904                     this.errorEl.setWidth(elp.getWidth(true)-20);
40905                 }
40906                 this.errorEl.update(msg);
40907                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40908                 break;
40909             case 'side':
40910                 if(!this.errorIcon){
40911                     var elp = this.el.findParent('.x-form-element', 5, true);
40912                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40913                 }
40914                 this.alignErrorIcon();
40915                 this.errorIcon.dom.qtip = msg;
40916                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40917                 this.errorIcon.show();
40918                 this.on('resize', this.alignErrorIcon, this);
40919                 break;
40920             default:
40921                 var t = Roo.getDom(this.msgTarget);
40922                 t.innerHTML = msg;
40923                 t.style.display = this.msgDisplay;
40924                 break;
40925         }
40926         this.fireEvent('invalid', this, msg);
40927     },
40928
40929     // private
40930     alignErrorIcon : function(){
40931         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40932     },
40933
40934     /**
40935      * Clear any invalid styles/messages for this field
40936      */
40937     clearInvalid : function(){
40938         if(!this.rendered || this.preventMark){ // not rendered
40939             return;
40940         }
40941         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40942         
40943         obj.el.removeClass(this.invalidClass);
40944         switch(this.msgTarget){
40945             case 'qtip':
40946                 obj.el.dom.qtip = '';
40947                 break;
40948             case 'title':
40949                 this.el.dom.title = '';
40950                 break;
40951             case 'under':
40952                 if(this.errorEl){
40953                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40954                 }
40955                 break;
40956             case 'side':
40957                 if(this.errorIcon){
40958                     this.errorIcon.dom.qtip = '';
40959                     this.errorIcon.hide();
40960                     this.un('resize', this.alignErrorIcon, this);
40961                 }
40962                 break;
40963             default:
40964                 var t = Roo.getDom(this.msgTarget);
40965                 t.innerHTML = '';
40966                 t.style.display = 'none';
40967                 break;
40968         }
40969         this.fireEvent('valid', this);
40970     },
40971
40972     /**
40973      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40974      * @return {Mixed} value The field value
40975      */
40976     getRawValue : function(){
40977         var v = this.el.getValue();
40978         
40979         return v;
40980     },
40981
40982     /**
40983      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40984      * @return {Mixed} value The field value
40985      */
40986     getValue : function(){
40987         var v = this.el.getValue();
40988          
40989         return v;
40990     },
40991
40992     /**
40993      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40994      * @param {Mixed} value The value to set
40995      */
40996     setRawValue : function(v){
40997         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40998     },
40999
41000     /**
41001      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41002      * @param {Mixed} value The value to set
41003      */
41004     setValue : function(v){
41005         this.value = v;
41006         if(this.rendered){
41007             this.el.dom.value = (v === null || v === undefined ? '' : v);
41008              this.validate();
41009         }
41010     },
41011
41012     adjustSize : function(w, h){
41013         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
41014         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
41015         return s;
41016     },
41017
41018     adjustWidth : function(tag, w){
41019         tag = tag.toLowerCase();
41020         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
41021             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
41022                 if(tag == 'input'){
41023                     return w + 2;
41024                 }
41025                 if(tag == 'textarea'){
41026                     return w-2;
41027                 }
41028             }else if(Roo.isOpera){
41029                 if(tag == 'input'){
41030                     return w + 2;
41031                 }
41032                 if(tag == 'textarea'){
41033                     return w-2;
41034                 }
41035             }
41036         }
41037         return w;
41038     }
41039 });
41040
41041
41042 // anything other than normal should be considered experimental
41043 Roo.form.Field.msgFx = {
41044     normal : {
41045         show: function(msgEl, f){
41046             msgEl.setDisplayed('block');
41047         },
41048
41049         hide : function(msgEl, f){
41050             msgEl.setDisplayed(false).update('');
41051         }
41052     },
41053
41054     slide : {
41055         show: function(msgEl, f){
41056             msgEl.slideIn('t', {stopFx:true});
41057         },
41058
41059         hide : function(msgEl, f){
41060             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
41061         }
41062     },
41063
41064     slideRight : {
41065         show: function(msgEl, f){
41066             msgEl.fixDisplay();
41067             msgEl.alignTo(f.el, 'tl-tr');
41068             msgEl.slideIn('l', {stopFx:true});
41069         },
41070
41071         hide : function(msgEl, f){
41072             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
41073         }
41074     }
41075 };/*
41076  * Based on:
41077  * Ext JS Library 1.1.1
41078  * Copyright(c) 2006-2007, Ext JS, LLC.
41079  *
41080  * Originally Released Under LGPL - original licence link has changed is not relivant.
41081  *
41082  * Fork - LGPL
41083  * <script type="text/javascript">
41084  */
41085  
41086
41087 /**
41088  * @class Roo.form.TextField
41089  * @extends Roo.form.Field
41090  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
41091  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
41092  * @constructor
41093  * Creates a new TextField
41094  * @param {Object} config Configuration options
41095  */
41096 Roo.form.TextField = function(config){
41097     Roo.form.TextField.superclass.constructor.call(this, config);
41098     this.addEvents({
41099         /**
41100          * @event autosize
41101          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
41102          * according to the default logic, but this event provides a hook for the developer to apply additional
41103          * logic at runtime to resize the field if needed.
41104              * @param {Roo.form.Field} this This text field
41105              * @param {Number} width The new field width
41106              */
41107         autosize : true
41108     });
41109 };
41110
41111 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
41112     /**
41113      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
41114      */
41115     grow : false,
41116     /**
41117      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
41118      */
41119     growMin : 30,
41120     /**
41121      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
41122      */
41123     growMax : 800,
41124     /**
41125      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
41126      */
41127     vtype : null,
41128     /**
41129      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
41130      */
41131     maskRe : null,
41132     /**
41133      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
41134      */
41135     disableKeyFilter : false,
41136     /**
41137      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
41138      */
41139     allowBlank : true,
41140     /**
41141      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
41142      */
41143     minLength : 0,
41144     /**
41145      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
41146      */
41147     maxLength : Number.MAX_VALUE,
41148     /**
41149      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
41150      */
41151     minLengthText : "The minimum length for this field is {0}",
41152     /**
41153      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
41154      */
41155     maxLengthText : "The maximum length for this field is {0}",
41156     /**
41157      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
41158      */
41159     selectOnFocus : false,
41160     /**
41161      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
41162      */    
41163     allowLeadingSpace : false,
41164     /**
41165      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
41166      */
41167     blankText : "This field is required",
41168     /**
41169      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
41170      * If available, this function will be called only after the basic validators all return true, and will be passed the
41171      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
41172      */
41173     validator : null,
41174     /**
41175      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
41176      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
41177      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
41178      */
41179     regex : null,
41180     /**
41181      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
41182      */
41183     regexText : "",
41184     /**
41185      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
41186      */
41187     emptyText : null,
41188    
41189
41190     // private
41191     initEvents : function()
41192     {
41193         if (this.emptyText) {
41194             this.el.attr('placeholder', this.emptyText);
41195         }
41196         
41197         Roo.form.TextField.superclass.initEvents.call(this);
41198         if(this.validationEvent == 'keyup'){
41199             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41200             this.el.on('keyup', this.filterValidation, this);
41201         }
41202         else if(this.validationEvent !== false){
41203             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41204         }
41205         
41206         if(this.selectOnFocus){
41207             this.on("focus", this.preFocus, this);
41208         }
41209         if (!this.allowLeadingSpace) {
41210             this.on('blur', this.cleanLeadingSpace, this);
41211         }
41212         
41213         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41214             this.el.on("keypress", this.filterKeys, this);
41215         }
41216         if(this.grow){
41217             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
41218             this.el.on("click", this.autoSize,  this);
41219         }
41220         if(this.el.is('input[type=password]') && Roo.isSafari){
41221             this.el.on('keydown', this.SafariOnKeyDown, this);
41222         }
41223     },
41224
41225     processValue : function(value){
41226         if(this.stripCharsRe){
41227             var newValue = value.replace(this.stripCharsRe, '');
41228             if(newValue !== value){
41229                 this.setRawValue(newValue);
41230                 return newValue;
41231             }
41232         }
41233         return value;
41234     },
41235
41236     filterValidation : function(e){
41237         if(!e.isNavKeyPress()){
41238             this.validationTask.delay(this.validationDelay);
41239         }
41240     },
41241
41242     // private
41243     onKeyUp : function(e){
41244         if(!e.isNavKeyPress()){
41245             this.autoSize();
41246         }
41247     },
41248     // private - clean the leading white space
41249     cleanLeadingSpace : function(e)
41250     {
41251         if ( this.inputType == 'file') {
41252             return;
41253         }
41254         
41255         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41256     },
41257     /**
41258      * Resets the current field value to the originally-loaded value and clears any validation messages.
41259      *  
41260      */
41261     reset : function(){
41262         Roo.form.TextField.superclass.reset.call(this);
41263        
41264     }, 
41265     // private
41266     preFocus : function(){
41267         
41268         if(this.selectOnFocus){
41269             this.el.dom.select();
41270         }
41271     },
41272
41273     
41274     // private
41275     filterKeys : function(e){
41276         var k = e.getKey();
41277         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41278             return;
41279         }
41280         var c = e.getCharCode(), cc = String.fromCharCode(c);
41281         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41282             return;
41283         }
41284         if(!this.maskRe.test(cc)){
41285             e.stopEvent();
41286         }
41287     },
41288
41289     setValue : function(v){
41290         
41291         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41292         
41293         this.autoSize();
41294     },
41295
41296     /**
41297      * Validates a value according to the field's validation rules and marks the field as invalid
41298      * if the validation fails
41299      * @param {Mixed} value The value to validate
41300      * @return {Boolean} True if the value is valid, else false
41301      */
41302     validateValue : function(value){
41303         if(value.length < 1)  { // if it's blank
41304              if(this.allowBlank){
41305                 this.clearInvalid();
41306                 return true;
41307              }else{
41308                 this.markInvalid(this.blankText);
41309                 return false;
41310              }
41311         }
41312         if(value.length < this.minLength){
41313             this.markInvalid(String.format(this.minLengthText, this.minLength));
41314             return false;
41315         }
41316         if(value.length > this.maxLength){
41317             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41318             return false;
41319         }
41320         if(this.vtype){
41321             var vt = Roo.form.VTypes;
41322             if(!vt[this.vtype](value, this)){
41323                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41324                 return false;
41325             }
41326         }
41327         if(typeof this.validator == "function"){
41328             var msg = this.validator(value);
41329             if(msg !== true){
41330                 this.markInvalid(msg);
41331                 return false;
41332             }
41333         }
41334         if(this.regex && !this.regex.test(value)){
41335             this.markInvalid(this.regexText);
41336             return false;
41337         }
41338         return true;
41339     },
41340
41341     /**
41342      * Selects text in this field
41343      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41344      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41345      */
41346     selectText : function(start, end){
41347         var v = this.getRawValue();
41348         if(v.length > 0){
41349             start = start === undefined ? 0 : start;
41350             end = end === undefined ? v.length : end;
41351             var d = this.el.dom;
41352             if(d.setSelectionRange){
41353                 d.setSelectionRange(start, end);
41354             }else if(d.createTextRange){
41355                 var range = d.createTextRange();
41356                 range.moveStart("character", start);
41357                 range.moveEnd("character", v.length-end);
41358                 range.select();
41359             }
41360         }
41361     },
41362
41363     /**
41364      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41365      * This only takes effect if grow = true, and fires the autosize event.
41366      */
41367     autoSize : function(){
41368         if(!this.grow || !this.rendered){
41369             return;
41370         }
41371         if(!this.metrics){
41372             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41373         }
41374         var el = this.el;
41375         var v = el.dom.value;
41376         var d = document.createElement('div');
41377         d.appendChild(document.createTextNode(v));
41378         v = d.innerHTML;
41379         d = null;
41380         v += "&#160;";
41381         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41382         this.el.setWidth(w);
41383         this.fireEvent("autosize", this, w);
41384     },
41385     
41386     // private
41387     SafariOnKeyDown : function(event)
41388     {
41389         // this is a workaround for a password hang bug on chrome/ webkit.
41390         
41391         var isSelectAll = false;
41392         
41393         if(this.el.dom.selectionEnd > 0){
41394             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41395         }
41396         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41397             event.preventDefault();
41398             this.setValue('');
41399             return;
41400         }
41401         
41402         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41403             
41404             event.preventDefault();
41405             // this is very hacky as keydown always get's upper case.
41406             
41407             var cc = String.fromCharCode(event.getCharCode());
41408             
41409             
41410             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41411             
41412         }
41413         
41414         
41415     }
41416 });/*
41417  * Based on:
41418  * Ext JS Library 1.1.1
41419  * Copyright(c) 2006-2007, Ext JS, LLC.
41420  *
41421  * Originally Released Under LGPL - original licence link has changed is not relivant.
41422  *
41423  * Fork - LGPL
41424  * <script type="text/javascript">
41425  */
41426  
41427 /**
41428  * @class Roo.form.Hidden
41429  * @extends Roo.form.TextField
41430  * Simple Hidden element used on forms 
41431  * 
41432  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41433  * 
41434  * @constructor
41435  * Creates a new Hidden form element.
41436  * @param {Object} config Configuration options
41437  */
41438
41439
41440
41441 // easy hidden field...
41442 Roo.form.Hidden = function(config){
41443     Roo.form.Hidden.superclass.constructor.call(this, config);
41444 };
41445   
41446 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41447     fieldLabel:      '',
41448     inputType:      'hidden',
41449     width:          50,
41450     allowBlank:     true,
41451     labelSeparator: '',
41452     hidden:         true,
41453     itemCls :       'x-form-item-display-none'
41454
41455
41456 });
41457
41458
41459 /*
41460  * Based on:
41461  * Ext JS Library 1.1.1
41462  * Copyright(c) 2006-2007, Ext JS, LLC.
41463  *
41464  * Originally Released Under LGPL - original licence link has changed is not relivant.
41465  *
41466  * Fork - LGPL
41467  * <script type="text/javascript">
41468  */
41469  
41470 /**
41471  * @class Roo.form.TriggerField
41472  * @extends Roo.form.TextField
41473  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41474  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41475  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41476  * for which you can provide a custom implementation.  For example:
41477  * <pre><code>
41478 var trigger = new Roo.form.TriggerField();
41479 trigger.onTriggerClick = myTriggerFn;
41480 trigger.applyTo('my-field');
41481 </code></pre>
41482  *
41483  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41484  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41485  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41486  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41487  * @constructor
41488  * Create a new TriggerField.
41489  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41490  * to the base TextField)
41491  */
41492 Roo.form.TriggerField = function(config){
41493     this.mimicing = false;
41494     Roo.form.TriggerField.superclass.constructor.call(this, config);
41495 };
41496
41497 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41498     /**
41499      * @cfg {String} triggerClass A CSS class to apply to the trigger
41500      */
41501     /**
41502      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41503      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41504      */
41505     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41506     /**
41507      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41508      */
41509     hideTrigger:false,
41510
41511     /** @cfg {Boolean} grow @hide */
41512     /** @cfg {Number} growMin @hide */
41513     /** @cfg {Number} growMax @hide */
41514
41515     /**
41516      * @hide 
41517      * @method
41518      */
41519     autoSize: Roo.emptyFn,
41520     // private
41521     monitorTab : true,
41522     // private
41523     deferHeight : true,
41524
41525     
41526     actionMode : 'wrap',
41527     // private
41528     onResize : function(w, h){
41529         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41530         if(typeof w == 'number'){
41531             var x = w - this.trigger.getWidth();
41532             this.el.setWidth(this.adjustWidth('input', x));
41533             this.trigger.setStyle('left', x+'px');
41534         }
41535     },
41536
41537     // private
41538     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41539
41540     // private
41541     getResizeEl : function(){
41542         return this.wrap;
41543     },
41544
41545     // private
41546     getPositionEl : function(){
41547         return this.wrap;
41548     },
41549
41550     // private
41551     alignErrorIcon : function(){
41552         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41553     },
41554
41555     // private
41556     onRender : function(ct, position){
41557         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41558         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41559         this.trigger = this.wrap.createChild(this.triggerConfig ||
41560                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41561         if(this.hideTrigger){
41562             this.trigger.setDisplayed(false);
41563         }
41564         this.initTrigger();
41565         if(!this.width){
41566             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41567         }
41568     },
41569
41570     // private
41571     initTrigger : function(){
41572         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41573         this.trigger.addClassOnOver('x-form-trigger-over');
41574         this.trigger.addClassOnClick('x-form-trigger-click');
41575     },
41576
41577     // private
41578     onDestroy : function(){
41579         if(this.trigger){
41580             this.trigger.removeAllListeners();
41581             this.trigger.remove();
41582         }
41583         if(this.wrap){
41584             this.wrap.remove();
41585         }
41586         Roo.form.TriggerField.superclass.onDestroy.call(this);
41587     },
41588
41589     // private
41590     onFocus : function(){
41591         Roo.form.TriggerField.superclass.onFocus.call(this);
41592         if(!this.mimicing){
41593             this.wrap.addClass('x-trigger-wrap-focus');
41594             this.mimicing = true;
41595             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41596             if(this.monitorTab){
41597                 this.el.on("keydown", this.checkTab, this);
41598             }
41599         }
41600     },
41601
41602     // private
41603     checkTab : function(e){
41604         if(e.getKey() == e.TAB){
41605             this.triggerBlur();
41606         }
41607     },
41608
41609     // private
41610     onBlur : function(){
41611         // do nothing
41612     },
41613
41614     // private
41615     mimicBlur : function(e, t){
41616         if(!this.wrap.contains(t) && this.validateBlur()){
41617             this.triggerBlur();
41618         }
41619     },
41620
41621     // private
41622     triggerBlur : function(){
41623         this.mimicing = false;
41624         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41625         if(this.monitorTab){
41626             this.el.un("keydown", this.checkTab, this);
41627         }
41628         this.wrap.removeClass('x-trigger-wrap-focus');
41629         Roo.form.TriggerField.superclass.onBlur.call(this);
41630     },
41631
41632     // private
41633     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41634     validateBlur : function(e, t){
41635         return true;
41636     },
41637
41638     // private
41639     onDisable : function(){
41640         Roo.form.TriggerField.superclass.onDisable.call(this);
41641         if(this.wrap){
41642             this.wrap.addClass('x-item-disabled');
41643         }
41644     },
41645
41646     // private
41647     onEnable : function(){
41648         Roo.form.TriggerField.superclass.onEnable.call(this);
41649         if(this.wrap){
41650             this.wrap.removeClass('x-item-disabled');
41651         }
41652     },
41653
41654     // private
41655     onShow : function(){
41656         var ae = this.getActionEl();
41657         
41658         if(ae){
41659             ae.dom.style.display = '';
41660             ae.dom.style.visibility = 'visible';
41661         }
41662     },
41663
41664     // private
41665     
41666     onHide : function(){
41667         var ae = this.getActionEl();
41668         ae.dom.style.display = 'none';
41669     },
41670
41671     /**
41672      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41673      * by an implementing function.
41674      * @method
41675      * @param {EventObject} e
41676      */
41677     onTriggerClick : Roo.emptyFn
41678 });
41679
41680 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41681 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41682 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41683 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41684     initComponent : function(){
41685         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41686
41687         this.triggerConfig = {
41688             tag:'span', cls:'x-form-twin-triggers', cn:[
41689             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41690             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41691         ]};
41692     },
41693
41694     getTrigger : function(index){
41695         return this.triggers[index];
41696     },
41697
41698     initTrigger : function(){
41699         var ts = this.trigger.select('.x-form-trigger', true);
41700         this.wrap.setStyle('overflow', 'hidden');
41701         var triggerField = this;
41702         ts.each(function(t, all, index){
41703             t.hide = function(){
41704                 var w = triggerField.wrap.getWidth();
41705                 this.dom.style.display = 'none';
41706                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41707             };
41708             t.show = function(){
41709                 var w = triggerField.wrap.getWidth();
41710                 this.dom.style.display = '';
41711                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41712             };
41713             var triggerIndex = 'Trigger'+(index+1);
41714
41715             if(this['hide'+triggerIndex]){
41716                 t.dom.style.display = 'none';
41717             }
41718             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41719             t.addClassOnOver('x-form-trigger-over');
41720             t.addClassOnClick('x-form-trigger-click');
41721         }, this);
41722         this.triggers = ts.elements;
41723     },
41724
41725     onTrigger1Click : Roo.emptyFn,
41726     onTrigger2Click : Roo.emptyFn
41727 });/*
41728  * Based on:
41729  * Ext JS Library 1.1.1
41730  * Copyright(c) 2006-2007, Ext JS, LLC.
41731  *
41732  * Originally Released Under LGPL - original licence link has changed is not relivant.
41733  *
41734  * Fork - LGPL
41735  * <script type="text/javascript">
41736  */
41737  
41738 /**
41739  * @class Roo.form.TextArea
41740  * @extends Roo.form.TextField
41741  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41742  * support for auto-sizing.
41743  * @constructor
41744  * Creates a new TextArea
41745  * @param {Object} config Configuration options
41746  */
41747 Roo.form.TextArea = function(config){
41748     Roo.form.TextArea.superclass.constructor.call(this, config);
41749     // these are provided exchanges for backwards compat
41750     // minHeight/maxHeight were replaced by growMin/growMax to be
41751     // compatible with TextField growing config values
41752     if(this.minHeight !== undefined){
41753         this.growMin = this.minHeight;
41754     }
41755     if(this.maxHeight !== undefined){
41756         this.growMax = this.maxHeight;
41757     }
41758 };
41759
41760 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41761     /**
41762      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41763      */
41764     growMin : 60,
41765     /**
41766      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41767      */
41768     growMax: 1000,
41769     /**
41770      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41771      * in the field (equivalent to setting overflow: hidden, defaults to false)
41772      */
41773     preventScrollbars: false,
41774     /**
41775      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41776      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41777      */
41778
41779     // private
41780     onRender : function(ct, position){
41781         if(!this.el){
41782             this.defaultAutoCreate = {
41783                 tag: "textarea",
41784                 style:"width:300px;height:60px;",
41785                 autocomplete: "new-password"
41786             };
41787         }
41788         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41789         if(this.grow){
41790             this.textSizeEl = Roo.DomHelper.append(document.body, {
41791                 tag: "pre", cls: "x-form-grow-sizer"
41792             });
41793             if(this.preventScrollbars){
41794                 this.el.setStyle("overflow", "hidden");
41795             }
41796             this.el.setHeight(this.growMin);
41797         }
41798     },
41799
41800     onDestroy : function(){
41801         if(this.textSizeEl){
41802             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41803         }
41804         Roo.form.TextArea.superclass.onDestroy.call(this);
41805     },
41806
41807     // private
41808     onKeyUp : function(e){
41809         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41810             this.autoSize();
41811         }
41812     },
41813
41814     /**
41815      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41816      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41817      */
41818     autoSize : function(){
41819         if(!this.grow || !this.textSizeEl){
41820             return;
41821         }
41822         var el = this.el;
41823         var v = el.dom.value;
41824         var ts = this.textSizeEl;
41825
41826         ts.innerHTML = '';
41827         ts.appendChild(document.createTextNode(v));
41828         v = ts.innerHTML;
41829
41830         Roo.fly(ts).setWidth(this.el.getWidth());
41831         if(v.length < 1){
41832             v = "&#160;&#160;";
41833         }else{
41834             if(Roo.isIE){
41835                 v = v.replace(/\n/g, '<p>&#160;</p>');
41836             }
41837             v += "&#160;\n&#160;";
41838         }
41839         ts.innerHTML = v;
41840         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41841         if(h != this.lastHeight){
41842             this.lastHeight = h;
41843             this.el.setHeight(h);
41844             this.fireEvent("autosize", this, h);
41845         }
41846     }
41847 });/*
41848  * Based on:
41849  * Ext JS Library 1.1.1
41850  * Copyright(c) 2006-2007, Ext JS, LLC.
41851  *
41852  * Originally Released Under LGPL - original licence link has changed is not relivant.
41853  *
41854  * Fork - LGPL
41855  * <script type="text/javascript">
41856  */
41857  
41858
41859 /**
41860  * @class Roo.form.NumberField
41861  * @extends Roo.form.TextField
41862  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41863  * @constructor
41864  * Creates a new NumberField
41865  * @param {Object} config Configuration options
41866  */
41867 Roo.form.NumberField = function(config){
41868     Roo.form.NumberField.superclass.constructor.call(this, config);
41869 };
41870
41871 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41872     /**
41873      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41874      */
41875     fieldClass: "x-form-field x-form-num-field",
41876     /**
41877      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41878      */
41879     allowDecimals : true,
41880     /**
41881      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41882      */
41883     decimalSeparator : ".",
41884     /**
41885      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41886      */
41887     decimalPrecision : 2,
41888     /**
41889      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41890      */
41891     allowNegative : true,
41892     /**
41893      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41894      */
41895     minValue : Number.NEGATIVE_INFINITY,
41896     /**
41897      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41898      */
41899     maxValue : Number.MAX_VALUE,
41900     /**
41901      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41902      */
41903     minText : "The minimum value for this field is {0}",
41904     /**
41905      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41906      */
41907     maxText : "The maximum value for this field is {0}",
41908     /**
41909      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41910      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41911      */
41912     nanText : "{0} is not a valid number",
41913
41914     // private
41915     initEvents : function(){
41916         Roo.form.NumberField.superclass.initEvents.call(this);
41917         var allowed = "0123456789";
41918         if(this.allowDecimals){
41919             allowed += this.decimalSeparator;
41920         }
41921         if(this.allowNegative){
41922             allowed += "-";
41923         }
41924         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41925         var keyPress = function(e){
41926             var k = e.getKey();
41927             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41928                 return;
41929             }
41930             var c = e.getCharCode();
41931             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41932                 e.stopEvent();
41933             }
41934         };
41935         this.el.on("keypress", keyPress, this);
41936     },
41937
41938     // private
41939     validateValue : function(value){
41940         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41941             return false;
41942         }
41943         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41944              return true;
41945         }
41946         var num = this.parseValue(value);
41947         if(isNaN(num)){
41948             this.markInvalid(String.format(this.nanText, value));
41949             return false;
41950         }
41951         if(num < this.minValue){
41952             this.markInvalid(String.format(this.minText, this.minValue));
41953             return false;
41954         }
41955         if(num > this.maxValue){
41956             this.markInvalid(String.format(this.maxText, this.maxValue));
41957             return false;
41958         }
41959         return true;
41960     },
41961
41962     getValue : function(){
41963         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41964     },
41965
41966     // private
41967     parseValue : function(value){
41968         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41969         return isNaN(value) ? '' : value;
41970     },
41971
41972     // private
41973     fixPrecision : function(value){
41974         var nan = isNaN(value);
41975         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41976             return nan ? '' : value;
41977         }
41978         return parseFloat(value).toFixed(this.decimalPrecision);
41979     },
41980
41981     setValue : function(v){
41982         v = this.fixPrecision(v);
41983         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41984     },
41985
41986     // private
41987     decimalPrecisionFcn : function(v){
41988         return Math.floor(v);
41989     },
41990
41991     beforeBlur : function(){
41992         var v = this.parseValue(this.getRawValue());
41993         if(v){
41994             this.setValue(v);
41995         }
41996     }
41997 });/*
41998  * Based on:
41999  * Ext JS Library 1.1.1
42000  * Copyright(c) 2006-2007, Ext JS, LLC.
42001  *
42002  * Originally Released Under LGPL - original licence link has changed is not relivant.
42003  *
42004  * Fork - LGPL
42005  * <script type="text/javascript">
42006  */
42007  
42008 /**
42009  * @class Roo.form.DateField
42010  * @extends Roo.form.TriggerField
42011  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42012 * @constructor
42013 * Create a new DateField
42014 * @param {Object} config
42015  */
42016 Roo.form.DateField = function(config)
42017 {
42018     Roo.form.DateField.superclass.constructor.call(this, config);
42019     
42020       this.addEvents({
42021          
42022         /**
42023          * @event select
42024          * Fires when a date is selected
42025              * @param {Roo.form.DateField} combo This combo box
42026              * @param {Date} date The date selected
42027              */
42028         'select' : true
42029          
42030     });
42031     
42032     
42033     if(typeof this.minValue == "string") {
42034         this.minValue = this.parseDate(this.minValue);
42035     }
42036     if(typeof this.maxValue == "string") {
42037         this.maxValue = this.parseDate(this.maxValue);
42038     }
42039     this.ddMatch = null;
42040     if(this.disabledDates){
42041         var dd = this.disabledDates;
42042         var re = "(?:";
42043         for(var i = 0; i < dd.length; i++){
42044             re += dd[i];
42045             if(i != dd.length-1) {
42046                 re += "|";
42047             }
42048         }
42049         this.ddMatch = new RegExp(re + ")");
42050     }
42051 };
42052
42053 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
42054     /**
42055      * @cfg {String} format
42056      * The default date format string which can be overriden for localization support.  The format must be
42057      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42058      */
42059     format : "m/d/y",
42060     /**
42061      * @cfg {String} altFormats
42062      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42063      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42064      */
42065     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
42066     /**
42067      * @cfg {Array} disabledDays
42068      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42069      */
42070     disabledDays : null,
42071     /**
42072      * @cfg {String} disabledDaysText
42073      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42074      */
42075     disabledDaysText : "Disabled",
42076     /**
42077      * @cfg {Array} disabledDates
42078      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42079      * expression so they are very powerful. Some examples:
42080      * <ul>
42081      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42082      * <li>["03/08", "09/16"] would disable those days for every year</li>
42083      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42084      * <li>["03/../2006"] would disable every day in March 2006</li>
42085      * <li>["^03"] would disable every day in every March</li>
42086      * </ul>
42087      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42088      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42089      */
42090     disabledDates : null,
42091     /**
42092      * @cfg {String} disabledDatesText
42093      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42094      */
42095     disabledDatesText : "Disabled",
42096         
42097         
42098         /**
42099      * @cfg {Date/String} zeroValue
42100      * if the date is less that this number, then the field is rendered as empty
42101      * default is 1800
42102      */
42103         zeroValue : '1800-01-01',
42104         
42105         
42106     /**
42107      * @cfg {Date/String} minValue
42108      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42109      * valid format (defaults to null).
42110      */
42111     minValue : null,
42112     /**
42113      * @cfg {Date/String} maxValue
42114      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42115      * valid format (defaults to null).
42116      */
42117     maxValue : null,
42118     /**
42119      * @cfg {String} minText
42120      * The error text to display when the date in the cell is before minValue (defaults to
42121      * 'The date in this field must be after {minValue}').
42122      */
42123     minText : "The date in this field must be equal to or after {0}",
42124     /**
42125      * @cfg {String} maxText
42126      * The error text to display when the date in the cell is after maxValue (defaults to
42127      * 'The date in this field must be before {maxValue}').
42128      */
42129     maxText : "The date in this field must be equal to or before {0}",
42130     /**
42131      * @cfg {String} invalidText
42132      * The error text to display when the date in the field is invalid (defaults to
42133      * '{value} is not a valid date - it must be in the format {format}').
42134      */
42135     invalidText : "{0} is not a valid date - it must be in the format {1}",
42136     /**
42137      * @cfg {String} triggerClass
42138      * An additional CSS class used to style the trigger button.  The trigger will always get the
42139      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42140      * which displays a calendar icon).
42141      */
42142     triggerClass : 'x-form-date-trigger',
42143     
42144
42145     /**
42146      * @cfg {Boolean} useIso
42147      * if enabled, then the date field will use a hidden field to store the 
42148      * real value as iso formated date. default (false)
42149      */ 
42150     useIso : false,
42151     /**
42152      * @cfg {String/Object} autoCreate
42153      * A DomHelper element spec, or true for a default element spec (defaults to
42154      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42155      */ 
42156     // private
42157     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
42158     
42159     // private
42160     hiddenField: false,
42161     
42162     onRender : function(ct, position)
42163     {
42164         Roo.form.DateField.superclass.onRender.call(this, ct, position);
42165         if (this.useIso) {
42166             //this.el.dom.removeAttribute('name'); 
42167             Roo.log("Changing name?");
42168             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
42169             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42170                     'before', true);
42171             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42172             // prevent input submission
42173             this.hiddenName = this.name;
42174         }
42175             
42176             
42177     },
42178     
42179     // private
42180     validateValue : function(value)
42181     {
42182         value = this.formatDate(value);
42183         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
42184             Roo.log('super failed');
42185             return false;
42186         }
42187         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42188              return true;
42189         }
42190         var svalue = value;
42191         value = this.parseDate(value);
42192         if(!value){
42193             Roo.log('parse date failed' + svalue);
42194             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42195             return false;
42196         }
42197         var time = value.getTime();
42198         if(this.minValue && time < this.minValue.getTime()){
42199             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42200             return false;
42201         }
42202         if(this.maxValue && time > this.maxValue.getTime()){
42203             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42204             return false;
42205         }
42206         if(this.disabledDays){
42207             var day = value.getDay();
42208             for(var i = 0; i < this.disabledDays.length; i++) {
42209                 if(day === this.disabledDays[i]){
42210                     this.markInvalid(this.disabledDaysText);
42211                     return false;
42212                 }
42213             }
42214         }
42215         var fvalue = this.formatDate(value);
42216         if(this.ddMatch && this.ddMatch.test(fvalue)){
42217             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42218             return false;
42219         }
42220         return true;
42221     },
42222
42223     // private
42224     // Provides logic to override the default TriggerField.validateBlur which just returns true
42225     validateBlur : function(){
42226         return !this.menu || !this.menu.isVisible();
42227     },
42228     
42229     getName: function()
42230     {
42231         // returns hidden if it's set..
42232         if (!this.rendered) {return ''};
42233         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42234         
42235     },
42236
42237     /**
42238      * Returns the current date value of the date field.
42239      * @return {Date} The date value
42240      */
42241     getValue : function(){
42242         
42243         return  this.hiddenField ?
42244                 this.hiddenField.value :
42245                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42246     },
42247
42248     /**
42249      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42250      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42251      * (the default format used is "m/d/y").
42252      * <br />Usage:
42253      * <pre><code>
42254 //All of these calls set the same date value (May 4, 2006)
42255
42256 //Pass a date object:
42257 var dt = new Date('5/4/06');
42258 dateField.setValue(dt);
42259
42260 //Pass a date string (default format):
42261 dateField.setValue('5/4/06');
42262
42263 //Pass a date string (custom format):
42264 dateField.format = 'Y-m-d';
42265 dateField.setValue('2006-5-4');
42266 </code></pre>
42267      * @param {String/Date} date The date or valid date string
42268      */
42269     setValue : function(date){
42270         if (this.hiddenField) {
42271             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42272         }
42273         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42274         // make sure the value field is always stored as a date..
42275         this.value = this.parseDate(date);
42276         
42277         
42278     },
42279
42280     // private
42281     parseDate : function(value){
42282                 
42283                 if (value instanceof Date) {
42284                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42285                                 return  '';
42286                         }
42287                         return value;
42288                 }
42289                 
42290                 
42291         if(!value || value instanceof Date){
42292             return value;
42293         }
42294         var v = Date.parseDate(value, this.format);
42295          if (!v && this.useIso) {
42296             v = Date.parseDate(value, 'Y-m-d');
42297         }
42298         if(!v && this.altFormats){
42299             if(!this.altFormatsArray){
42300                 this.altFormatsArray = this.altFormats.split("|");
42301             }
42302             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42303                 v = Date.parseDate(value, this.altFormatsArray[i]);
42304             }
42305         }
42306                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42307                         v = '';
42308                 }
42309         return v;
42310     },
42311
42312     // private
42313     formatDate : function(date, fmt){
42314         return (!date || !(date instanceof Date)) ?
42315                date : date.dateFormat(fmt || this.format);
42316     },
42317
42318     // private
42319     menuListeners : {
42320         select: function(m, d){
42321             
42322             this.setValue(d);
42323             this.fireEvent('select', this, d);
42324         },
42325         show : function(){ // retain focus styling
42326             this.onFocus();
42327         },
42328         hide : function(){
42329             this.focus.defer(10, this);
42330             var ml = this.menuListeners;
42331             this.menu.un("select", ml.select,  this);
42332             this.menu.un("show", ml.show,  this);
42333             this.menu.un("hide", ml.hide,  this);
42334         }
42335     },
42336
42337     // private
42338     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42339     onTriggerClick : function(){
42340         if(this.disabled){
42341             return;
42342         }
42343         if(this.menu == null){
42344             this.menu = new Roo.menu.DateMenu();
42345         }
42346         Roo.apply(this.menu.picker,  {
42347             showClear: this.allowBlank,
42348             minDate : this.minValue,
42349             maxDate : this.maxValue,
42350             disabledDatesRE : this.ddMatch,
42351             disabledDatesText : this.disabledDatesText,
42352             disabledDays : this.disabledDays,
42353             disabledDaysText : this.disabledDaysText,
42354             format : this.useIso ? 'Y-m-d' : this.format,
42355             minText : String.format(this.minText, this.formatDate(this.minValue)),
42356             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42357         });
42358         this.menu.on(Roo.apply({}, this.menuListeners, {
42359             scope:this
42360         }));
42361         this.menu.picker.setValue(this.getValue() || new Date());
42362         this.menu.show(this.el, "tl-bl?");
42363     },
42364
42365     beforeBlur : function(){
42366         var v = this.parseDate(this.getRawValue());
42367         if(v){
42368             this.setValue(v);
42369         }
42370     },
42371
42372     /*@
42373      * overide
42374      * 
42375      */
42376     isDirty : function() {
42377         if(this.disabled) {
42378             return false;
42379         }
42380         
42381         if(typeof(this.startValue) === 'undefined'){
42382             return false;
42383         }
42384         
42385         return String(this.getValue()) !== String(this.startValue);
42386         
42387     },
42388     // @overide
42389     cleanLeadingSpace : function(e)
42390     {
42391        return;
42392     }
42393     
42394 });/*
42395  * Based on:
42396  * Ext JS Library 1.1.1
42397  * Copyright(c) 2006-2007, Ext JS, LLC.
42398  *
42399  * Originally Released Under LGPL - original licence link has changed is not relivant.
42400  *
42401  * Fork - LGPL
42402  * <script type="text/javascript">
42403  */
42404  
42405 /**
42406  * @class Roo.form.MonthField
42407  * @extends Roo.form.TriggerField
42408  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42409 * @constructor
42410 * Create a new MonthField
42411 * @param {Object} config
42412  */
42413 Roo.form.MonthField = function(config){
42414     
42415     Roo.form.MonthField.superclass.constructor.call(this, config);
42416     
42417       this.addEvents({
42418          
42419         /**
42420          * @event select
42421          * Fires when a date is selected
42422              * @param {Roo.form.MonthFieeld} combo This combo box
42423              * @param {Date} date The date selected
42424              */
42425         'select' : true
42426          
42427     });
42428     
42429     
42430     if(typeof this.minValue == "string") {
42431         this.minValue = this.parseDate(this.minValue);
42432     }
42433     if(typeof this.maxValue == "string") {
42434         this.maxValue = this.parseDate(this.maxValue);
42435     }
42436     this.ddMatch = null;
42437     if(this.disabledDates){
42438         var dd = this.disabledDates;
42439         var re = "(?:";
42440         for(var i = 0; i < dd.length; i++){
42441             re += dd[i];
42442             if(i != dd.length-1) {
42443                 re += "|";
42444             }
42445         }
42446         this.ddMatch = new RegExp(re + ")");
42447     }
42448 };
42449
42450 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42451     /**
42452      * @cfg {String} format
42453      * The default date format string which can be overriden for localization support.  The format must be
42454      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42455      */
42456     format : "M Y",
42457     /**
42458      * @cfg {String} altFormats
42459      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42460      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42461      */
42462     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42463     /**
42464      * @cfg {Array} disabledDays
42465      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42466      */
42467     disabledDays : [0,1,2,3,4,5,6],
42468     /**
42469      * @cfg {String} disabledDaysText
42470      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42471      */
42472     disabledDaysText : "Disabled",
42473     /**
42474      * @cfg {Array} disabledDates
42475      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42476      * expression so they are very powerful. Some examples:
42477      * <ul>
42478      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42479      * <li>["03/08", "09/16"] would disable those days for every year</li>
42480      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42481      * <li>["03/../2006"] would disable every day in March 2006</li>
42482      * <li>["^03"] would disable every day in every March</li>
42483      * </ul>
42484      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42485      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42486      */
42487     disabledDates : null,
42488     /**
42489      * @cfg {String} disabledDatesText
42490      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42491      */
42492     disabledDatesText : "Disabled",
42493     /**
42494      * @cfg {Date/String} minValue
42495      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42496      * valid format (defaults to null).
42497      */
42498     minValue : null,
42499     /**
42500      * @cfg {Date/String} maxValue
42501      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42502      * valid format (defaults to null).
42503      */
42504     maxValue : null,
42505     /**
42506      * @cfg {String} minText
42507      * The error text to display when the date in the cell is before minValue (defaults to
42508      * 'The date in this field must be after {minValue}').
42509      */
42510     minText : "The date in this field must be equal to or after {0}",
42511     /**
42512      * @cfg {String} maxTextf
42513      * The error text to display when the date in the cell is after maxValue (defaults to
42514      * 'The date in this field must be before {maxValue}').
42515      */
42516     maxText : "The date in this field must be equal to or before {0}",
42517     /**
42518      * @cfg {String} invalidText
42519      * The error text to display when the date in the field is invalid (defaults to
42520      * '{value} is not a valid date - it must be in the format {format}').
42521      */
42522     invalidText : "{0} is not a valid date - it must be in the format {1}",
42523     /**
42524      * @cfg {String} triggerClass
42525      * An additional CSS class used to style the trigger button.  The trigger will always get the
42526      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42527      * which displays a calendar icon).
42528      */
42529     triggerClass : 'x-form-date-trigger',
42530     
42531
42532     /**
42533      * @cfg {Boolean} useIso
42534      * if enabled, then the date field will use a hidden field to store the 
42535      * real value as iso formated date. default (true)
42536      */ 
42537     useIso : true,
42538     /**
42539      * @cfg {String/Object} autoCreate
42540      * A DomHelper element spec, or true for a default element spec (defaults to
42541      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42542      */ 
42543     // private
42544     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42545     
42546     // private
42547     hiddenField: false,
42548     
42549     hideMonthPicker : false,
42550     
42551     onRender : function(ct, position)
42552     {
42553         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42554         if (this.useIso) {
42555             this.el.dom.removeAttribute('name'); 
42556             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42557                     'before', true);
42558             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42559             // prevent input submission
42560             this.hiddenName = this.name;
42561         }
42562             
42563             
42564     },
42565     
42566     // private
42567     validateValue : function(value)
42568     {
42569         value = this.formatDate(value);
42570         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42571             return false;
42572         }
42573         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42574              return true;
42575         }
42576         var svalue = value;
42577         value = this.parseDate(value);
42578         if(!value){
42579             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42580             return false;
42581         }
42582         var time = value.getTime();
42583         if(this.minValue && time < this.minValue.getTime()){
42584             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42585             return false;
42586         }
42587         if(this.maxValue && time > this.maxValue.getTime()){
42588             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42589             return false;
42590         }
42591         /*if(this.disabledDays){
42592             var day = value.getDay();
42593             for(var i = 0; i < this.disabledDays.length; i++) {
42594                 if(day === this.disabledDays[i]){
42595                     this.markInvalid(this.disabledDaysText);
42596                     return false;
42597                 }
42598             }
42599         }
42600         */
42601         var fvalue = this.formatDate(value);
42602         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42603             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42604             return false;
42605         }
42606         */
42607         return true;
42608     },
42609
42610     // private
42611     // Provides logic to override the default TriggerField.validateBlur which just returns true
42612     validateBlur : function(){
42613         return !this.menu || !this.menu.isVisible();
42614     },
42615
42616     /**
42617      * Returns the current date value of the date field.
42618      * @return {Date} The date value
42619      */
42620     getValue : function(){
42621         
42622         
42623         
42624         return  this.hiddenField ?
42625                 this.hiddenField.value :
42626                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42627     },
42628
42629     /**
42630      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42631      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42632      * (the default format used is "m/d/y").
42633      * <br />Usage:
42634      * <pre><code>
42635 //All of these calls set the same date value (May 4, 2006)
42636
42637 //Pass a date object:
42638 var dt = new Date('5/4/06');
42639 monthField.setValue(dt);
42640
42641 //Pass a date string (default format):
42642 monthField.setValue('5/4/06');
42643
42644 //Pass a date string (custom format):
42645 monthField.format = 'Y-m-d';
42646 monthField.setValue('2006-5-4');
42647 </code></pre>
42648      * @param {String/Date} date The date or valid date string
42649      */
42650     setValue : function(date){
42651         Roo.log('month setValue' + date);
42652         // can only be first of month..
42653         
42654         var val = this.parseDate(date);
42655         
42656         if (this.hiddenField) {
42657             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42658         }
42659         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42660         this.value = this.parseDate(date);
42661     },
42662
42663     // private
42664     parseDate : function(value){
42665         if(!value || value instanceof Date){
42666             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42667             return value;
42668         }
42669         var v = Date.parseDate(value, this.format);
42670         if (!v && this.useIso) {
42671             v = Date.parseDate(value, 'Y-m-d');
42672         }
42673         if (v) {
42674             // 
42675             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42676         }
42677         
42678         
42679         if(!v && this.altFormats){
42680             if(!this.altFormatsArray){
42681                 this.altFormatsArray = this.altFormats.split("|");
42682             }
42683             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42684                 v = Date.parseDate(value, this.altFormatsArray[i]);
42685             }
42686         }
42687         return v;
42688     },
42689
42690     // private
42691     formatDate : function(date, fmt){
42692         return (!date || !(date instanceof Date)) ?
42693                date : date.dateFormat(fmt || this.format);
42694     },
42695
42696     // private
42697     menuListeners : {
42698         select: function(m, d){
42699             this.setValue(d);
42700             this.fireEvent('select', this, d);
42701         },
42702         show : function(){ // retain focus styling
42703             this.onFocus();
42704         },
42705         hide : function(){
42706             this.focus.defer(10, this);
42707             var ml = this.menuListeners;
42708             this.menu.un("select", ml.select,  this);
42709             this.menu.un("show", ml.show,  this);
42710             this.menu.un("hide", ml.hide,  this);
42711         }
42712     },
42713     // private
42714     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42715     onTriggerClick : function(){
42716         if(this.disabled){
42717             return;
42718         }
42719         if(this.menu == null){
42720             this.menu = new Roo.menu.DateMenu();
42721            
42722         }
42723         
42724         Roo.apply(this.menu.picker,  {
42725             
42726             showClear: this.allowBlank,
42727             minDate : this.minValue,
42728             maxDate : this.maxValue,
42729             disabledDatesRE : this.ddMatch,
42730             disabledDatesText : this.disabledDatesText,
42731             
42732             format : this.useIso ? 'Y-m-d' : this.format,
42733             minText : String.format(this.minText, this.formatDate(this.minValue)),
42734             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42735             
42736         });
42737          this.menu.on(Roo.apply({}, this.menuListeners, {
42738             scope:this
42739         }));
42740        
42741         
42742         var m = this.menu;
42743         var p = m.picker;
42744         
42745         // hide month picker get's called when we called by 'before hide';
42746         
42747         var ignorehide = true;
42748         p.hideMonthPicker  = function(disableAnim){
42749             if (ignorehide) {
42750                 return;
42751             }
42752              if(this.monthPicker){
42753                 Roo.log("hideMonthPicker called");
42754                 if(disableAnim === true){
42755                     this.monthPicker.hide();
42756                 }else{
42757                     this.monthPicker.slideOut('t', {duration:.2});
42758                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42759                     p.fireEvent("select", this, this.value);
42760                     m.hide();
42761                 }
42762             }
42763         }
42764         
42765         Roo.log('picker set value');
42766         Roo.log(this.getValue());
42767         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42768         m.show(this.el, 'tl-bl?');
42769         ignorehide  = false;
42770         // this will trigger hideMonthPicker..
42771         
42772         
42773         // hidden the day picker
42774         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42775         
42776         
42777         
42778       
42779         
42780         p.showMonthPicker.defer(100, p);
42781     
42782         
42783        
42784     },
42785
42786     beforeBlur : function(){
42787         var v = this.parseDate(this.getRawValue());
42788         if(v){
42789             this.setValue(v);
42790         }
42791     }
42792
42793     /** @cfg {Boolean} grow @hide */
42794     /** @cfg {Number} growMin @hide */
42795     /** @cfg {Number} growMax @hide */
42796     /**
42797      * @hide
42798      * @method autoSize
42799      */
42800 });/*
42801  * Based on:
42802  * Ext JS Library 1.1.1
42803  * Copyright(c) 2006-2007, Ext JS, LLC.
42804  *
42805  * Originally Released Under LGPL - original licence link has changed is not relivant.
42806  *
42807  * Fork - LGPL
42808  * <script type="text/javascript">
42809  */
42810  
42811
42812 /**
42813  * @class Roo.form.ComboBox
42814  * @extends Roo.form.TriggerField
42815  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42816  * @constructor
42817  * Create a new ComboBox.
42818  * @param {Object} config Configuration options
42819  */
42820 Roo.form.ComboBox = function(config){
42821     Roo.form.ComboBox.superclass.constructor.call(this, config);
42822     this.addEvents({
42823         /**
42824          * @event expand
42825          * Fires when the dropdown list is expanded
42826              * @param {Roo.form.ComboBox} combo This combo box
42827              */
42828         'expand' : true,
42829         /**
42830          * @event collapse
42831          * Fires when the dropdown list is collapsed
42832              * @param {Roo.form.ComboBox} combo This combo box
42833              */
42834         'collapse' : true,
42835         /**
42836          * @event beforeselect
42837          * Fires before a list item is selected. Return false to cancel the selection.
42838              * @param {Roo.form.ComboBox} combo This combo box
42839              * @param {Roo.data.Record} record The data record returned from the underlying store
42840              * @param {Number} index The index of the selected item in the dropdown list
42841              */
42842         'beforeselect' : true,
42843         /**
42844          * @event select
42845          * Fires when a list item is selected
42846              * @param {Roo.form.ComboBox} combo This combo box
42847              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42848              * @param {Number} index The index of the selected item in the dropdown list
42849              */
42850         'select' : true,
42851         /**
42852          * @event beforequery
42853          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42854          * The event object passed has these properties:
42855              * @param {Roo.form.ComboBox} combo This combo box
42856              * @param {String} query The query
42857              * @param {Boolean} forceAll true to force "all" query
42858              * @param {Boolean} cancel true to cancel the query
42859              * @param {Object} e The query event object
42860              */
42861         'beforequery': true,
42862          /**
42863          * @event add
42864          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42865              * @param {Roo.form.ComboBox} combo This combo box
42866              */
42867         'add' : true,
42868         /**
42869          * @event edit
42870          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42871              * @param {Roo.form.ComboBox} combo This combo box
42872              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42873              */
42874         'edit' : true
42875         
42876         
42877     });
42878     if(this.transform){
42879         this.allowDomMove = false;
42880         var s = Roo.getDom(this.transform);
42881         if(!this.hiddenName){
42882             this.hiddenName = s.name;
42883         }
42884         if(!this.store){
42885             this.mode = 'local';
42886             var d = [], opts = s.options;
42887             for(var i = 0, len = opts.length;i < len; i++){
42888                 var o = opts[i];
42889                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42890                 if(o.selected) {
42891                     this.value = value;
42892                 }
42893                 d.push([value, o.text]);
42894             }
42895             this.store = new Roo.data.SimpleStore({
42896                 'id': 0,
42897                 fields: ['value', 'text'],
42898                 data : d
42899             });
42900             this.valueField = 'value';
42901             this.displayField = 'text';
42902         }
42903         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42904         if(!this.lazyRender){
42905             this.target = true;
42906             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42907             s.parentNode.removeChild(s); // remove it
42908             this.render(this.el.parentNode);
42909         }else{
42910             s.parentNode.removeChild(s); // remove it
42911         }
42912
42913     }
42914     if (this.store) {
42915         this.store = Roo.factory(this.store, Roo.data);
42916     }
42917     
42918     this.selectedIndex = -1;
42919     if(this.mode == 'local'){
42920         if(config.queryDelay === undefined){
42921             this.queryDelay = 10;
42922         }
42923         if(config.minChars === undefined){
42924             this.minChars = 0;
42925         }
42926     }
42927 };
42928
42929 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42930     /**
42931      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42932      */
42933     /**
42934      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42935      * rendering into an Roo.Editor, defaults to false)
42936      */
42937     /**
42938      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42939      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42940      */
42941     /**
42942      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42943      */
42944     /**
42945      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42946      * the dropdown list (defaults to undefined, with no header element)
42947      */
42948
42949      /**
42950      * @cfg {String/Roo.Template} tpl The template to use to render the output
42951      */
42952      
42953     // private
42954     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42955     /**
42956      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42957      */
42958     listWidth: undefined,
42959     /**
42960      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42961      * mode = 'remote' or 'text' if mode = 'local')
42962      */
42963     displayField: undefined,
42964     /**
42965      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42966      * mode = 'remote' or 'value' if mode = 'local'). 
42967      * Note: use of a valueField requires the user make a selection
42968      * in order for a value to be mapped.
42969      */
42970     valueField: undefined,
42971     
42972     
42973     /**
42974      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42975      * field's data value (defaults to the underlying DOM element's name)
42976      */
42977     hiddenName: undefined,
42978     /**
42979      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42980      */
42981     listClass: '',
42982     /**
42983      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42984      */
42985     selectedClass: 'x-combo-selected',
42986     /**
42987      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42988      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42989      * which displays a downward arrow icon).
42990      */
42991     triggerClass : 'x-form-arrow-trigger',
42992     /**
42993      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42994      */
42995     shadow:'sides',
42996     /**
42997      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42998      * anchor positions (defaults to 'tl-bl')
42999      */
43000     listAlign: 'tl-bl?',
43001     /**
43002      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
43003      */
43004     maxHeight: 300,
43005     /**
43006      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
43007      * query specified by the allQuery config option (defaults to 'query')
43008      */
43009     triggerAction: 'query',
43010     /**
43011      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
43012      * (defaults to 4, does not apply if editable = false)
43013      */
43014     minChars : 4,
43015     /**
43016      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
43017      * delay (typeAheadDelay) if it matches a known value (defaults to false)
43018      */
43019     typeAhead: false,
43020     /**
43021      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
43022      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
43023      */
43024     queryDelay: 500,
43025     /**
43026      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
43027      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
43028      */
43029     pageSize: 0,
43030     /**
43031      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
43032      * when editable = true (defaults to false)
43033      */
43034     selectOnFocus:false,
43035     /**
43036      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
43037      */
43038     queryParam: 'query',
43039     /**
43040      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
43041      * when mode = 'remote' (defaults to 'Loading...')
43042      */
43043     loadingText: 'Loading...',
43044     /**
43045      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
43046      */
43047     resizable: false,
43048     /**
43049      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
43050      */
43051     handleHeight : 8,
43052     /**
43053      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
43054      * traditional select (defaults to true)
43055      */
43056     editable: true,
43057     /**
43058      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
43059      */
43060     allQuery: '',
43061     /**
43062      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
43063      */
43064     mode: 'remote',
43065     /**
43066      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
43067      * listWidth has a higher value)
43068      */
43069     minListWidth : 70,
43070     /**
43071      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
43072      * allow the user to set arbitrary text into the field (defaults to false)
43073      */
43074     forceSelection:false,
43075     /**
43076      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
43077      * if typeAhead = true (defaults to 250)
43078      */
43079     typeAheadDelay : 250,
43080     /**
43081      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
43082      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
43083      */
43084     valueNotFoundText : undefined,
43085     /**
43086      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
43087      */
43088     blockFocus : false,
43089     
43090     /**
43091      * @cfg {Boolean} disableClear Disable showing of clear button.
43092      */
43093     disableClear : false,
43094     /**
43095      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
43096      */
43097     alwaysQuery : false,
43098     
43099     //private
43100     addicon : false,
43101     editicon: false,
43102     
43103     // element that contains real text value.. (when hidden is used..)
43104      
43105     // private
43106     onRender : function(ct, position)
43107     {
43108         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
43109         
43110         if(this.hiddenName){
43111             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43112                     'before', true);
43113             this.hiddenField.value =
43114                 this.hiddenValue !== undefined ? this.hiddenValue :
43115                 this.value !== undefined ? this.value : '';
43116
43117             // prevent input submission
43118             this.el.dom.removeAttribute('name');
43119              
43120              
43121         }
43122         
43123         if(Roo.isGecko){
43124             this.el.dom.setAttribute('autocomplete', 'off');
43125         }
43126
43127         var cls = 'x-combo-list';
43128
43129         this.list = new Roo.Layer({
43130             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43131         });
43132
43133         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43134         this.list.setWidth(lw);
43135         this.list.swallowEvent('mousewheel');
43136         this.assetHeight = 0;
43137
43138         if(this.title){
43139             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43140             this.assetHeight += this.header.getHeight();
43141         }
43142
43143         this.innerList = this.list.createChild({cls:cls+'-inner'});
43144         this.innerList.on('mouseover', this.onViewOver, this);
43145         this.innerList.on('mousemove', this.onViewMove, this);
43146         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43147         
43148         if(this.allowBlank && !this.pageSize && !this.disableClear){
43149             this.footer = this.list.createChild({cls:cls+'-ft'});
43150             this.pageTb = new Roo.Toolbar(this.footer);
43151            
43152         }
43153         if(this.pageSize){
43154             this.footer = this.list.createChild({cls:cls+'-ft'});
43155             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
43156                     {pageSize: this.pageSize});
43157             
43158         }
43159         
43160         if (this.pageTb && this.allowBlank && !this.disableClear) {
43161             var _this = this;
43162             this.pageTb.add(new Roo.Toolbar.Fill(), {
43163                 cls: 'x-btn-icon x-btn-clear',
43164                 text: '&#160;',
43165                 handler: function()
43166                 {
43167                     _this.collapse();
43168                     _this.clearValue();
43169                     _this.onSelect(false, -1);
43170                 }
43171             });
43172         }
43173         if (this.footer) {
43174             this.assetHeight += this.footer.getHeight();
43175         }
43176         
43177
43178         if(!this.tpl){
43179             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
43180         }
43181
43182         this.view = new Roo.View(this.innerList, this.tpl, {
43183             singleSelect:true,
43184             store: this.store,
43185             selectedClass: this.selectedClass
43186         });
43187
43188         this.view.on('click', this.onViewClick, this);
43189
43190         this.store.on('beforeload', this.onBeforeLoad, this);
43191         this.store.on('load', this.onLoad, this);
43192         this.store.on('loadexception', this.onLoadException, this);
43193
43194         if(this.resizable){
43195             this.resizer = new Roo.Resizable(this.list,  {
43196                pinned:true, handles:'se'
43197             });
43198             this.resizer.on('resize', function(r, w, h){
43199                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
43200                 this.listWidth = w;
43201                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
43202                 this.restrictHeight();
43203             }, this);
43204             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
43205         }
43206         if(!this.editable){
43207             this.editable = true;
43208             this.setEditable(false);
43209         }  
43210         
43211         
43212         if (typeof(this.events.add.listeners) != 'undefined') {
43213             
43214             this.addicon = this.wrap.createChild(
43215                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
43216        
43217             this.addicon.on('click', function(e) {
43218                 this.fireEvent('add', this);
43219             }, this);
43220         }
43221         if (typeof(this.events.edit.listeners) != 'undefined') {
43222             
43223             this.editicon = this.wrap.createChild(
43224                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
43225             if (this.addicon) {
43226                 this.editicon.setStyle('margin-left', '40px');
43227             }
43228             this.editicon.on('click', function(e) {
43229                 
43230                 // we fire even  if inothing is selected..
43231                 this.fireEvent('edit', this, this.lastData );
43232                 
43233             }, this);
43234         }
43235         
43236         
43237         
43238     },
43239
43240     // private
43241     initEvents : function(){
43242         Roo.form.ComboBox.superclass.initEvents.call(this);
43243
43244         this.keyNav = new Roo.KeyNav(this.el, {
43245             "up" : function(e){
43246                 this.inKeyMode = true;
43247                 this.selectPrev();
43248             },
43249
43250             "down" : function(e){
43251                 if(!this.isExpanded()){
43252                     this.onTriggerClick();
43253                 }else{
43254                     this.inKeyMode = true;
43255                     this.selectNext();
43256                 }
43257             },
43258
43259             "enter" : function(e){
43260                 this.onViewClick();
43261                 //return true;
43262             },
43263
43264             "esc" : function(e){
43265                 this.collapse();
43266             },
43267
43268             "tab" : function(e){
43269                 this.onViewClick(false);
43270                 this.fireEvent("specialkey", this, e);
43271                 return true;
43272             },
43273
43274             scope : this,
43275
43276             doRelay : function(foo, bar, hname){
43277                 if(hname == 'down' || this.scope.isExpanded()){
43278                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43279                 }
43280                 return true;
43281             },
43282
43283             forceKeyDown: true
43284         });
43285         this.queryDelay = Math.max(this.queryDelay || 10,
43286                 this.mode == 'local' ? 10 : 250);
43287         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43288         if(this.typeAhead){
43289             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43290         }
43291         if(this.editable !== false){
43292             this.el.on("keyup", this.onKeyUp, this);
43293         }
43294         if(this.forceSelection){
43295             this.on('blur', this.doForce, this);
43296         }
43297     },
43298
43299     onDestroy : function(){
43300         if(this.view){
43301             this.view.setStore(null);
43302             this.view.el.removeAllListeners();
43303             this.view.el.remove();
43304             this.view.purgeListeners();
43305         }
43306         if(this.list){
43307             this.list.destroy();
43308         }
43309         if(this.store){
43310             this.store.un('beforeload', this.onBeforeLoad, this);
43311             this.store.un('load', this.onLoad, this);
43312             this.store.un('loadexception', this.onLoadException, this);
43313         }
43314         Roo.form.ComboBox.superclass.onDestroy.call(this);
43315     },
43316
43317     // private
43318     fireKey : function(e){
43319         if(e.isNavKeyPress() && !this.list.isVisible()){
43320             this.fireEvent("specialkey", this, e);
43321         }
43322     },
43323
43324     // private
43325     onResize: function(w, h){
43326         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43327         
43328         if(typeof w != 'number'){
43329             // we do not handle it!?!?
43330             return;
43331         }
43332         var tw = this.trigger.getWidth();
43333         tw += this.addicon ? this.addicon.getWidth() : 0;
43334         tw += this.editicon ? this.editicon.getWidth() : 0;
43335         var x = w - tw;
43336         this.el.setWidth( this.adjustWidth('input', x));
43337             
43338         this.trigger.setStyle('left', x+'px');
43339         
43340         if(this.list && this.listWidth === undefined){
43341             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43342             this.list.setWidth(lw);
43343             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43344         }
43345         
43346     
43347         
43348     },
43349
43350     /**
43351      * Allow or prevent the user from directly editing the field text.  If false is passed,
43352      * the user will only be able to select from the items defined in the dropdown list.  This method
43353      * is the runtime equivalent of setting the 'editable' config option at config time.
43354      * @param {Boolean} value True to allow the user to directly edit the field text
43355      */
43356     setEditable : function(value){
43357         if(value == this.editable){
43358             return;
43359         }
43360         this.editable = value;
43361         if(!value){
43362             this.el.dom.setAttribute('readOnly', true);
43363             this.el.on('mousedown', this.onTriggerClick,  this);
43364             this.el.addClass('x-combo-noedit');
43365         }else{
43366             this.el.dom.setAttribute('readOnly', false);
43367             this.el.un('mousedown', this.onTriggerClick,  this);
43368             this.el.removeClass('x-combo-noedit');
43369         }
43370     },
43371
43372     // private
43373     onBeforeLoad : function(){
43374         if(!this.hasFocus){
43375             return;
43376         }
43377         this.innerList.update(this.loadingText ?
43378                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43379         this.restrictHeight();
43380         this.selectedIndex = -1;
43381     },
43382
43383     // private
43384     onLoad : function(){
43385         if(!this.hasFocus){
43386             return;
43387         }
43388         if(this.store.getCount() > 0){
43389             this.expand();
43390             this.restrictHeight();
43391             if(this.lastQuery == this.allQuery){
43392                 if(this.editable){
43393                     this.el.dom.select();
43394                 }
43395                 if(!this.selectByValue(this.value, true)){
43396                     this.select(0, true);
43397                 }
43398             }else{
43399                 this.selectNext();
43400                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43401                     this.taTask.delay(this.typeAheadDelay);
43402                 }
43403             }
43404         }else{
43405             this.onEmptyResults();
43406         }
43407         //this.el.focus();
43408     },
43409     // private
43410     onLoadException : function()
43411     {
43412         this.collapse();
43413         Roo.log(this.store.reader.jsonData);
43414         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43415             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43416         }
43417         
43418         
43419     },
43420     // private
43421     onTypeAhead : function(){
43422         if(this.store.getCount() > 0){
43423             var r = this.store.getAt(0);
43424             var newValue = r.data[this.displayField];
43425             var len = newValue.length;
43426             var selStart = this.getRawValue().length;
43427             if(selStart != len){
43428                 this.setRawValue(newValue);
43429                 this.selectText(selStart, newValue.length);
43430             }
43431         }
43432     },
43433
43434     // private
43435     onSelect : function(record, index){
43436         if(this.fireEvent('beforeselect', this, record, index) !== false){
43437             this.setFromData(index > -1 ? record.data : false);
43438             this.collapse();
43439             this.fireEvent('select', this, record, index);
43440         }
43441     },
43442
43443     /**
43444      * Returns the currently selected field value or empty string if no value is set.
43445      * @return {String} value The selected value
43446      */
43447     getValue : function(){
43448         if(this.valueField){
43449             return typeof this.value != 'undefined' ? this.value : '';
43450         }
43451         return Roo.form.ComboBox.superclass.getValue.call(this);
43452     },
43453
43454     /**
43455      * Clears any text/value currently set in the field
43456      */
43457     clearValue : function(){
43458         if(this.hiddenField){
43459             this.hiddenField.value = '';
43460         }
43461         this.value = '';
43462         this.setRawValue('');
43463         this.lastSelectionText = '';
43464         
43465     },
43466
43467     /**
43468      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43469      * will be displayed in the field.  If the value does not match the data value of an existing item,
43470      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43471      * Otherwise the field will be blank (although the value will still be set).
43472      * @param {String} value The value to match
43473      */
43474     setValue : function(v){
43475         var text = v;
43476         if(this.valueField){
43477             var r = this.findRecord(this.valueField, v);
43478             if(r){
43479                 text = r.data[this.displayField];
43480             }else if(this.valueNotFoundText !== undefined){
43481                 text = this.valueNotFoundText;
43482             }
43483         }
43484         this.lastSelectionText = text;
43485         if(this.hiddenField){
43486             this.hiddenField.value = v;
43487         }
43488         Roo.form.ComboBox.superclass.setValue.call(this, text);
43489         this.value = v;
43490     },
43491     /**
43492      * @property {Object} the last set data for the element
43493      */
43494     
43495     lastData : false,
43496     /**
43497      * Sets the value of the field based on a object which is related to the record format for the store.
43498      * @param {Object} value the value to set as. or false on reset?
43499      */
43500     setFromData : function(o){
43501         var dv = ''; // display value
43502         var vv = ''; // value value..
43503         this.lastData = o;
43504         if (this.displayField) {
43505             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43506         } else {
43507             // this is an error condition!!!
43508             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43509         }
43510         
43511         if(this.valueField){
43512             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43513         }
43514         if(this.hiddenField){
43515             this.hiddenField.value = vv;
43516             
43517             this.lastSelectionText = dv;
43518             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43519             this.value = vv;
43520             return;
43521         }
43522         // no hidden field.. - we store the value in 'value', but still display
43523         // display field!!!!
43524         this.lastSelectionText = dv;
43525         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43526         this.value = vv;
43527         
43528         
43529     },
43530     // private
43531     reset : function(){
43532         // overridden so that last data is reset..
43533         this.setValue(this.resetValue);
43534         this.originalValue = this.getValue();
43535         this.clearInvalid();
43536         this.lastData = false;
43537         if (this.view) {
43538             this.view.clearSelections();
43539         }
43540     },
43541     // private
43542     findRecord : function(prop, value){
43543         var record;
43544         if(this.store.getCount() > 0){
43545             this.store.each(function(r){
43546                 if(r.data[prop] == value){
43547                     record = r;
43548                     return false;
43549                 }
43550                 return true;
43551             });
43552         }
43553         return record;
43554     },
43555     
43556     getName: function()
43557     {
43558         // returns hidden if it's set..
43559         if (!this.rendered) {return ''};
43560         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43561         
43562     },
43563     // private
43564     onViewMove : function(e, t){
43565         this.inKeyMode = false;
43566     },
43567
43568     // private
43569     onViewOver : function(e, t){
43570         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43571             return;
43572         }
43573         var item = this.view.findItemFromChild(t);
43574         if(item){
43575             var index = this.view.indexOf(item);
43576             this.select(index, false);
43577         }
43578     },
43579
43580     // private
43581     onViewClick : function(doFocus)
43582     {
43583         var index = this.view.getSelectedIndexes()[0];
43584         var r = this.store.getAt(index);
43585         if(r){
43586             this.onSelect(r, index);
43587         }
43588         if(doFocus !== false && !this.blockFocus){
43589             this.el.focus();
43590         }
43591     },
43592
43593     // private
43594     restrictHeight : function(){
43595         this.innerList.dom.style.height = '';
43596         var inner = this.innerList.dom;
43597         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43598         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43599         this.list.beginUpdate();
43600         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43601         this.list.alignTo(this.el, this.listAlign);
43602         this.list.endUpdate();
43603     },
43604
43605     // private
43606     onEmptyResults : function(){
43607         this.collapse();
43608     },
43609
43610     /**
43611      * Returns true if the dropdown list is expanded, else false.
43612      */
43613     isExpanded : function(){
43614         return this.list.isVisible();
43615     },
43616
43617     /**
43618      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43619      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43620      * @param {String} value The data value of the item to select
43621      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43622      * selected item if it is not currently in view (defaults to true)
43623      * @return {Boolean} True if the value matched an item in the list, else false
43624      */
43625     selectByValue : function(v, scrollIntoView){
43626         if(v !== undefined && v !== null){
43627             var r = this.findRecord(this.valueField || this.displayField, v);
43628             if(r){
43629                 this.select(this.store.indexOf(r), scrollIntoView);
43630                 return true;
43631             }
43632         }
43633         return false;
43634     },
43635
43636     /**
43637      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43638      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43639      * @param {Number} index The zero-based index of the list item to select
43640      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43641      * selected item if it is not currently in view (defaults to true)
43642      */
43643     select : function(index, scrollIntoView){
43644         this.selectedIndex = index;
43645         this.view.select(index);
43646         if(scrollIntoView !== false){
43647             var el = this.view.getNode(index);
43648             if(el){
43649                 this.innerList.scrollChildIntoView(el, false);
43650             }
43651         }
43652     },
43653
43654     // private
43655     selectNext : function(){
43656         var ct = this.store.getCount();
43657         if(ct > 0){
43658             if(this.selectedIndex == -1){
43659                 this.select(0);
43660             }else if(this.selectedIndex < ct-1){
43661                 this.select(this.selectedIndex+1);
43662             }
43663         }
43664     },
43665
43666     // private
43667     selectPrev : function(){
43668         var ct = this.store.getCount();
43669         if(ct > 0){
43670             if(this.selectedIndex == -1){
43671                 this.select(0);
43672             }else if(this.selectedIndex != 0){
43673                 this.select(this.selectedIndex-1);
43674             }
43675         }
43676     },
43677
43678     // private
43679     onKeyUp : function(e){
43680         if(this.editable !== false && !e.isSpecialKey()){
43681             this.lastKey = e.getKey();
43682             this.dqTask.delay(this.queryDelay);
43683         }
43684     },
43685
43686     // private
43687     validateBlur : function(){
43688         return !this.list || !this.list.isVisible();   
43689     },
43690
43691     // private
43692     initQuery : function(){
43693         this.doQuery(this.getRawValue());
43694     },
43695
43696     // private
43697     doForce : function(){
43698         if(this.el.dom.value.length > 0){
43699             this.el.dom.value =
43700                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43701              
43702         }
43703     },
43704
43705     /**
43706      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43707      * query allowing the query action to be canceled if needed.
43708      * @param {String} query The SQL query to execute
43709      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43710      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43711      * saved in the current store (defaults to false)
43712      */
43713     doQuery : function(q, forceAll){
43714         if(q === undefined || q === null){
43715             q = '';
43716         }
43717         var qe = {
43718             query: q,
43719             forceAll: forceAll,
43720             combo: this,
43721             cancel:false
43722         };
43723         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43724             return false;
43725         }
43726         q = qe.query;
43727         forceAll = qe.forceAll;
43728         if(forceAll === true || (q.length >= this.minChars)){
43729             if(this.lastQuery != q || this.alwaysQuery){
43730                 this.lastQuery = q;
43731                 if(this.mode == 'local'){
43732                     this.selectedIndex = -1;
43733                     if(forceAll){
43734                         this.store.clearFilter();
43735                     }else{
43736                         this.store.filter(this.displayField, q);
43737                     }
43738                     this.onLoad();
43739                 }else{
43740                     this.store.baseParams[this.queryParam] = q;
43741                     this.store.load({
43742                         params: this.getParams(q)
43743                     });
43744                     this.expand();
43745                 }
43746             }else{
43747                 this.selectedIndex = -1;
43748                 this.onLoad();   
43749             }
43750         }
43751     },
43752
43753     // private
43754     getParams : function(q){
43755         var p = {};
43756         //p[this.queryParam] = q;
43757         if(this.pageSize){
43758             p.start = 0;
43759             p.limit = this.pageSize;
43760         }
43761         return p;
43762     },
43763
43764     /**
43765      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43766      */
43767     collapse : function(){
43768         if(!this.isExpanded()){
43769             return;
43770         }
43771         this.list.hide();
43772         Roo.get(document).un('mousedown', this.collapseIf, this);
43773         Roo.get(document).un('mousewheel', this.collapseIf, this);
43774         if (!this.editable) {
43775             Roo.get(document).un('keydown', this.listKeyPress, this);
43776         }
43777         this.fireEvent('collapse', this);
43778     },
43779
43780     // private
43781     collapseIf : function(e){
43782         if(!e.within(this.wrap) && !e.within(this.list)){
43783             this.collapse();
43784         }
43785     },
43786
43787     /**
43788      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43789      */
43790     expand : function(){
43791         if(this.isExpanded() || !this.hasFocus){
43792             return;
43793         }
43794         this.list.alignTo(this.el, this.listAlign);
43795         this.list.show();
43796         Roo.get(document).on('mousedown', this.collapseIf, this);
43797         Roo.get(document).on('mousewheel', this.collapseIf, this);
43798         if (!this.editable) {
43799             Roo.get(document).on('keydown', this.listKeyPress, this);
43800         }
43801         
43802         this.fireEvent('expand', this);
43803     },
43804
43805     // private
43806     // Implements the default empty TriggerField.onTriggerClick function
43807     onTriggerClick : function(){
43808         if(this.disabled){
43809             return;
43810         }
43811         if(this.isExpanded()){
43812             this.collapse();
43813             if (!this.blockFocus) {
43814                 this.el.focus();
43815             }
43816             
43817         }else {
43818             this.hasFocus = true;
43819             if(this.triggerAction == 'all') {
43820                 this.doQuery(this.allQuery, true);
43821             } else {
43822                 this.doQuery(this.getRawValue());
43823             }
43824             if (!this.blockFocus) {
43825                 this.el.focus();
43826             }
43827         }
43828     },
43829     listKeyPress : function(e)
43830     {
43831         //Roo.log('listkeypress');
43832         // scroll to first matching element based on key pres..
43833         if (e.isSpecialKey()) {
43834             return false;
43835         }
43836         var k = String.fromCharCode(e.getKey()).toUpperCase();
43837         //Roo.log(k);
43838         var match  = false;
43839         var csel = this.view.getSelectedNodes();
43840         var cselitem = false;
43841         if (csel.length) {
43842             var ix = this.view.indexOf(csel[0]);
43843             cselitem  = this.store.getAt(ix);
43844             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43845                 cselitem = false;
43846             }
43847             
43848         }
43849         
43850         this.store.each(function(v) { 
43851             if (cselitem) {
43852                 // start at existing selection.
43853                 if (cselitem.id == v.id) {
43854                     cselitem = false;
43855                 }
43856                 return;
43857             }
43858                 
43859             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43860                 match = this.store.indexOf(v);
43861                 return false;
43862             }
43863         }, this);
43864         
43865         if (match === false) {
43866             return true; // no more action?
43867         }
43868         // scroll to?
43869         this.view.select(match);
43870         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43871         sn.scrollIntoView(sn.dom.parentNode, false);
43872     } 
43873
43874     /** 
43875     * @cfg {Boolean} grow 
43876     * @hide 
43877     */
43878     /** 
43879     * @cfg {Number} growMin 
43880     * @hide 
43881     */
43882     /** 
43883     * @cfg {Number} growMax 
43884     * @hide 
43885     */
43886     /**
43887      * @hide
43888      * @method autoSize
43889      */
43890 });/*
43891  * Copyright(c) 2010-2012, Roo J Solutions Limited
43892  *
43893  * Licence LGPL
43894  *
43895  */
43896
43897 /**
43898  * @class Roo.form.ComboBoxArray
43899  * @extends Roo.form.TextField
43900  * A facebook style adder... for lists of email / people / countries  etc...
43901  * pick multiple items from a combo box, and shows each one.
43902  *
43903  *  Fred [x]  Brian [x]  [Pick another |v]
43904  *
43905  *
43906  *  For this to work: it needs various extra information
43907  *    - normal combo problay has
43908  *      name, hiddenName
43909  *    + displayField, valueField
43910  *
43911  *    For our purpose...
43912  *
43913  *
43914  *   If we change from 'extends' to wrapping...
43915  *   
43916  *  
43917  *
43918  
43919  
43920  * @constructor
43921  * Create a new ComboBoxArray.
43922  * @param {Object} config Configuration options
43923  */
43924  
43925
43926 Roo.form.ComboBoxArray = function(config)
43927 {
43928     this.addEvents({
43929         /**
43930          * @event beforeremove
43931          * Fires before remove the value from the list
43932              * @param {Roo.form.ComboBoxArray} _self This combo box array
43933              * @param {Roo.form.ComboBoxArray.Item} item removed item
43934              */
43935         'beforeremove' : true,
43936         /**
43937          * @event remove
43938          * Fires when remove the value from the list
43939              * @param {Roo.form.ComboBoxArray} _self This combo box array
43940              * @param {Roo.form.ComboBoxArray.Item} item removed item
43941              */
43942         'remove' : true
43943         
43944         
43945     });
43946     
43947     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43948     
43949     this.items = new Roo.util.MixedCollection(false);
43950     
43951     // construct the child combo...
43952     
43953     
43954     
43955     
43956    
43957     
43958 }
43959
43960  
43961 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43962
43963     /**
43964      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43965      */
43966     
43967     lastData : false,
43968     
43969     // behavies liek a hiddne field
43970     inputType:      'hidden',
43971     /**
43972      * @cfg {Number} width The width of the box that displays the selected element
43973      */ 
43974     width:          300,
43975
43976     
43977     
43978     /**
43979      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43980      */
43981     name : false,
43982     /**
43983      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43984      */
43985     hiddenName : false,
43986       /**
43987      * @cfg {String} seperator    The value seperator normally ',' 
43988      */
43989     seperator : ',',
43990     
43991     // private the array of items that are displayed..
43992     items  : false,
43993     // private - the hidden field el.
43994     hiddenEl : false,
43995     // private - the filed el..
43996     el : false,
43997     
43998     //validateValue : function() { return true; }, // all values are ok!
43999     //onAddClick: function() { },
44000     
44001     onRender : function(ct, position) 
44002     {
44003         
44004         // create the standard hidden element
44005         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
44006         
44007         
44008         // give fake names to child combo;
44009         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
44010         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
44011         
44012         this.combo = Roo.factory(this.combo, Roo.form);
44013         this.combo.onRender(ct, position);
44014         if (typeof(this.combo.width) != 'undefined') {
44015             this.combo.onResize(this.combo.width,0);
44016         }
44017         
44018         this.combo.initEvents();
44019         
44020         // assigned so form know we need to do this..
44021         this.store          = this.combo.store;
44022         this.valueField     = this.combo.valueField;
44023         this.displayField   = this.combo.displayField ;
44024         
44025         
44026         this.combo.wrap.addClass('x-cbarray-grp');
44027         
44028         var cbwrap = this.combo.wrap.createChild(
44029             {tag: 'div', cls: 'x-cbarray-cb'},
44030             this.combo.el.dom
44031         );
44032         
44033              
44034         this.hiddenEl = this.combo.wrap.createChild({
44035             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
44036         });
44037         this.el = this.combo.wrap.createChild({
44038             tag: 'input',  type:'hidden' , name: this.name, value : ''
44039         });
44040          //   this.el.dom.removeAttribute("name");
44041         
44042         
44043         this.outerWrap = this.combo.wrap;
44044         this.wrap = cbwrap;
44045         
44046         this.outerWrap.setWidth(this.width);
44047         this.outerWrap.dom.removeChild(this.el.dom);
44048         
44049         this.wrap.dom.appendChild(this.el.dom);
44050         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
44051         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
44052         
44053         this.combo.trigger.setStyle('position','relative');
44054         this.combo.trigger.setStyle('left', '0px');
44055         this.combo.trigger.setStyle('top', '2px');
44056         
44057         this.combo.el.setStyle('vertical-align', 'text-bottom');
44058         
44059         //this.trigger.setStyle('vertical-align', 'top');
44060         
44061         // this should use the code from combo really... on('add' ....)
44062         if (this.adder) {
44063             
44064         
44065             this.adder = this.outerWrap.createChild(
44066                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
44067             var _t = this;
44068             this.adder.on('click', function(e) {
44069                 _t.fireEvent('adderclick', this, e);
44070             }, _t);
44071         }
44072         //var _t = this;
44073         //this.adder.on('click', this.onAddClick, _t);
44074         
44075         
44076         this.combo.on('select', function(cb, rec, ix) {
44077             this.addItem(rec.data);
44078             
44079             cb.setValue('');
44080             cb.el.dom.value = '';
44081             //cb.lastData = rec.data;
44082             // add to list
44083             
44084         }, this);
44085         
44086         
44087     },
44088     
44089     
44090     getName: function()
44091     {
44092         // returns hidden if it's set..
44093         if (!this.rendered) {return ''};
44094         return  this.hiddenName ? this.hiddenName : this.name;
44095         
44096     },
44097     
44098     
44099     onResize: function(w, h){
44100         
44101         return;
44102         // not sure if this is needed..
44103         //this.combo.onResize(w,h);
44104         
44105         if(typeof w != 'number'){
44106             // we do not handle it!?!?
44107             return;
44108         }
44109         var tw = this.combo.trigger.getWidth();
44110         tw += this.addicon ? this.addicon.getWidth() : 0;
44111         tw += this.editicon ? this.editicon.getWidth() : 0;
44112         var x = w - tw;
44113         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
44114             
44115         this.combo.trigger.setStyle('left', '0px');
44116         
44117         if(this.list && this.listWidth === undefined){
44118             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
44119             this.list.setWidth(lw);
44120             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
44121         }
44122         
44123     
44124         
44125     },
44126     
44127     addItem: function(rec)
44128     {
44129         var valueField = this.combo.valueField;
44130         var displayField = this.combo.displayField;
44131         
44132         if (this.items.indexOfKey(rec[valueField]) > -1) {
44133             //console.log("GOT " + rec.data.id);
44134             return;
44135         }
44136         
44137         var x = new Roo.form.ComboBoxArray.Item({
44138             //id : rec[this.idField],
44139             data : rec,
44140             displayField : displayField ,
44141             tipField : displayField ,
44142             cb : this
44143         });
44144         // use the 
44145         this.items.add(rec[valueField],x);
44146         // add it before the element..
44147         this.updateHiddenEl();
44148         x.render(this.outerWrap, this.wrap.dom);
44149         // add the image handler..
44150     },
44151     
44152     updateHiddenEl : function()
44153     {
44154         this.validate();
44155         if (!this.hiddenEl) {
44156             return;
44157         }
44158         var ar = [];
44159         var idField = this.combo.valueField;
44160         
44161         this.items.each(function(f) {
44162             ar.push(f.data[idField]);
44163         });
44164         this.hiddenEl.dom.value = ar.join(this.seperator);
44165         this.validate();
44166     },
44167     
44168     reset : function()
44169     {
44170         this.items.clear();
44171         
44172         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
44173            el.remove();
44174         });
44175         
44176         this.el.dom.value = '';
44177         if (this.hiddenEl) {
44178             this.hiddenEl.dom.value = '';
44179         }
44180         
44181     },
44182     getValue: function()
44183     {
44184         return this.hiddenEl ? this.hiddenEl.dom.value : '';
44185     },
44186     setValue: function(v) // not a valid action - must use addItems..
44187     {
44188         
44189         this.reset();
44190          
44191         if (this.store.isLocal && (typeof(v) == 'string')) {
44192             // then we can use the store to find the values..
44193             // comma seperated at present.. this needs to allow JSON based encoding..
44194             this.hiddenEl.value  = v;
44195             var v_ar = [];
44196             Roo.each(v.split(this.seperator), function(k) {
44197                 Roo.log("CHECK " + this.valueField + ',' + k);
44198                 var li = this.store.query(this.valueField, k);
44199                 if (!li.length) {
44200                     return;
44201                 }
44202                 var add = {};
44203                 add[this.valueField] = k;
44204                 add[this.displayField] = li.item(0).data[this.displayField];
44205                 
44206                 this.addItem(add);
44207             }, this) 
44208              
44209         }
44210         if (typeof(v) == 'object' ) {
44211             // then let's assume it's an array of objects..
44212             Roo.each(v, function(l) {
44213                 var add = l;
44214                 if (typeof(l) == 'string') {
44215                     add = {};
44216                     add[this.valueField] = l;
44217                     add[this.displayField] = l
44218                 }
44219                 this.addItem(add);
44220             }, this);
44221              
44222         }
44223         
44224         
44225     },
44226     setFromData: function(v)
44227     {
44228         // this recieves an object, if setValues is called.
44229         this.reset();
44230         this.el.dom.value = v[this.displayField];
44231         this.hiddenEl.dom.value = v[this.valueField];
44232         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44233             return;
44234         }
44235         var kv = v[this.valueField];
44236         var dv = v[this.displayField];
44237         kv = typeof(kv) != 'string' ? '' : kv;
44238         dv = typeof(dv) != 'string' ? '' : dv;
44239         
44240         
44241         var keys = kv.split(this.seperator);
44242         var display = dv.split(this.seperator);
44243         for (var i = 0 ; i < keys.length; i++) {
44244             add = {};
44245             add[this.valueField] = keys[i];
44246             add[this.displayField] = display[i];
44247             this.addItem(add);
44248         }
44249       
44250         
44251     },
44252     
44253     /**
44254      * Validates the combox array value
44255      * @return {Boolean} True if the value is valid, else false
44256      */
44257     validate : function(){
44258         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44259             this.clearInvalid();
44260             return true;
44261         }
44262         return false;
44263     },
44264     
44265     validateValue : function(value){
44266         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44267         
44268     },
44269     
44270     /*@
44271      * overide
44272      * 
44273      */
44274     isDirty : function() {
44275         if(this.disabled) {
44276             return false;
44277         }
44278         
44279         try {
44280             var d = Roo.decode(String(this.originalValue));
44281         } catch (e) {
44282             return String(this.getValue()) !== String(this.originalValue);
44283         }
44284         
44285         var originalValue = [];
44286         
44287         for (var i = 0; i < d.length; i++){
44288             originalValue.push(d[i][this.valueField]);
44289         }
44290         
44291         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44292         
44293     }
44294     
44295 });
44296
44297
44298
44299 /**
44300  * @class Roo.form.ComboBoxArray.Item
44301  * @extends Roo.BoxComponent
44302  * A selected item in the list
44303  *  Fred [x]  Brian [x]  [Pick another |v]
44304  * 
44305  * @constructor
44306  * Create a new item.
44307  * @param {Object} config Configuration options
44308  */
44309  
44310 Roo.form.ComboBoxArray.Item = function(config) {
44311     config.id = Roo.id();
44312     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44313 }
44314
44315 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44316     data : {},
44317     cb: false,
44318     displayField : false,
44319     tipField : false,
44320     
44321     
44322     defaultAutoCreate : {
44323         tag: 'div',
44324         cls: 'x-cbarray-item',
44325         cn : [ 
44326             { tag: 'div' },
44327             {
44328                 tag: 'img',
44329                 width:16,
44330                 height : 16,
44331                 src : Roo.BLANK_IMAGE_URL ,
44332                 align: 'center'
44333             }
44334         ]
44335         
44336     },
44337     
44338  
44339     onRender : function(ct, position)
44340     {
44341         Roo.form.Field.superclass.onRender.call(this, ct, position);
44342         
44343         if(!this.el){
44344             var cfg = this.getAutoCreate();
44345             this.el = ct.createChild(cfg, position);
44346         }
44347         
44348         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44349         
44350         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44351             this.cb.renderer(this.data) :
44352             String.format('{0}',this.data[this.displayField]);
44353         
44354             
44355         this.el.child('div').dom.setAttribute('qtip',
44356                         String.format('{0}',this.data[this.tipField])
44357         );
44358         
44359         this.el.child('img').on('click', this.remove, this);
44360         
44361     },
44362    
44363     remove : function()
44364     {
44365         if(this.cb.disabled){
44366             return;
44367         }
44368         
44369         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44370             this.cb.items.remove(this);
44371             this.el.child('img').un('click', this.remove, this);
44372             this.el.remove();
44373             this.cb.updateHiddenEl();
44374
44375             this.cb.fireEvent('remove', this.cb, this);
44376         }
44377         
44378     }
44379 });/*
44380  * RooJS Library 1.1.1
44381  * Copyright(c) 2008-2011  Alan Knowles
44382  *
44383  * License - LGPL
44384  */
44385  
44386
44387 /**
44388  * @class Roo.form.ComboNested
44389  * @extends Roo.form.ComboBox
44390  * A combobox for that allows selection of nested items in a list,
44391  * eg.
44392  *
44393  *  Book
44394  *    -> red
44395  *    -> green
44396  *  Table
44397  *    -> square
44398  *      ->red
44399  *      ->green
44400  *    -> rectangle
44401  *      ->green
44402  *      
44403  * 
44404  * @constructor
44405  * Create a new ComboNested
44406  * @param {Object} config Configuration options
44407  */
44408 Roo.form.ComboNested = function(config){
44409     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44410     // should verify some data...
44411     // like
44412     // hiddenName = required..
44413     // displayField = required
44414     // valudField == required
44415     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44416     var _t = this;
44417     Roo.each(req, function(e) {
44418         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44419             throw "Roo.form.ComboNested : missing value for: " + e;
44420         }
44421     });
44422      
44423     
44424 };
44425
44426 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44427    
44428     /*
44429      * @config {Number} max Number of columns to show
44430      */
44431     
44432     maxColumns : 3,
44433    
44434     list : null, // the outermost div..
44435     innerLists : null, // the
44436     views : null,
44437     stores : null,
44438     // private
44439     loadingChildren : false,
44440     
44441     onRender : function(ct, position)
44442     {
44443         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44444         
44445         if(this.hiddenName){
44446             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44447                     'before', true);
44448             this.hiddenField.value =
44449                 this.hiddenValue !== undefined ? this.hiddenValue :
44450                 this.value !== undefined ? this.value : '';
44451
44452             // prevent input submission
44453             this.el.dom.removeAttribute('name');
44454              
44455              
44456         }
44457         
44458         if(Roo.isGecko){
44459             this.el.dom.setAttribute('autocomplete', 'off');
44460         }
44461
44462         var cls = 'x-combo-list';
44463
44464         this.list = new Roo.Layer({
44465             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44466         });
44467
44468         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44469         this.list.setWidth(lw);
44470         this.list.swallowEvent('mousewheel');
44471         this.assetHeight = 0;
44472
44473         if(this.title){
44474             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44475             this.assetHeight += this.header.getHeight();
44476         }
44477         this.innerLists = [];
44478         this.views = [];
44479         this.stores = [];
44480         for (var i =0 ; i < this.maxColumns; i++) {
44481             this.onRenderList( cls, i);
44482         }
44483         
44484         // always needs footer, as we are going to have an 'OK' button.
44485         this.footer = this.list.createChild({cls:cls+'-ft'});
44486         this.pageTb = new Roo.Toolbar(this.footer);  
44487         var _this = this;
44488         this.pageTb.add(  {
44489             
44490             text: 'Done',
44491             handler: function()
44492             {
44493                 _this.collapse();
44494             }
44495         });
44496         
44497         if ( this.allowBlank && !this.disableClear) {
44498             
44499             this.pageTb.add(new Roo.Toolbar.Fill(), {
44500                 cls: 'x-btn-icon x-btn-clear',
44501                 text: '&#160;',
44502                 handler: function()
44503                 {
44504                     _this.collapse();
44505                     _this.clearValue();
44506                     _this.onSelect(false, -1);
44507                 }
44508             });
44509         }
44510         if (this.footer) {
44511             this.assetHeight += this.footer.getHeight();
44512         }
44513         
44514     },
44515     onRenderList : function (  cls, i)
44516     {
44517         
44518         var lw = Math.floor(
44519                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44520         );
44521         
44522         this.list.setWidth(lw); // default to '1'
44523
44524         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44525         //il.on('mouseover', this.onViewOver, this, { list:  i });
44526         //il.on('mousemove', this.onViewMove, this, { list:  i });
44527         il.setWidth(lw);
44528         il.setStyle({ 'overflow-x' : 'hidden'});
44529
44530         if(!this.tpl){
44531             this.tpl = new Roo.Template({
44532                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44533                 isEmpty: function (value, allValues) {
44534                     //Roo.log(value);
44535                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44536                     return dl ? 'has-children' : 'no-children'
44537                 }
44538             });
44539         }
44540         
44541         var store  = this.store;
44542         if (i > 0) {
44543             store  = new Roo.data.SimpleStore({
44544                 //fields : this.store.reader.meta.fields,
44545                 reader : this.store.reader,
44546                 data : [ ]
44547             });
44548         }
44549         this.stores[i]  = store;
44550                   
44551         var view = this.views[i] = new Roo.View(
44552             il,
44553             this.tpl,
44554             {
44555                 singleSelect:true,
44556                 store: store,
44557                 selectedClass: this.selectedClass
44558             }
44559         );
44560         view.getEl().setWidth(lw);
44561         view.getEl().setStyle({
44562             position: i < 1 ? 'relative' : 'absolute',
44563             top: 0,
44564             left: (i * lw ) + 'px',
44565             display : i > 0 ? 'none' : 'block'
44566         });
44567         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44568         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44569         //view.on('click', this.onViewClick, this, { list : i });
44570
44571         store.on('beforeload', this.onBeforeLoad, this);
44572         store.on('load',  this.onLoad, this, { list  : i});
44573         store.on('loadexception', this.onLoadException, this);
44574
44575         // hide the other vies..
44576         
44577         
44578         
44579     },
44580       
44581     restrictHeight : function()
44582     {
44583         var mh = 0;
44584         Roo.each(this.innerLists, function(il,i) {
44585             var el = this.views[i].getEl();
44586             el.dom.style.height = '';
44587             var inner = el.dom;
44588             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44589             // only adjust heights on other ones..
44590             mh = Math.max(h, mh);
44591             if (i < 1) {
44592                 
44593                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44594                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44595                
44596             }
44597             
44598             
44599         }, this);
44600         
44601         this.list.beginUpdate();
44602         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44603         this.list.alignTo(this.el, this.listAlign);
44604         this.list.endUpdate();
44605         
44606     },
44607      
44608     
44609     // -- store handlers..
44610     // private
44611     onBeforeLoad : function()
44612     {
44613         if(!this.hasFocus){
44614             return;
44615         }
44616         this.innerLists[0].update(this.loadingText ?
44617                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44618         this.restrictHeight();
44619         this.selectedIndex = -1;
44620     },
44621     // private
44622     onLoad : function(a,b,c,d)
44623     {
44624         if (!this.loadingChildren) {
44625             // then we are loading the top level. - hide the children
44626             for (var i = 1;i < this.views.length; i++) {
44627                 this.views[i].getEl().setStyle({ display : 'none' });
44628             }
44629             var lw = Math.floor(
44630                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44631             );
44632         
44633              this.list.setWidth(lw); // default to '1'
44634
44635             
44636         }
44637         if(!this.hasFocus){
44638             return;
44639         }
44640         
44641         if(this.store.getCount() > 0) {
44642             this.expand();
44643             this.restrictHeight();   
44644         } else {
44645             this.onEmptyResults();
44646         }
44647         
44648         if (!this.loadingChildren) {
44649             this.selectActive();
44650         }
44651         /*
44652         this.stores[1].loadData([]);
44653         this.stores[2].loadData([]);
44654         this.views
44655         */    
44656     
44657         //this.el.focus();
44658     },
44659     
44660     
44661     // private
44662     onLoadException : function()
44663     {
44664         this.collapse();
44665         Roo.log(this.store.reader.jsonData);
44666         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44667             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44668         }
44669         
44670         
44671     },
44672     // no cleaning of leading spaces on blur here.
44673     cleanLeadingSpace : function(e) { },
44674     
44675
44676     onSelectChange : function (view, sels, opts )
44677     {
44678         var ix = view.getSelectedIndexes();
44679          
44680         if (opts.list > this.maxColumns - 2) {
44681             if (view.store.getCount()<  1) {
44682                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44683
44684             } else  {
44685                 if (ix.length) {
44686                     // used to clear ?? but if we are loading unselected 
44687                     this.setFromData(view.store.getAt(ix[0]).data);
44688                 }
44689                 
44690             }
44691             
44692             return;
44693         }
44694         
44695         if (!ix.length) {
44696             // this get's fired when trigger opens..
44697            // this.setFromData({});
44698             var str = this.stores[opts.list+1];
44699             str.data.clear(); // removeall wihtout the fire events..
44700             return;
44701         }
44702         
44703         var rec = view.store.getAt(ix[0]);
44704          
44705         this.setFromData(rec.data);
44706         this.fireEvent('select', this, rec, ix[0]);
44707         
44708         var lw = Math.floor(
44709              (
44710                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44711              ) / this.maxColumns
44712         );
44713         this.loadingChildren = true;
44714         this.stores[opts.list+1].loadDataFromChildren( rec );
44715         this.loadingChildren = false;
44716         var dl = this.stores[opts.list+1]. getTotalCount();
44717         
44718         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44719         
44720         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44721         for (var i = opts.list+2; i < this.views.length;i++) {
44722             this.views[i].getEl().setStyle({ display : 'none' });
44723         }
44724         
44725         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44726         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44727         
44728         if (this.isLoading) {
44729            // this.selectActive(opts.list);
44730         }
44731          
44732     },
44733     
44734     
44735     
44736     
44737     onDoubleClick : function()
44738     {
44739         this.collapse(); //??
44740     },
44741     
44742      
44743     
44744     
44745     
44746     // private
44747     recordToStack : function(store, prop, value, stack)
44748     {
44749         var cstore = new Roo.data.SimpleStore({
44750             //fields : this.store.reader.meta.fields, // we need array reader.. for
44751             reader : this.store.reader,
44752             data : [ ]
44753         });
44754         var _this = this;
44755         var record  = false;
44756         var srec = false;
44757         if(store.getCount() < 1){
44758             return false;
44759         }
44760         store.each(function(r){
44761             if(r.data[prop] == value){
44762                 record = r;
44763             srec = r;
44764                 return false;
44765             }
44766             if (r.data.cn && r.data.cn.length) {
44767                 cstore.loadDataFromChildren( r);
44768                 var cret = _this.recordToStack(cstore, prop, value, stack);
44769                 if (cret !== false) {
44770                     record = cret;
44771                     srec = r;
44772                     return false;
44773                 }
44774             }
44775              
44776             return true;
44777         });
44778         if (record == false) {
44779             return false
44780         }
44781         stack.unshift(srec);
44782         return record;
44783     },
44784     
44785     /*
44786      * find the stack of stores that match our value.
44787      *
44788      * 
44789      */
44790     
44791     selectActive : function ()
44792     {
44793         // if store is not loaded, then we will need to wait for that to happen first.
44794         var stack = [];
44795         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44796         for (var i = 0; i < stack.length; i++ ) {
44797             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44798         }
44799         
44800     }
44801         
44802          
44803     
44804     
44805     
44806     
44807 });/*
44808  * Based on:
44809  * Ext JS Library 1.1.1
44810  * Copyright(c) 2006-2007, Ext JS, LLC.
44811  *
44812  * Originally Released Under LGPL - original licence link has changed is not relivant.
44813  *
44814  * Fork - LGPL
44815  * <script type="text/javascript">
44816  */
44817 /**
44818  * @class Roo.form.Checkbox
44819  * @extends Roo.form.Field
44820  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44821  * @constructor
44822  * Creates a new Checkbox
44823  * @param {Object} config Configuration options
44824  */
44825 Roo.form.Checkbox = function(config){
44826     Roo.form.Checkbox.superclass.constructor.call(this, config);
44827     this.addEvents({
44828         /**
44829          * @event check
44830          * Fires when the checkbox is checked or unchecked.
44831              * @param {Roo.form.Checkbox} this This checkbox
44832              * @param {Boolean} checked The new checked value
44833              */
44834         check : true
44835     });
44836 };
44837
44838 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44839     /**
44840      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44841      */
44842     focusClass : undefined,
44843     /**
44844      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44845      */
44846     fieldClass: "x-form-field",
44847     /**
44848      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44849      */
44850     checked: false,
44851     /**
44852      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44853      * {tag: "input", type: "checkbox", autocomplete: "off"})
44854      */
44855     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44856     /**
44857      * @cfg {String} boxLabel The text that appears beside the checkbox
44858      */
44859     boxLabel : "",
44860     /**
44861      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44862      */  
44863     inputValue : '1',
44864     /**
44865      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44866      */
44867      valueOff: '0', // value when not checked..
44868
44869     actionMode : 'viewEl', 
44870     //
44871     // private
44872     itemCls : 'x-menu-check-item x-form-item',
44873     groupClass : 'x-menu-group-item',
44874     inputType : 'hidden',
44875     
44876     
44877     inSetChecked: false, // check that we are not calling self...
44878     
44879     inputElement: false, // real input element?
44880     basedOn: false, // ????
44881     
44882     isFormField: true, // not sure where this is needed!!!!
44883
44884     onResize : function(){
44885         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44886         if(!this.boxLabel){
44887             this.el.alignTo(this.wrap, 'c-c');
44888         }
44889     },
44890
44891     initEvents : function(){
44892         Roo.form.Checkbox.superclass.initEvents.call(this);
44893         this.el.on("click", this.onClick,  this);
44894         this.el.on("change", this.onClick,  this);
44895     },
44896
44897
44898     getResizeEl : function(){
44899         return this.wrap;
44900     },
44901
44902     getPositionEl : function(){
44903         return this.wrap;
44904     },
44905
44906     // private
44907     onRender : function(ct, position){
44908         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44909         /*
44910         if(this.inputValue !== undefined){
44911             this.el.dom.value = this.inputValue;
44912         }
44913         */
44914         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44915         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44916         var viewEl = this.wrap.createChild({ 
44917             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44918         this.viewEl = viewEl;   
44919         this.wrap.on('click', this.onClick,  this); 
44920         
44921         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44922         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44923         
44924         
44925         
44926         if(this.boxLabel){
44927             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44928         //    viewEl.on('click', this.onClick,  this); 
44929         }
44930         //if(this.checked){
44931             this.setChecked(this.checked);
44932         //}else{
44933             //this.checked = this.el.dom;
44934         //}
44935
44936     },
44937
44938     // private
44939     initValue : Roo.emptyFn,
44940
44941     /**
44942      * Returns the checked state of the checkbox.
44943      * @return {Boolean} True if checked, else false
44944      */
44945     getValue : function(){
44946         if(this.el){
44947             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44948         }
44949         return this.valueOff;
44950         
44951     },
44952
44953         // private
44954     onClick : function(){ 
44955         if (this.disabled) {
44956             return;
44957         }
44958         this.setChecked(!this.checked);
44959
44960         //if(this.el.dom.checked != this.checked){
44961         //    this.setValue(this.el.dom.checked);
44962        // }
44963     },
44964
44965     /**
44966      * Sets the checked state of the checkbox.
44967      * On is always based on a string comparison between inputValue and the param.
44968      * @param {Boolean/String} value - the value to set 
44969      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44970      */
44971     setValue : function(v,suppressEvent){
44972         
44973         
44974         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44975         //if(this.el && this.el.dom){
44976         //    this.el.dom.checked = this.checked;
44977         //    this.el.dom.defaultChecked = this.checked;
44978         //}
44979         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44980         //this.fireEvent("check", this, this.checked);
44981     },
44982     // private..
44983     setChecked : function(state,suppressEvent)
44984     {
44985         if (this.inSetChecked) {
44986             this.checked = state;
44987             return;
44988         }
44989         
44990     
44991         if(this.wrap){
44992             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44993         }
44994         this.checked = state;
44995         if(suppressEvent !== true){
44996             this.fireEvent('check', this, state);
44997         }
44998         this.inSetChecked = true;
44999         this.el.dom.value = state ? this.inputValue : this.valueOff;
45000         this.inSetChecked = false;
45001         
45002     },
45003     // handle setting of hidden value by some other method!!?!?
45004     setFromHidden: function()
45005     {
45006         if(!this.el){
45007             return;
45008         }
45009         //console.log("SET FROM HIDDEN");
45010         //alert('setFrom hidden');
45011         this.setValue(this.el.dom.value);
45012     },
45013     
45014     onDestroy : function()
45015     {
45016         if(this.viewEl){
45017             Roo.get(this.viewEl).remove();
45018         }
45019          
45020         Roo.form.Checkbox.superclass.onDestroy.call(this);
45021     },
45022     
45023     setBoxLabel : function(str)
45024     {
45025         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
45026     }
45027
45028 });/*
45029  * Based on:
45030  * Ext JS Library 1.1.1
45031  * Copyright(c) 2006-2007, Ext JS, LLC.
45032  *
45033  * Originally Released Under LGPL - original licence link has changed is not relivant.
45034  *
45035  * Fork - LGPL
45036  * <script type="text/javascript">
45037  */
45038  
45039 /**
45040  * @class Roo.form.Radio
45041  * @extends Roo.form.Checkbox
45042  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
45043  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
45044  * @constructor
45045  * Creates a new Radio
45046  * @param {Object} config Configuration options
45047  */
45048 Roo.form.Radio = function(){
45049     Roo.form.Radio.superclass.constructor.apply(this, arguments);
45050 };
45051 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
45052     inputType: 'radio',
45053
45054     /**
45055      * If this radio is part of a group, it will return the selected value
45056      * @return {String}
45057      */
45058     getGroupValue : function(){
45059         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
45060     },
45061     
45062     
45063     onRender : function(ct, position){
45064         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45065         
45066         if(this.inputValue !== undefined){
45067             this.el.dom.value = this.inputValue;
45068         }
45069          
45070         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
45071         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
45072         //var viewEl = this.wrap.createChild({ 
45073         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
45074         //this.viewEl = viewEl;   
45075         //this.wrap.on('click', this.onClick,  this); 
45076         
45077         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45078         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
45079         
45080         
45081         
45082         if(this.boxLabel){
45083             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
45084         //    viewEl.on('click', this.onClick,  this); 
45085         }
45086          if(this.checked){
45087             this.el.dom.checked =   'checked' ;
45088         }
45089          
45090     } 
45091     
45092     
45093 });Roo.rtf = {}; // namespace
45094 Roo.rtf.Hex = function(hex)
45095 {
45096     this.hexstr = hex;
45097 };
45098 Roo.rtf.Paragraph = function(opts)
45099 {
45100     this.content = []; ///??? is that used?
45101 };Roo.rtf.Span = function(opts)
45102 {
45103     this.value = opts.value;
45104 };
45105
45106 Roo.rtf.Group = function(parent)
45107 {
45108     // we dont want to acutally store parent - it will make debug a nightmare..
45109     this.content = [];
45110     this.cn  = [];
45111      
45112        
45113     
45114 };
45115
45116 Roo.rtf.Group.prototype = {
45117     ignorable : false,
45118     content: false,
45119     cn: false,
45120     addContent : function(node) {
45121         // could set styles...
45122         this.content.push(node);
45123     },
45124     addChild : function(cn)
45125     {
45126         this.cn.push(cn);
45127     },
45128     // only for images really...
45129     toDataURL : function()
45130     {
45131         var mimetype = false;
45132         switch(true) {
45133             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
45134                 mimetype = "image/png";
45135                 break;
45136              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
45137                 mimetype = "image/jpeg";
45138                 break;
45139             default :
45140                 return 'about:blank'; // ?? error?
45141         }
45142         
45143         
45144         var hexstring = this.content[this.content.length-1].value;
45145         
45146         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
45147             return String.fromCharCode(parseInt(a, 16));
45148         }).join(""));
45149     }
45150     
45151 };
45152 // this looks like it's normally the {rtf{ .... }}
45153 Roo.rtf.Document = function()
45154 {
45155     // we dont want to acutally store parent - it will make debug a nightmare..
45156     this.rtlch  = [];
45157     this.content = [];
45158     this.cn = [];
45159     
45160 };
45161 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
45162     addChild : function(cn)
45163     {
45164         this.cn.push(cn);
45165         switch(cn.type) {
45166             case 'rtlch': // most content seems to be inside this??
45167             case 'listtext':
45168             case 'shpinst':
45169                 this.rtlch.push(cn);
45170                 return;
45171             default:
45172                 this[cn.type] = cn;
45173         }
45174         
45175     },
45176     
45177     getElementsByType : function(type)
45178     {
45179         var ret =  [];
45180         this._getElementsByType(type, ret, this.cn, 'rtf');
45181         return ret;
45182     },
45183     _getElementsByType : function (type, ret, search_array, path)
45184     {
45185         search_array.forEach(function(n,i) {
45186             if (n.type == type) {
45187                 n.path = path + '/' + n.type + ':' + i;
45188                 ret.push(n);
45189             }
45190             if (n.cn.length > 0) {
45191                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
45192             }
45193         },this);
45194     }
45195     
45196 });
45197  
45198 Roo.rtf.Ctrl = function(opts)
45199 {
45200     this.value = opts.value;
45201     this.param = opts.param;
45202 };
45203 /**
45204  *
45205  *
45206  * based on this https://github.com/iarna/rtf-parser
45207  * it's really only designed to extract pict from pasted RTF 
45208  *
45209  * usage:
45210  *
45211  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45212  *  
45213  *
45214  */
45215
45216  
45217
45218
45219
45220 Roo.rtf.Parser = function(text) {
45221     //super({objectMode: true})
45222     this.text = '';
45223     this.parserState = this.parseText;
45224     
45225     // these are for interpeter...
45226     this.doc = {};
45227     ///this.parserState = this.parseTop
45228     this.groupStack = [];
45229     this.hexStore = [];
45230     this.doc = false;
45231     
45232     this.groups = []; // where we put the return.
45233     
45234     for (var ii = 0; ii < text.length; ++ii) {
45235         ++this.cpos;
45236         
45237         if (text[ii] === '\n') {
45238             ++this.row;
45239             this.col = 1;
45240         } else {
45241             ++this.col;
45242         }
45243         this.parserState(text[ii]);
45244     }
45245     
45246     
45247     
45248 };
45249 Roo.rtf.Parser.prototype = {
45250     text : '', // string being parsed..
45251     controlWord : '',
45252     controlWordParam :  '',
45253     hexChar : '',
45254     doc : false,
45255     group: false,
45256     groupStack : false,
45257     hexStore : false,
45258     
45259     
45260     cpos : 0, 
45261     row : 1, // reportin?
45262     col : 1, //
45263
45264      
45265     push : function (el)
45266     {
45267         var m = 'cmd'+ el.type;
45268         if (typeof(this[m]) == 'undefined') {
45269             Roo.log('invalid cmd:' + el.type);
45270             return;
45271         }
45272         this[m](el);
45273         //Roo.log(el);
45274     },
45275     flushHexStore : function()
45276     {
45277         if (this.hexStore.length < 1) {
45278             return;
45279         }
45280         var hexstr = this.hexStore.map(
45281             function(cmd) {
45282                 return cmd.value;
45283         }).join('');
45284         
45285         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45286               
45287             
45288         this.hexStore.splice(0)
45289         
45290     },
45291     
45292     cmdgroupstart : function()
45293     {
45294         this.flushHexStore();
45295         if (this.group) {
45296             this.groupStack.push(this.group);
45297         }
45298          // parent..
45299         if (this.doc === false) {
45300             this.group = this.doc = new Roo.rtf.Document();
45301             return;
45302             
45303         }
45304         this.group = new Roo.rtf.Group(this.group);
45305     },
45306     cmdignorable : function()
45307     {
45308         this.flushHexStore();
45309         this.group.ignorable = true;
45310     },
45311     cmdendparagraph : function()
45312     {
45313         this.flushHexStore();
45314         this.group.addContent(new Roo.rtf.Paragraph());
45315     },
45316     cmdgroupend : function ()
45317     {
45318         this.flushHexStore();
45319         var endingGroup = this.group;
45320         
45321         
45322         this.group = this.groupStack.pop();
45323         if (this.group) {
45324             this.group.addChild(endingGroup);
45325         }
45326         
45327         
45328         
45329         var doc = this.group || this.doc;
45330         //if (endingGroup instanceof FontTable) {
45331         //  doc.fonts = endingGroup.table
45332         //} else if (endingGroup instanceof ColorTable) {
45333         //  doc.colors = endingGroup.table
45334         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45335         if (endingGroup.ignorable === false) {
45336             //code
45337             this.groups.push(endingGroup);
45338            // Roo.log( endingGroup );
45339         }
45340             //Roo.each(endingGroup.content, function(item)) {
45341             //    doc.addContent(item);
45342             //}
45343             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45344         //}
45345     },
45346     cmdtext : function (cmd)
45347     {
45348         this.flushHexStore();
45349         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45350             //this.group = this.doc
45351             return;  // we really don't care about stray text...
45352         }
45353         this.group.addContent(new Roo.rtf.Span(cmd));
45354     },
45355     cmdcontrolword : function (cmd)
45356     {
45357         this.flushHexStore();
45358         if (!this.group.type) {
45359             this.group.type = cmd.value;
45360             return;
45361         }
45362         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45363         // we actually don't care about ctrl words...
45364         return ;
45365         /*
45366         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45367         if (this[method]) {
45368             this[method](cmd.param)
45369         } else {
45370             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45371         }
45372         */
45373     },
45374     cmdhexchar : function(cmd) {
45375         this.hexStore.push(cmd);
45376     },
45377     cmderror : function(cmd) {
45378         throw new Exception (cmd.value);
45379     },
45380     
45381     /*
45382       _flush (done) {
45383         if (this.text !== '\u0000') this.emitText()
45384         done()
45385       }
45386       */
45387       
45388       
45389     parseText : function(c)
45390     {
45391         if (c === '\\') {
45392             this.parserState = this.parseEscapes;
45393         } else if (c === '{') {
45394             this.emitStartGroup();
45395         } else if (c === '}') {
45396             this.emitEndGroup();
45397         } else if (c === '\x0A' || c === '\x0D') {
45398             // cr/lf are noise chars
45399         } else {
45400             this.text += c;
45401         }
45402     },
45403     
45404     parseEscapes: function (c)
45405     {
45406         if (c === '\\' || c === '{' || c === '}') {
45407             this.text += c;
45408             this.parserState = this.parseText;
45409         } else {
45410             this.parserState = this.parseControlSymbol;
45411             this.parseControlSymbol(c);
45412         }
45413     },
45414     parseControlSymbol: function(c)
45415     {
45416         if (c === '~') {
45417             this.text += '\u00a0'; // nbsp
45418             this.parserState = this.parseText
45419         } else if (c === '-') {
45420              this.text += '\u00ad'; // soft hyphen
45421         } else if (c === '_') {
45422             this.text += '\u2011'; // non-breaking hyphen
45423         } else if (c === '*') {
45424             this.emitIgnorable();
45425             this.parserState = this.parseText;
45426         } else if (c === "'") {
45427             this.parserState = this.parseHexChar;
45428         } else if (c === '|') { // formula cacter
45429             this.emitFormula();
45430             this.parserState = this.parseText;
45431         } else if (c === ':') { // subentry in an index entry
45432             this.emitIndexSubEntry();
45433             this.parserState = this.parseText;
45434         } else if (c === '\x0a') {
45435             this.emitEndParagraph();
45436             this.parserState = this.parseText;
45437         } else if (c === '\x0d') {
45438             this.emitEndParagraph();
45439             this.parserState = this.parseText;
45440         } else {
45441             this.parserState = this.parseControlWord;
45442             this.parseControlWord(c);
45443         }
45444     },
45445     parseHexChar: function (c)
45446     {
45447         if (/^[A-Fa-f0-9]$/.test(c)) {
45448             this.hexChar += c;
45449             if (this.hexChar.length >= 2) {
45450               this.emitHexChar();
45451               this.parserState = this.parseText;
45452             }
45453             return;
45454         }
45455         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45456         this.parserState = this.parseText;
45457         
45458     },
45459     parseControlWord : function(c)
45460     {
45461         if (c === ' ') {
45462             this.emitControlWord();
45463             this.parserState = this.parseText;
45464         } else if (/^[-\d]$/.test(c)) {
45465             this.parserState = this.parseControlWordParam;
45466             this.controlWordParam += c;
45467         } else if (/^[A-Za-z]$/.test(c)) {
45468           this.controlWord += c;
45469         } else {
45470           this.emitControlWord();
45471           this.parserState = this.parseText;
45472           this.parseText(c);
45473         }
45474     },
45475     parseControlWordParam : function (c) {
45476         if (/^\d$/.test(c)) {
45477           this.controlWordParam += c;
45478         } else if (c === ' ') {
45479           this.emitControlWord();
45480           this.parserState = this.parseText;
45481         } else {
45482           this.emitControlWord();
45483           this.parserState = this.parseText;
45484           this.parseText(c);
45485         }
45486     },
45487     
45488     
45489     
45490     
45491     emitText : function () {
45492         if (this.text === '') {
45493             return;
45494         }
45495         this.push({
45496             type: 'text',
45497             value: this.text,
45498             pos: this.cpos,
45499             row: this.row,
45500             col: this.col
45501         });
45502         this.text = ''
45503     },
45504     emitControlWord : function ()
45505     {
45506         this.emitText();
45507         if (this.controlWord === '') {
45508             this.emitError('empty control word');
45509         } else {
45510             this.push({
45511                   type: 'controlword',
45512                   value: this.controlWord,
45513                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45514                   pos: this.cpos,
45515                   row: this.row,
45516                   col: this.col
45517             });
45518         }
45519         this.controlWord = '';
45520         this.controlWordParam = '';
45521     },
45522     emitStartGroup : function ()
45523     {
45524         this.emitText();
45525         this.push({
45526             type: 'groupstart',
45527             pos: this.cpos,
45528             row: this.row,
45529             col: this.col
45530         });
45531     },
45532     emitEndGroup : function ()
45533     {
45534         this.emitText();
45535         this.push({
45536             type: 'groupend',
45537             pos: this.cpos,
45538             row: this.row,
45539             col: this.col
45540         });
45541     },
45542     emitIgnorable : function ()
45543     {
45544         this.emitText();
45545         this.push({
45546             type: 'ignorable',
45547             pos: this.cpos,
45548             row: this.row,
45549             col: this.col
45550         });
45551     },
45552     emitHexChar : function ()
45553     {
45554         this.emitText();
45555         this.push({
45556             type: 'hexchar',
45557             value: this.hexChar,
45558             pos: this.cpos,
45559             row: this.row,
45560             col: this.col
45561         });
45562         this.hexChar = ''
45563     },
45564     emitError : function (message)
45565     {
45566       this.emitText();
45567       this.push({
45568             type: 'error',
45569             value: message,
45570             row: this.row,
45571             col: this.col,
45572             char: this.cpos //,
45573             //stack: new Error().stack
45574         });
45575     },
45576     emitEndParagraph : function () {
45577         this.emitText();
45578         this.push({
45579             type: 'endparagraph',
45580             pos: this.cpos,
45581             row: this.row,
45582             col: this.col
45583         });
45584     }
45585      
45586 } ;
45587 Roo.htmleditor = {};
45588  
45589 /**
45590  * @class Roo.htmleditor.Filter
45591  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45592  * @cfg {DomElement} node The node to iterate and filter
45593  * @cfg {boolean|String|Array} tag Tags to replace 
45594  * @constructor
45595  * Create a new Filter.
45596  * @param {Object} config Configuration options
45597  */
45598
45599
45600
45601 Roo.htmleditor.Filter = function(cfg) {
45602     Roo.apply(this.cfg);
45603     // this does not actually call walk as it's really just a abstract class
45604 }
45605
45606
45607 Roo.htmleditor.Filter.prototype = {
45608     
45609     node: false,
45610     
45611     tag: false,
45612
45613     // overrride to do replace comments.
45614     replaceComment : false,
45615     
45616     // overrride to do replace or do stuff with tags..
45617     replaceTag : false,
45618     
45619     walk : function(dom)
45620     {
45621         Roo.each( Array.from(dom.childNodes), function( e ) {
45622             switch(true) {
45623                 
45624                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
45625                     this.replaceComment(e);
45626                     return;
45627                 
45628                 case e.nodeType != 1: //not a node.
45629                     return;
45630                 
45631                 case this.tag === true: // everything
45632                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45633                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45634                     if (this.replaceTag && false === this.replaceTag(e)) {
45635                         return;
45636                     }
45637                     if (e.hasChildNodes()) {
45638                         this.walk(e);
45639                     }
45640                     return;
45641                 
45642                 default:    // tags .. that do not match.
45643                     if (e.hasChildNodes()) {
45644                         this.walk(e);
45645                     }
45646             }
45647             
45648         }, this);
45649         
45650     }
45651 }; 
45652
45653 /**
45654  * @class Roo.htmleditor.FilterAttributes
45655  * clean attributes and  styles including http:// etc.. in attribute
45656  * @constructor
45657 * Run a new Attribute Filter
45658 * @param {Object} config Configuration options
45659  */
45660 Roo.htmleditor.FilterAttributes = function(cfg)
45661 {
45662     Roo.apply(this, cfg);
45663     this.attrib_black = this.attrib_black || [];
45664     this.attrib_white = this.attrib_white || [];
45665
45666     this.attrib_clean = this.attrib_clean || [];
45667     this.style_white = this.style_white || [];
45668     this.style_black = this.style_black || [];
45669     this.walk(cfg.node);
45670 }
45671
45672 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45673 {
45674     tag: true, // all tags
45675     
45676     attrib_black : false, // array
45677     attrib_clean : false,
45678     attrib_white : false,
45679
45680     style_white : false,
45681     style_black : false,
45682      
45683      
45684     replaceTag : function(node)
45685     {
45686         if (!node.attributes || !node.attributes.length) {
45687             return true;
45688         }
45689         
45690         for (var i = node.attributes.length-1; i > -1 ; i--) {
45691             var a = node.attributes[i];
45692             //console.log(a);
45693             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45694                 node.removeAttribute(a.name);
45695                 continue;
45696             }
45697             
45698             
45699             
45700             if (a.name.toLowerCase().substr(0,2)=='on')  {
45701                 node.removeAttribute(a.name);
45702                 continue;
45703             }
45704             
45705             
45706             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45707                 node.removeAttribute(a.name);
45708                 continue;
45709             }
45710             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45711                 this.cleanAttr(node,a.name,a.value); // fixme..
45712                 continue;
45713             }
45714             if (a.name == 'style') {
45715                 this.cleanStyle(node,a.name,a.value);
45716                 continue;
45717             }
45718             /// clean up MS crap..
45719             // tecnically this should be a list of valid class'es..
45720             
45721             
45722             if (a.name == 'class') {
45723                 if (a.value.match(/^Mso/)) {
45724                     node.removeAttribute('class');
45725                 }
45726                 
45727                 if (a.value.match(/^body$/)) {
45728                     node.removeAttribute('class');
45729                 }
45730                 continue;
45731             }
45732             
45733             
45734             // style cleanup!?
45735             // class cleanup?
45736             
45737         }
45738         return true; // clean children
45739     },
45740         
45741     cleanAttr: function(node, n,v)
45742     {
45743         
45744         if (v.match(/^\./) || v.match(/^\//)) {
45745             return;
45746         }
45747         if (v.match(/^(http|https):\/\//)
45748             || v.match(/^mailto:/) 
45749             || v.match(/^ftp:/)
45750             || v.match(/^data:/)
45751             ) {
45752             return;
45753         }
45754         if (v.match(/^#/)) {
45755             return;
45756         }
45757         if (v.match(/^\{/)) { // allow template editing.
45758             return;
45759         }
45760 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45761         node.removeAttribute(n);
45762         
45763     },
45764     cleanStyle : function(node,  n,v)
45765     {
45766         if (v.match(/expression/)) { //XSS?? should we even bother..
45767             node.removeAttribute(n);
45768             return;
45769         }
45770         
45771         var parts = v.split(/;/);
45772         var clean = [];
45773         
45774         Roo.each(parts, function(p) {
45775             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45776             if (!p.length) {
45777                 return true;
45778             }
45779             var l = p.split(':').shift().replace(/\s+/g,'');
45780             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45781             
45782             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45783                 return true;
45784             }
45785             //Roo.log()
45786             // only allow 'c whitelisted system attributes'
45787             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45788                 return true;
45789             }
45790             
45791             
45792             clean.push(p);
45793             return true;
45794         },this);
45795         if (clean.length) { 
45796             node.setAttribute(n, clean.join(';'));
45797         } else {
45798             node.removeAttribute(n);
45799         }
45800         
45801     }
45802         
45803         
45804         
45805     
45806 });/**
45807  * @class Roo.htmleditor.FilterBlack
45808  * remove blacklisted elements.
45809  * @constructor
45810  * Run a new Blacklisted Filter
45811  * @param {Object} config Configuration options
45812  */
45813
45814 Roo.htmleditor.FilterBlack = function(cfg)
45815 {
45816     Roo.apply(this, cfg);
45817     this.walk(cfg.node);
45818 }
45819
45820 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45821 {
45822     tag : true, // all elements.
45823    
45824     replaceTag : function(n)
45825     {
45826         n.parentNode.removeChild(n);
45827     }
45828 });
45829 /**
45830  * @class Roo.htmleditor.FilterComment
45831  * remove comments.
45832  * @constructor
45833 * Run a new Comments Filter
45834 * @param {Object} config Configuration options
45835  */
45836 Roo.htmleditor.FilterComment = function(cfg)
45837 {
45838     this.walk(cfg.node);
45839 }
45840
45841 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45842 {
45843   
45844     replaceComment : function(n)
45845     {
45846         n.parentNode.removeChild(n);
45847     }
45848 });/**
45849  * @class Roo.htmleditor.FilterKeepChildren
45850  * remove tags but keep children
45851  * @constructor
45852  * Run a new Keep Children Filter
45853  * @param {Object} config Configuration options
45854  */
45855
45856 Roo.htmleditor.FilterKeepChildren = function(cfg)
45857 {
45858     Roo.apply(this, cfg);
45859     if (this.tag === false) {
45860         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45861     }
45862     this.walk(cfg.node);
45863 }
45864
45865 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45866 {
45867     
45868   
45869     replaceTag : function(node)
45870     {
45871         // walk children...
45872         //Roo.log(node);
45873         var ar = Array.from(node.childNodes);
45874         //remove first..
45875         for (var i = 0; i < ar.length; i++) {
45876             if (ar[i].nodeType == 1) {
45877                 if (
45878                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45879                     || // array and it matches
45880                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45881                 ) {
45882                     this.replaceTag(ar[i]); // child is blacklisted as well...
45883                     continue;
45884                 }
45885             }
45886         }  
45887         ar = Array.from(node.childNodes);
45888         for (var i = 0; i < ar.length; i++) {
45889          
45890             node.removeChild(ar[i]);
45891             // what if we need to walk these???
45892             node.parentNode.insertBefore(ar[i], node);
45893             if (this.tag !== false) {
45894                 this.walk(ar[i]);
45895                 
45896             }
45897         }
45898         node.parentNode.removeChild(node);
45899         return false; // don't walk children
45900         
45901         
45902     }
45903 });/**
45904  * @class Roo.htmleditor.FilterParagraph
45905  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45906  * like on 'push' to remove the <p> tags and replace them with line breaks.
45907  * @constructor
45908  * Run a new Paragraph Filter
45909  * @param {Object} config Configuration options
45910  */
45911
45912 Roo.htmleditor.FilterParagraph = function(cfg)
45913 {
45914     // no need to apply config.
45915     this.walk(cfg.node);
45916 }
45917
45918 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45919 {
45920     
45921      
45922     tag : 'P',
45923     
45924      
45925     replaceTag : function(node)
45926     {
45927         
45928         if (node.childNodes.length == 1 &&
45929             node.childNodes[0].nodeType == 3 &&
45930             node.childNodes[0].textContent.trim().length < 1
45931             ) {
45932             // remove and replace with '<BR>';
45933             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45934             return false; // no need to walk..
45935         }
45936         var ar = Array.from(node.childNodes);
45937         for (var i = 0; i < ar.length; i++) {
45938             node.removeChild(ar[i]);
45939             // what if we need to walk these???
45940             node.parentNode.insertBefore(ar[i], node);
45941         }
45942         // now what about this?
45943         // <p> &nbsp; </p>
45944         
45945         // double BR.
45946         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45947         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45948         node.parentNode.removeChild(node);
45949         
45950         return false;
45951
45952     }
45953     
45954 });/**
45955  * @class Roo.htmleditor.FilterSpan
45956  * filter span's with no attributes out..
45957  * @constructor
45958  * Run a new Span Filter
45959  * @param {Object} config Configuration options
45960  */
45961
45962 Roo.htmleditor.FilterSpan = function(cfg)
45963 {
45964     // no need to apply config.
45965     this.walk(cfg.node);
45966 }
45967
45968 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45969 {
45970      
45971     tag : 'SPAN',
45972      
45973  
45974     replaceTag : function(node)
45975     {
45976         if (node.attributes && node.attributes.length > 0) {
45977             return true; // walk if there are any.
45978         }
45979         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45980         return false;
45981      
45982     }
45983     
45984 });/**
45985  * @class Roo.htmleditor.FilterTableWidth
45986   try and remove table width data - as that frequently messes up other stuff.
45987  * 
45988  *      was cleanTableWidths.
45989  *
45990  * Quite often pasting from word etc.. results in tables with column and widths.
45991  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45992  *
45993  * @constructor
45994  * Run a new Table Filter
45995  * @param {Object} config Configuration options
45996  */
45997
45998 Roo.htmleditor.FilterTableWidth = function(cfg)
45999 {
46000     // no need to apply config.
46001     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
46002     this.walk(cfg.node);
46003 }
46004
46005 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
46006 {
46007      
46008      
46009     
46010     replaceTag: function(node) {
46011         
46012         
46013       
46014         if (node.hasAttribute('width')) {
46015             node.removeAttribute('width');
46016         }
46017         
46018          
46019         if (node.hasAttribute("style")) {
46020             // pretty basic...
46021             
46022             var styles = node.getAttribute("style").split(";");
46023             var nstyle = [];
46024             Roo.each(styles, function(s) {
46025                 if (!s.match(/:/)) {
46026                     return;
46027                 }
46028                 var kv = s.split(":");
46029                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
46030                     return;
46031                 }
46032                 // what ever is left... we allow.
46033                 nstyle.push(s);
46034             });
46035             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46036             if (!nstyle.length) {
46037                 node.removeAttribute('style');
46038             }
46039         }
46040         
46041         return true; // continue doing children..
46042     }
46043 });/**
46044  * @class Roo.htmleditor.FilterWord
46045  * try and clean up all the mess that Word generates.
46046  * 
46047  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
46048  
46049  * @constructor
46050  * Run a new Span Filter
46051  * @param {Object} config Configuration options
46052  */
46053
46054 Roo.htmleditor.FilterWord = function(cfg)
46055 {
46056     // no need to apply config.
46057     this.walk(cfg.node);
46058 }
46059
46060 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
46061 {
46062     tag: true,
46063      
46064     
46065     /**
46066      * Clean up MS wordisms...
46067      */
46068     replaceTag : function(node)
46069     {
46070          
46071         // no idea what this does - span with text, replaceds with just text.
46072         if(
46073                 node.nodeName == 'SPAN' &&
46074                 !node.hasAttributes() &&
46075                 node.childNodes.length == 1 &&
46076                 node.firstChild.nodeName == "#text"  
46077         ) {
46078             var textNode = node.firstChild;
46079             node.removeChild(textNode);
46080             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46081                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
46082             }
46083             node.parentNode.insertBefore(textNode, node);
46084             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46085                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
46086             }
46087             
46088             node.parentNode.removeChild(node);
46089             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
46090         }
46091         
46092    
46093         
46094         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
46095             node.parentNode.removeChild(node);
46096             return false; // dont do chidlren
46097         }
46098         //Roo.log(node.tagName);
46099         // remove - but keep children..
46100         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
46101             //Roo.log('-- removed');
46102             while (node.childNodes.length) {
46103                 var cn = node.childNodes[0];
46104                 node.removeChild(cn);
46105                 node.parentNode.insertBefore(cn, node);
46106                 // move node to parent - and clean it..
46107                 this.replaceTag(cn);
46108             }
46109             node.parentNode.removeChild(node);
46110             /// no need to iterate chidlren = it's got none..
46111             //this.iterateChildren(node, this.cleanWord);
46112             return false; // no need to iterate children.
46113         }
46114         // clean styles
46115         if (node.className.length) {
46116             
46117             var cn = node.className.split(/\W+/);
46118             var cna = [];
46119             Roo.each(cn, function(cls) {
46120                 if (cls.match(/Mso[a-zA-Z]+/)) {
46121                     return;
46122                 }
46123                 cna.push(cls);
46124             });
46125             node.className = cna.length ? cna.join(' ') : '';
46126             if (!cna.length) {
46127                 node.removeAttribute("class");
46128             }
46129         }
46130         
46131         if (node.hasAttribute("lang")) {
46132             node.removeAttribute("lang");
46133         }
46134         
46135         if (node.hasAttribute("style")) {
46136             
46137             var styles = node.getAttribute("style").split(";");
46138             var nstyle = [];
46139             Roo.each(styles, function(s) {
46140                 if (!s.match(/:/)) {
46141                     return;
46142                 }
46143                 var kv = s.split(":");
46144                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
46145                     return;
46146                 }
46147                 // what ever is left... we allow.
46148                 nstyle.push(s);
46149             });
46150             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46151             if (!nstyle.length) {
46152                 node.removeAttribute('style');
46153             }
46154         }
46155         return true; // do children
46156         
46157         
46158         
46159     }
46160 });
46161 /**
46162  * @class Roo.htmleditor.FilterStyleToTag
46163  * part of the word stuff... - certain 'styles' should be converted to tags.
46164  * eg.
46165  *   font-weight: bold -> bold
46166  *   ?? super / subscrit etc..
46167  * 
46168  * @constructor
46169 * Run a new style to tag filter.
46170 * @param {Object} config Configuration options
46171  */
46172 Roo.htmleditor.FilterStyleToTag = function(cfg)
46173 {
46174     
46175     this.tags = {
46176         B  : [ 'fontWeight' , 'bold'],
46177         I :  [ 'fontStyle' , 'italic'],
46178         //pre :  [ 'font-style' , 'italic'],
46179         // h1.. h6 ?? font-size?
46180         SUP : [ 'verticalAlign' , 'super' ],
46181         SUB : [ 'verticalAlign' , 'sub' ]
46182         
46183         
46184     };
46185     
46186     Roo.apply(this, cfg);
46187      
46188     
46189     this.walk(cfg.node);
46190     
46191     
46192     
46193 }
46194
46195
46196 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46197 {
46198     tag: true, // all tags
46199     
46200     tags : false,
46201     
46202     
46203     replaceTag : function(node)
46204     {
46205         
46206         
46207         if (node.getAttribute("style") === null) {
46208             return true;
46209         }
46210         var inject = [];
46211         for (var k in this.tags) {
46212             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46213                 inject.push(k);
46214                 node.style.removeProperty(this.tags[k][0]);
46215             }
46216         }
46217         if (!inject.length) {
46218             return true; 
46219         }
46220         var cn = Array.from(node.childNodes);
46221         var nn = node;
46222         Roo.each(inject, function(t) {
46223             var nc = node.ownerDocument.createElement(t);
46224             nn.appendChild(nc);
46225             nn = nc;
46226         });
46227         for(var i = 0;i < cn.length;cn++) {
46228             node.removeChild(cn[i]);
46229             nn.appendChild(cn[i]);
46230         }
46231         return true /// iterate thru
46232     }
46233     
46234 })/**
46235  * @class Roo.htmleditor.FilterLongBr
46236  * BR/BR/BR - keep a maximum of 2...
46237  * @constructor
46238  * Run a new Long BR Filter
46239  * @param {Object} config Configuration options
46240  */
46241
46242 Roo.htmleditor.FilterLongBr = function(cfg)
46243 {
46244     // no need to apply config.
46245     this.walk(cfg.node);
46246 }
46247
46248 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46249 {
46250     
46251      
46252     tag : 'BR',
46253     
46254      
46255     replaceTag : function(node)
46256     {
46257         
46258         var ps = node.nextSibling;
46259         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46260             ps = ps.nextSibling;
46261         }
46262         
46263         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46264             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46265             return false;
46266         }
46267         
46268         if (!ps || ps.nodeType != 1) {
46269             return false;
46270         }
46271         
46272         if (!ps || ps.tagName != 'BR') {
46273            
46274             return false;
46275         }
46276         
46277         
46278         
46279         
46280         
46281         if (!node.previousSibling) {
46282             return false;
46283         }
46284         var ps = node.previousSibling;
46285         
46286         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46287             ps = ps.previousSibling;
46288         }
46289         if (!ps || ps.nodeType != 1) {
46290             return false;
46291         }
46292         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46293         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46294             return false;
46295         }
46296         
46297         node.parentNode.removeChild(node); // remove me...
46298         
46299         return false; // no need to do children
46300
46301     }
46302     
46303 }); 
46304
46305 /**
46306  * @class Roo.htmleditor.FilterBlock
46307  * removes id / data-block and contenteditable that are associated with blocks
46308  * usage should be done on a cloned copy of the dom
46309  * @constructor
46310 * Run a new Attribute Filter { node : xxxx }}
46311 * @param {Object} config Configuration options
46312  */
46313 Roo.htmleditor.FilterBlock = function(cfg)
46314 {
46315     Roo.apply(this, cfg);
46316     var qa = cfg.node.querySelectorAll;
46317     this.removeAttributes('data-block');
46318     this.removeAttributes('contenteditable');
46319     this.removeAttributes('id');
46320     
46321 }
46322
46323 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
46324 {
46325     node: true, // all tags
46326      
46327      
46328     removeAttributes : function(attr)
46329     {
46330         var ar = this.node.querySelectorAll('*[' + attr + ']');
46331         for (var i =0;i<ar.length;i++) {
46332             ar[i].removeAttribute(attr);
46333         }
46334     }
46335         
46336         
46337         
46338     
46339 });
46340 /***
46341  * This is based loosely on tinymce 
46342  * @class Roo.htmleditor.TidySerializer
46343  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46344  * @constructor
46345  * @method Serializer
46346  * @param {Object} settings Name/value settings object.
46347  */
46348
46349
46350 Roo.htmleditor.TidySerializer = function(settings)
46351 {
46352     Roo.apply(this, settings);
46353     
46354     this.writer = new Roo.htmleditor.TidyWriter(settings);
46355     
46356     
46357
46358 };
46359 Roo.htmleditor.TidySerializer.prototype = {
46360     
46361     /**
46362      * @param {boolean} inner do the inner of the node.
46363      */
46364     inner : false,
46365     
46366     writer : false,
46367     
46368     /**
46369     * Serializes the specified node into a string.
46370     *
46371     * @example
46372     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
46373     * @method serialize
46374     * @param {DomElement} node Node instance to serialize.
46375     * @return {String} String with HTML based on DOM tree.
46376     */
46377     serialize : function(node) {
46378         
46379         // = settings.validate;
46380         var writer = this.writer;
46381         var self  = this;
46382         this.handlers = {
46383             // #text
46384             3: function(node) {
46385                 
46386                 writer.text(node.nodeValue, node);
46387             },
46388             // #comment
46389             8: function(node) {
46390                 writer.comment(node.nodeValue);
46391             },
46392             // Processing instruction
46393             7: function(node) {
46394                 writer.pi(node.name, node.nodeValue);
46395             },
46396             // Doctype
46397             10: function(node) {
46398                 writer.doctype(node.nodeValue);
46399             },
46400             // CDATA
46401             4: function(node) {
46402                 writer.cdata(node.nodeValue);
46403             },
46404             // Document fragment
46405             11: function(node) {
46406                 node = node.firstChild;
46407                 if (!node) {
46408                     return;
46409                 }
46410                 while(node) {
46411                     self.walk(node);
46412                     node = node.nextSibling
46413                 }
46414             }
46415         };
46416         writer.reset();
46417         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
46418         return writer.getContent();
46419     },
46420
46421     walk: function(node)
46422     {
46423         var attrName, attrValue, sortedAttrs, i, l, elementRule,
46424             handler = this.handlers[node.nodeType];
46425             
46426         if (handler) {
46427             handler(node);
46428             return;
46429         }
46430     
46431         var name = node.nodeName;
46432         var isEmpty = node.childNodes.length < 1;
46433       
46434         var writer = this.writer;
46435         var attrs = node.attributes;
46436         // Sort attributes
46437         
46438         writer.start(node.nodeName, attrs, isEmpty, node);
46439         if (isEmpty) {
46440             return;
46441         }
46442         node = node.firstChild;
46443         if (!node) {
46444             writer.end(name);
46445             return;
46446         }
46447         while (node) {
46448             this.walk(node);
46449             node = node.nextSibling;
46450         }
46451         writer.end(name);
46452         
46453     
46454     }
46455     // Serialize element and treat all non elements as fragments
46456    
46457 }; 
46458
46459 /***
46460  * This is based loosely on tinymce 
46461  * @class Roo.htmleditor.TidyWriter
46462  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46463  *
46464  * Known issues?
46465  * - not tested much with 'PRE' formated elements.
46466  * 
46467  *
46468  *
46469  */
46470
46471 Roo.htmleditor.TidyWriter = function(settings)
46472 {
46473     
46474     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
46475     Roo.apply(this, settings);
46476     this.html = [];
46477     this.state = [];
46478      
46479     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
46480   
46481 }
46482 Roo.htmleditor.TidyWriter.prototype = {
46483
46484  
46485     state : false,
46486     
46487     indent :  '  ',
46488     
46489     // part of state...
46490     indentstr : '',
46491     in_pre: false,
46492     in_inline : false,
46493     last_inline : false,
46494     encode : false,
46495      
46496     
46497             /**
46498     * Writes the a start element such as <p id="a">.
46499     *
46500     * @method start
46501     * @param {String} name Name of the element.
46502     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
46503     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
46504     */
46505     start: function(name, attrs, empty, node)
46506     {
46507         var i, l, attr, value;
46508         
46509         // there are some situations where adding line break && indentation will not work. will not work.
46510         // <span / b / i ... formating?
46511         
46512         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46513         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
46514         
46515         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
46516         
46517         var add_lb = name == 'BR' ? false : in_inline;
46518         
46519         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
46520             i_inline = false;
46521         }
46522
46523         var indentstr =  this.indentstr;
46524         
46525         // e_inline = elements that can be inline, but still allow \n before and after?
46526         // only 'BR' ??? any others?
46527         
46528         // ADD LINE BEFORE tage
46529         if (!this.in_pre) {
46530             if (in_inline) {
46531                 //code
46532                 if (name == 'BR') {
46533                     this.addLine();
46534                 } else if (this.lastElementEndsWS()) {
46535                     this.addLine();
46536                 } else{
46537                     // otherwise - no new line. (and dont indent.)
46538                     indentstr = '';
46539                 }
46540                 
46541             } else {
46542                 this.addLine();
46543             }
46544         } else {
46545             indentstr = '';
46546         }
46547         
46548         this.html.push(indentstr + '<', name.toLowerCase());
46549         
46550         if (attrs) {
46551             for (i = 0, l = attrs.length; i < l; i++) {
46552                 attr = attrs[i];
46553                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
46554             }
46555         }
46556      
46557         if (empty) {
46558             if (is_short) {
46559                 this.html[this.html.length] = '/>';
46560             } else {
46561                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
46562             }
46563             var e_inline = name == 'BR' ? false : this.in_inline;
46564             
46565             if (!e_inline && !this.in_pre) {
46566                 this.addLine();
46567             }
46568             return;
46569         
46570         }
46571         // not empty..
46572         this.html[this.html.length] = '>';
46573         
46574         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
46575         /*
46576         if (!in_inline && !in_pre) {
46577             var cn = node.firstChild;
46578             while(cn) {
46579                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
46580                     in_inline = true
46581                     break;
46582                 }
46583                 cn = cn.nextSibling;
46584             }
46585              
46586         }
46587         */
46588         
46589         
46590         this.pushState({
46591             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
46592             in_pre : in_pre,
46593             in_inline :  in_inline
46594         });
46595         // add a line after if we are not in a
46596         
46597         if (!in_inline && !in_pre) {
46598             this.addLine();
46599         }
46600         
46601             
46602          
46603         
46604     },
46605     
46606     lastElementEndsWS : function()
46607     {
46608         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
46609         if (value === false) {
46610             return true;
46611         }
46612         return value.match(/\s+$/);
46613         
46614     },
46615     
46616     /**
46617      * Writes the a end element such as </p>.
46618      *
46619      * @method end
46620      * @param {String} name Name of the element.
46621      */
46622     end: function(name) {
46623         var value;
46624         this.popState();
46625         var indentstr = '';
46626         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46627         
46628         if (!this.in_pre && !in_inline) {
46629             this.addLine();
46630             indentstr  = this.indentstr;
46631         }
46632         this.html.push(indentstr + '</', name.toLowerCase(), '>');
46633         this.last_inline = in_inline;
46634         
46635         // pop the indent state..
46636     },
46637     /**
46638      * Writes a text node.
46639      *
46640      * In pre - we should not mess with the contents.
46641      * 
46642      *
46643      * @method text
46644      * @param {String} text String to write out.
46645      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
46646      */
46647     text: function(text, node)
46648     {
46649         // if not in whitespace critical
46650         if (text.length < 1) {
46651             return;
46652         }
46653         if (this.in_pre) {
46654             this.html[this.html.length] =  text;
46655             return;   
46656         }
46657         
46658         if (this.in_inline) {
46659             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
46660             if (text != ' ') {
46661                 text = text.replace(/\s+/,' ');  // all white space to single white space
46662                 
46663                     
46664                 // if next tag is '<BR>', then we can trim right..
46665                 if (node.nextSibling &&
46666                     node.nextSibling.nodeType == 1 &&
46667                     node.nextSibling.nodeName == 'BR' )
46668                 {
46669                     text = text.replace(/\s+$/g,'');
46670                 }
46671                 // if previous tag was a BR, we can also trim..
46672                 if (node.previousSibling &&
46673                     node.previousSibling.nodeType == 1 &&
46674                     node.previousSibling.nodeName == 'BR' )
46675                 {
46676                     text = this.indentstr +  text.replace(/^\s+/g,'');
46677                 }
46678                 if (text.match(/\n/)) {
46679                     text = text.replace(
46680                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46681                     );
46682                     // remoeve the last whitespace / line break.
46683                     text = text.replace(/\n\s+$/,'');
46684                 }
46685                 // repace long lines
46686                 
46687             }
46688              
46689             this.html[this.html.length] =  text;
46690             return;   
46691         }
46692         // see if previous element was a inline element.
46693         var indentstr = this.indentstr;
46694    
46695         text = text.replace(/\s+/g," "); // all whitespace into single white space.
46696         
46697         // should trim left?
46698         if (node.previousSibling &&
46699             node.previousSibling.nodeType == 1 &&
46700             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
46701         {
46702             indentstr = '';
46703             
46704         } else {
46705             this.addLine();
46706             text = text.replace(/^\s+/,''); // trim left
46707           
46708         }
46709         // should trim right?
46710         if (node.nextSibling &&
46711             node.nextSibling.nodeType == 1 &&
46712             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
46713         {
46714           // noop
46715             
46716         }  else {
46717             text = text.replace(/\s+$/,''); // trim right
46718         }
46719          
46720               
46721         
46722         
46723         
46724         if (text.length < 1) {
46725             return;
46726         }
46727         if (!text.match(/\n/)) {
46728             this.html.push(indentstr + text);
46729             return;
46730         }
46731         
46732         text = this.indentstr + text.replace(
46733             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46734         );
46735         // remoeve the last whitespace / line break.
46736         text = text.replace(/\s+$/,''); 
46737         
46738         this.html.push(text);
46739         
46740         // split and indent..
46741         
46742         
46743     },
46744     /**
46745      * Writes a cdata node such as <![CDATA[data]]>.
46746      *
46747      * @method cdata
46748      * @param {String} text String to write out inside the cdata.
46749      */
46750     cdata: function(text) {
46751         this.html.push('<![CDATA[', text, ']]>');
46752     },
46753     /**
46754     * Writes a comment node such as <!-- Comment -->.
46755     *
46756     * @method cdata
46757     * @param {String} text String to write out inside the comment.
46758     */
46759    comment: function(text) {
46760        this.html.push('<!--', text, '-->');
46761    },
46762     /**
46763      * Writes a PI node such as <?xml attr="value" ?>.
46764      *
46765      * @method pi
46766      * @param {String} name Name of the pi.
46767      * @param {String} text String to write out inside the pi.
46768      */
46769     pi: function(name, text) {
46770         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
46771         this.indent != '' && this.html.push('\n');
46772     },
46773     /**
46774      * Writes a doctype node such as <!DOCTYPE data>.
46775      *
46776      * @method doctype
46777      * @param {String} text String to write out inside the doctype.
46778      */
46779     doctype: function(text) {
46780         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
46781     },
46782     /**
46783      * Resets the internal buffer if one wants to reuse the writer.
46784      *
46785      * @method reset
46786      */
46787     reset: function() {
46788         this.html.length = 0;
46789         this.state = [];
46790         this.pushState({
46791             indentstr : '',
46792             in_pre : false, 
46793             in_inline : false
46794         })
46795     },
46796     /**
46797      * Returns the contents that got serialized.
46798      *
46799      * @method getContent
46800      * @return {String} HTML contents that got written down.
46801      */
46802     getContent: function() {
46803         return this.html.join('').replace(/\n$/, '');
46804     },
46805     
46806     pushState : function(cfg)
46807     {
46808         this.state.push(cfg);
46809         Roo.apply(this, cfg);
46810     },
46811     
46812     popState : function()
46813     {
46814         if (this.state.length < 1) {
46815             return; // nothing to push
46816         }
46817         var cfg = {
46818             in_pre: false,
46819             indentstr : ''
46820         };
46821         this.state.pop();
46822         if (this.state.length > 0) {
46823             cfg = this.state[this.state.length-1]; 
46824         }
46825         Roo.apply(this, cfg);
46826     },
46827     
46828     addLine: function()
46829     {
46830         if (this.html.length < 1) {
46831             return;
46832         }
46833         
46834         
46835         var value = this.html[this.html.length - 1];
46836         if (value.length > 0 && '\n' !== value) {
46837             this.html.push('\n');
46838         }
46839     }
46840     
46841     
46842 //'pre script noscript style textarea video audio iframe object code'
46843 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
46844 // inline 
46845 };
46846
46847 Roo.htmleditor.TidyWriter.inline_elements = [
46848         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
46849         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
46850 ];
46851 Roo.htmleditor.TidyWriter.shortend_elements = [
46852     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
46853     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
46854 ];
46855
46856 Roo.htmleditor.TidyWriter.whitespace_elements = [
46857     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
46858 ];/***
46859  * This is based loosely on tinymce 
46860  * @class Roo.htmleditor.TidyEntities
46861  * @static
46862  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46863  *
46864  * Not 100% sure this is actually used or needed.
46865  */
46866
46867 Roo.htmleditor.TidyEntities = {
46868     
46869     /**
46870      * initialize data..
46871      */
46872     init : function (){
46873      
46874         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
46875        
46876     },
46877
46878
46879     buildEntitiesLookup: function(items, radix) {
46880         var i, chr, entity, lookup = {};
46881         if (!items) {
46882             return {};
46883         }
46884         items = typeof(items) == 'string' ? items.split(',') : items;
46885         radix = radix || 10;
46886         // Build entities lookup table
46887         for (i = 0; i < items.length; i += 2) {
46888             chr = String.fromCharCode(parseInt(items[i], radix));
46889             // Only add non base entities
46890             if (!this.baseEntities[chr]) {
46891                 entity = '&' + items[i + 1] + ';';
46892                 lookup[chr] = entity;
46893                 lookup[entity] = chr;
46894             }
46895         }
46896         return lookup;
46897         
46898     },
46899     
46900     asciiMap : {
46901             128: '€',
46902             130: '‚',
46903             131: 'ƒ',
46904             132: '„',
46905             133: '…',
46906             134: '†',
46907             135: '‡',
46908             136: 'ˆ',
46909             137: '‰',
46910             138: 'Š',
46911             139: '‹',
46912             140: 'Œ',
46913             142: 'Ž',
46914             145: '‘',
46915             146: '’',
46916             147: '“',
46917             148: '”',
46918             149: '•',
46919             150: '–',
46920             151: '—',
46921             152: '˜',
46922             153: '™',
46923             154: 'š',
46924             155: '›',
46925             156: 'œ',
46926             158: 'ž',
46927             159: 'Ÿ'
46928     },
46929     // Raw entities
46930     baseEntities : {
46931         '"': '&quot;',
46932         // Needs to be escaped since the YUI compressor would otherwise break the code
46933         '\'': '&#39;',
46934         '<': '&lt;',
46935         '>': '&gt;',
46936         '&': '&amp;',
46937         '`': '&#96;'
46938     },
46939     // Reverse lookup table for raw entities
46940     reverseEntities : {
46941         '&lt;': '<',
46942         '&gt;': '>',
46943         '&amp;': '&',
46944         '&quot;': '"',
46945         '&apos;': '\''
46946     },
46947     
46948     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
46949     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
46950     rawCharsRegExp : /[<>&\"\']/g,
46951     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
46952     namedEntities  : false,
46953     namedEntitiesData : [ 
46954         '50',
46955         'nbsp',
46956         '51',
46957         'iexcl',
46958         '52',
46959         'cent',
46960         '53',
46961         'pound',
46962         '54',
46963         'curren',
46964         '55',
46965         'yen',
46966         '56',
46967         'brvbar',
46968         '57',
46969         'sect',
46970         '58',
46971         'uml',
46972         '59',
46973         'copy',
46974         '5a',
46975         'ordf',
46976         '5b',
46977         'laquo',
46978         '5c',
46979         'not',
46980         '5d',
46981         'shy',
46982         '5e',
46983         'reg',
46984         '5f',
46985         'macr',
46986         '5g',
46987         'deg',
46988         '5h',
46989         'plusmn',
46990         '5i',
46991         'sup2',
46992         '5j',
46993         'sup3',
46994         '5k',
46995         'acute',
46996         '5l',
46997         'micro',
46998         '5m',
46999         'para',
47000         '5n',
47001         'middot',
47002         '5o',
47003         'cedil',
47004         '5p',
47005         'sup1',
47006         '5q',
47007         'ordm',
47008         '5r',
47009         'raquo',
47010         '5s',
47011         'frac14',
47012         '5t',
47013         'frac12',
47014         '5u',
47015         'frac34',
47016         '5v',
47017         'iquest',
47018         '60',
47019         'Agrave',
47020         '61',
47021         'Aacute',
47022         '62',
47023         'Acirc',
47024         '63',
47025         'Atilde',
47026         '64',
47027         'Auml',
47028         '65',
47029         'Aring',
47030         '66',
47031         'AElig',
47032         '67',
47033         'Ccedil',
47034         '68',
47035         'Egrave',
47036         '69',
47037         'Eacute',
47038         '6a',
47039         'Ecirc',
47040         '6b',
47041         'Euml',
47042         '6c',
47043         'Igrave',
47044         '6d',
47045         'Iacute',
47046         '6e',
47047         'Icirc',
47048         '6f',
47049         'Iuml',
47050         '6g',
47051         'ETH',
47052         '6h',
47053         'Ntilde',
47054         '6i',
47055         'Ograve',
47056         '6j',
47057         'Oacute',
47058         '6k',
47059         'Ocirc',
47060         '6l',
47061         'Otilde',
47062         '6m',
47063         'Ouml',
47064         '6n',
47065         'times',
47066         '6o',
47067         'Oslash',
47068         '6p',
47069         'Ugrave',
47070         '6q',
47071         'Uacute',
47072         '6r',
47073         'Ucirc',
47074         '6s',
47075         'Uuml',
47076         '6t',
47077         'Yacute',
47078         '6u',
47079         'THORN',
47080         '6v',
47081         'szlig',
47082         '70',
47083         'agrave',
47084         '71',
47085         'aacute',
47086         '72',
47087         'acirc',
47088         '73',
47089         'atilde',
47090         '74',
47091         'auml',
47092         '75',
47093         'aring',
47094         '76',
47095         'aelig',
47096         '77',
47097         'ccedil',
47098         '78',
47099         'egrave',
47100         '79',
47101         'eacute',
47102         '7a',
47103         'ecirc',
47104         '7b',
47105         'euml',
47106         '7c',
47107         'igrave',
47108         '7d',
47109         'iacute',
47110         '7e',
47111         'icirc',
47112         '7f',
47113         'iuml',
47114         '7g',
47115         'eth',
47116         '7h',
47117         'ntilde',
47118         '7i',
47119         'ograve',
47120         '7j',
47121         'oacute',
47122         '7k',
47123         'ocirc',
47124         '7l',
47125         'otilde',
47126         '7m',
47127         'ouml',
47128         '7n',
47129         'divide',
47130         '7o',
47131         'oslash',
47132         '7p',
47133         'ugrave',
47134         '7q',
47135         'uacute',
47136         '7r',
47137         'ucirc',
47138         '7s',
47139         'uuml',
47140         '7t',
47141         'yacute',
47142         '7u',
47143         'thorn',
47144         '7v',
47145         'yuml',
47146         'ci',
47147         'fnof',
47148         'sh',
47149         'Alpha',
47150         'si',
47151         'Beta',
47152         'sj',
47153         'Gamma',
47154         'sk',
47155         'Delta',
47156         'sl',
47157         'Epsilon',
47158         'sm',
47159         'Zeta',
47160         'sn',
47161         'Eta',
47162         'so',
47163         'Theta',
47164         'sp',
47165         'Iota',
47166         'sq',
47167         'Kappa',
47168         'sr',
47169         'Lambda',
47170         'ss',
47171         'Mu',
47172         'st',
47173         'Nu',
47174         'su',
47175         'Xi',
47176         'sv',
47177         'Omicron',
47178         't0',
47179         'Pi',
47180         't1',
47181         'Rho',
47182         't3',
47183         'Sigma',
47184         't4',
47185         'Tau',
47186         't5',
47187         'Upsilon',
47188         't6',
47189         'Phi',
47190         't7',
47191         'Chi',
47192         't8',
47193         'Psi',
47194         't9',
47195         'Omega',
47196         'th',
47197         'alpha',
47198         'ti',
47199         'beta',
47200         'tj',
47201         'gamma',
47202         'tk',
47203         'delta',
47204         'tl',
47205         'epsilon',
47206         'tm',
47207         'zeta',
47208         'tn',
47209         'eta',
47210         'to',
47211         'theta',
47212         'tp',
47213         'iota',
47214         'tq',
47215         'kappa',
47216         'tr',
47217         'lambda',
47218         'ts',
47219         'mu',
47220         'tt',
47221         'nu',
47222         'tu',
47223         'xi',
47224         'tv',
47225         'omicron',
47226         'u0',
47227         'pi',
47228         'u1',
47229         'rho',
47230         'u2',
47231         'sigmaf',
47232         'u3',
47233         'sigma',
47234         'u4',
47235         'tau',
47236         'u5',
47237         'upsilon',
47238         'u6',
47239         'phi',
47240         'u7',
47241         'chi',
47242         'u8',
47243         'psi',
47244         'u9',
47245         'omega',
47246         'uh',
47247         'thetasym',
47248         'ui',
47249         'upsih',
47250         'um',
47251         'piv',
47252         '812',
47253         'bull',
47254         '816',
47255         'hellip',
47256         '81i',
47257         'prime',
47258         '81j',
47259         'Prime',
47260         '81u',
47261         'oline',
47262         '824',
47263         'frasl',
47264         '88o',
47265         'weierp',
47266         '88h',
47267         'image',
47268         '88s',
47269         'real',
47270         '892',
47271         'trade',
47272         '89l',
47273         'alefsym',
47274         '8cg',
47275         'larr',
47276         '8ch',
47277         'uarr',
47278         '8ci',
47279         'rarr',
47280         '8cj',
47281         'darr',
47282         '8ck',
47283         'harr',
47284         '8dl',
47285         'crarr',
47286         '8eg',
47287         'lArr',
47288         '8eh',
47289         'uArr',
47290         '8ei',
47291         'rArr',
47292         '8ej',
47293         'dArr',
47294         '8ek',
47295         'hArr',
47296         '8g0',
47297         'forall',
47298         '8g2',
47299         'part',
47300         '8g3',
47301         'exist',
47302         '8g5',
47303         'empty',
47304         '8g7',
47305         'nabla',
47306         '8g8',
47307         'isin',
47308         '8g9',
47309         'notin',
47310         '8gb',
47311         'ni',
47312         '8gf',
47313         'prod',
47314         '8gh',
47315         'sum',
47316         '8gi',
47317         'minus',
47318         '8gn',
47319         'lowast',
47320         '8gq',
47321         'radic',
47322         '8gt',
47323         'prop',
47324         '8gu',
47325         'infin',
47326         '8h0',
47327         'ang',
47328         '8h7',
47329         'and',
47330         '8h8',
47331         'or',
47332         '8h9',
47333         'cap',
47334         '8ha',
47335         'cup',
47336         '8hb',
47337         'int',
47338         '8hk',
47339         'there4',
47340         '8hs',
47341         'sim',
47342         '8i5',
47343         'cong',
47344         '8i8',
47345         'asymp',
47346         '8j0',
47347         'ne',
47348         '8j1',
47349         'equiv',
47350         '8j4',
47351         'le',
47352         '8j5',
47353         'ge',
47354         '8k2',
47355         'sub',
47356         '8k3',
47357         'sup',
47358         '8k4',
47359         'nsub',
47360         '8k6',
47361         'sube',
47362         '8k7',
47363         'supe',
47364         '8kl',
47365         'oplus',
47366         '8kn',
47367         'otimes',
47368         '8l5',
47369         'perp',
47370         '8m5',
47371         'sdot',
47372         '8o8',
47373         'lceil',
47374         '8o9',
47375         'rceil',
47376         '8oa',
47377         'lfloor',
47378         '8ob',
47379         'rfloor',
47380         '8p9',
47381         'lang',
47382         '8pa',
47383         'rang',
47384         '9ea',
47385         'loz',
47386         '9j0',
47387         'spades',
47388         '9j3',
47389         'clubs',
47390         '9j5',
47391         'hearts',
47392         '9j6',
47393         'diams',
47394         'ai',
47395         'OElig',
47396         'aj',
47397         'oelig',
47398         'b0',
47399         'Scaron',
47400         'b1',
47401         'scaron',
47402         'bo',
47403         'Yuml',
47404         'm6',
47405         'circ',
47406         'ms',
47407         'tilde',
47408         '802',
47409         'ensp',
47410         '803',
47411         'emsp',
47412         '809',
47413         'thinsp',
47414         '80c',
47415         'zwnj',
47416         '80d',
47417         'zwj',
47418         '80e',
47419         'lrm',
47420         '80f',
47421         'rlm',
47422         '80j',
47423         'ndash',
47424         '80k',
47425         'mdash',
47426         '80o',
47427         'lsquo',
47428         '80p',
47429         'rsquo',
47430         '80q',
47431         'sbquo',
47432         '80s',
47433         'ldquo',
47434         '80t',
47435         'rdquo',
47436         '80u',
47437         'bdquo',
47438         '810',
47439         'dagger',
47440         '811',
47441         'Dagger',
47442         '81g',
47443         'permil',
47444         '81p',
47445         'lsaquo',
47446         '81q',
47447         'rsaquo',
47448         '85c',
47449         'euro'
47450     ],
47451
47452          
47453     /**
47454      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
47455      *
47456      * @method encodeRaw
47457      * @param {String} text Text to encode.
47458      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47459      * @return {String} Entity encoded text.
47460      */
47461     encodeRaw: function(text, attr)
47462     {
47463         var t = this;
47464         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47465             return t.baseEntities[chr] || chr;
47466         });
47467     },
47468     /**
47469      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
47470      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
47471      * and is exposed as the DOMUtils.encode function.
47472      *
47473      * @method encodeAllRaw
47474      * @param {String} text Text to encode.
47475      * @return {String} Entity encoded text.
47476      */
47477     encodeAllRaw: function(text) {
47478         var t = this;
47479         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
47480             return t.baseEntities[chr] || chr;
47481         });
47482     },
47483     /**
47484      * Encodes the specified string using numeric entities. The core entities will be
47485      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
47486      *
47487      * @method encodeNumeric
47488      * @param {String} text Text to encode.
47489      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47490      * @return {String} Entity encoded text.
47491      */
47492     encodeNumeric: function(text, attr) {
47493         var t = this;
47494         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47495             // Multi byte sequence convert it to a single entity
47496             if (chr.length > 1) {
47497                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
47498             }
47499             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
47500         });
47501     },
47502     /**
47503      * Encodes the specified string using named entities. The core entities will be encoded
47504      * as named ones but all non lower ascii characters will be encoded into named entities.
47505      *
47506      * @method encodeNamed
47507      * @param {String} text Text to encode.
47508      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47509      * @param {Object} entities Optional parameter with entities to use.
47510      * @return {String} Entity encoded text.
47511      */
47512     encodeNamed: function(text, attr, entities) {
47513         var t = this;
47514         entities = entities || this.namedEntities;
47515         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47516             return t.baseEntities[chr] || entities[chr] || chr;
47517         });
47518     },
47519     /**
47520      * Returns an encode function based on the name(s) and it's optional entities.
47521      *
47522      * @method getEncodeFunc
47523      * @param {String} name Comma separated list of encoders for example named,numeric.
47524      * @param {String} entities Optional parameter with entities to use instead of the built in set.
47525      * @return {function} Encode function to be used.
47526      */
47527     getEncodeFunc: function(name, entities) {
47528         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
47529         var t = this;
47530         function encodeNamedAndNumeric(text, attr) {
47531             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
47532                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
47533             });
47534         }
47535
47536         function encodeCustomNamed(text, attr) {
47537             return t.encodeNamed(text, attr, entities);
47538         }
47539         // Replace + with , to be compatible with previous TinyMCE versions
47540         name = this.makeMap(name.replace(/\+/g, ','));
47541         // Named and numeric encoder
47542         if (name.named && name.numeric) {
47543             return this.encodeNamedAndNumeric;
47544         }
47545         // Named encoder
47546         if (name.named) {
47547             // Custom names
47548             if (entities) {
47549                 return encodeCustomNamed;
47550             }
47551             return this.encodeNamed;
47552         }
47553         // Numeric
47554         if (name.numeric) {
47555             return this.encodeNumeric;
47556         }
47557         // Raw encoder
47558         return this.encodeRaw;
47559     },
47560     /**
47561      * Decodes the specified string, this will replace entities with raw UTF characters.
47562      *
47563      * @method decode
47564      * @param {String} text Text to entity decode.
47565      * @return {String} Entity decoded string.
47566      */
47567     decode: function(text)
47568     {
47569         var  t = this;
47570         return text.replace(this.entityRegExp, function(all, numeric) {
47571             if (numeric) {
47572                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
47573                 // Support upper UTF
47574                 if (numeric > 65535) {
47575                     numeric -= 65536;
47576                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
47577                 }
47578                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
47579             }
47580             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
47581         });
47582     },
47583     nativeDecode : function (text) {
47584         return text;
47585     },
47586     makeMap : function (items, delim, map) {
47587                 var i;
47588                 items = items || [];
47589                 delim = delim || ',';
47590                 if (typeof items == "string") {
47591                         items = items.split(delim);
47592                 }
47593                 map = map || {};
47594                 i = items.length;
47595                 while (i--) {
47596                         map[items[i]] = {};
47597                 }
47598                 return map;
47599         }
47600 };
47601     
47602     
47603     
47604 Roo.htmleditor.TidyEntities.init();
47605 /**
47606  * @class Roo.htmleditor.KeyEnter
47607  * Handle Enter press..
47608  * @cfg {Roo.HtmlEditorCore} core the editor.
47609  * @constructor
47610  * Create a new Filter.
47611  * @param {Object} config Configuration options
47612  */
47613
47614
47615
47616
47617
47618 Roo.htmleditor.KeyEnter = function(cfg) {
47619     Roo.apply(this, cfg);
47620     // this does not actually call walk as it's really just a abstract class
47621  
47622     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
47623 }
47624
47625 //Roo.htmleditor.KeyEnter.i = 0;
47626
47627
47628 Roo.htmleditor.KeyEnter.prototype = {
47629     
47630     core : false,
47631     
47632     keypress : function(e)
47633     {
47634         if (e.charCode != 13 && e.charCode != 10) {
47635             Roo.log([e.charCode,e]);
47636             return true;
47637         }
47638         e.preventDefault();
47639         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
47640         var doc = this.core.doc;
47641           //add a new line
47642        
47643     
47644         var sel = this.core.getSelection();
47645         var range = sel.getRangeAt(0);
47646         var n = range.commonAncestorContainer;
47647         var pc = range.closest([ 'ol', 'ul']);
47648         var pli = range.closest('li');
47649         if (!pc || e.ctrlKey) {
47650             sel.insertNode('br', 'after'); 
47651          
47652             this.core.undoManager.addEvent();
47653             this.core.fireEditorEvent(e);
47654             return false;
47655         }
47656         
47657         // deal with <li> insetion
47658         if (pli.innerText.trim() == '' &&
47659             pli.previousSibling &&
47660             pli.previousSibling.nodeName == 'LI' &&
47661             pli.previousSibling.innerText.trim() ==  '') {
47662             pli.parentNode.removeChild(pli.previousSibling);
47663             sel.cursorAfter(pc);
47664             this.core.undoManager.addEvent();
47665             this.core.fireEditorEvent(e);
47666             return false;
47667         }
47668     
47669         var li = doc.createElement('LI');
47670         li.innerHTML = '&nbsp;';
47671         if (!pli || !pli.firstSibling) {
47672             pc.appendChild(li);
47673         } else {
47674             pli.parentNode.insertBefore(li, pli.firstSibling);
47675         }
47676         sel.cursorText (li.firstChild);
47677       
47678         this.core.undoManager.addEvent();
47679         this.core.fireEditorEvent(e);
47680
47681         return false;
47682         
47683     
47684         
47685         
47686          
47687     }
47688 };
47689      
47690 /**
47691  * @class Roo.htmleditor.Block
47692  * Base class for html editor blocks - do not use it directly .. extend it..
47693  * @cfg {DomElement} node The node to apply stuff to.
47694  * @cfg {String} friendly_name the name that appears in the context bar about this block
47695  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
47696  
47697  * @constructor
47698  * Create a new Filter.
47699  * @param {Object} config Configuration options
47700  */
47701
47702 Roo.htmleditor.Block  = function(cfg)
47703 {
47704     // do nothing .. should not be called really.
47705 }
47706 /**
47707  * factory method to get the block from an element (using cache if necessary)
47708  * @static
47709  * @param {HtmlElement} the dom element
47710  */
47711 Roo.htmleditor.Block.factory = function(node)
47712 {
47713     var cc = Roo.htmleditor.Block.cache;
47714     var id = Roo.get(node).id;
47715     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
47716         Roo.htmleditor.Block.cache[id].readElement(node);
47717         return Roo.htmleditor.Block.cache[id];
47718     }
47719     var db  = node.getAttribute('data-block');
47720     if (!db) {
47721         db = node.nodeName.toLowerCase().toUpperCaseFirst();
47722     }
47723     var cls = Roo.htmleditor['Block' + db];
47724     if (typeof(cls) == 'undefined') {
47725         //Roo.log(node.getAttribute('data-block'));
47726         Roo.log("OOps missing block : " + 'Block' + db);
47727         return false;
47728     }
47729     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
47730     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
47731 };
47732
47733 /**
47734  * initalize all Elements from content that are 'blockable'
47735  * @static
47736  * @param the body element
47737  */
47738 Roo.htmleditor.Block.initAll = function(body, type)
47739 {
47740     if (typeof(type) == 'undefined') {
47741         var ia = Roo.htmleditor.Block.initAll;
47742         ia(body,'table');
47743         ia(body,'td');
47744         ia(body,'figure');
47745         return;
47746     }
47747     Roo.each(Roo.get(body).query(type), function(e) {
47748         Roo.htmleditor.Block.factory(e);    
47749     },this);
47750 };
47751 // question goes here... do we need to clear out this cache sometimes?
47752 // or show we make it relivant to the htmleditor.
47753 Roo.htmleditor.Block.cache = {};
47754
47755 Roo.htmleditor.Block.prototype = {
47756     
47757     node : false,
47758     
47759      // used by context menu
47760     friendly_name : 'Based Block',
47761     
47762     // text for button to delete this element
47763     deleteTitle : false,
47764     
47765     context : false,
47766     /**
47767      * Update a node with values from this object
47768      * @param {DomElement} node
47769      */
47770     updateElement : function(node)
47771     {
47772         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
47773     },
47774      /**
47775      * convert to plain HTML for calling insertAtCursor..
47776      */
47777     toHTML : function()
47778     {
47779         return Roo.DomHelper.markup(this.toObject());
47780     },
47781     /**
47782      * used by readEleemnt to extract data from a node
47783      * may need improving as it's pretty basic
47784      
47785      * @param {DomElement} node
47786      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
47787      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
47788      * @param {String} style the style property - eg. text-align
47789      */
47790     getVal : function(node, tag, attr, style)
47791     {
47792         var n = node;
47793         if (tag !== true && n.tagName != tag.toUpperCase()) {
47794             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
47795             // but kiss for now.
47796             n = node.getElementsByTagName(tag).item(0);
47797         }
47798         if (!n) {
47799             return '';
47800         }
47801         if (attr === false) {
47802             return n;
47803         }
47804         if (attr == 'html') {
47805             return n.innerHTML;
47806         }
47807         if (attr == 'style') {
47808             return n.style[style]; 
47809         }
47810         
47811         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
47812             
47813     },
47814     /**
47815      * create a DomHelper friendly object - for use with 
47816      * Roo.DomHelper.markup / overwrite / etc..
47817      * (override this)
47818      */
47819     toObject : function()
47820     {
47821         return {};
47822     },
47823       /**
47824      * Read a node that has a 'data-block' property - and extract the values from it.
47825      * @param {DomElement} node - the node
47826      */
47827     readElement : function(node)
47828     {
47829         
47830     } 
47831     
47832     
47833 };
47834
47835  
47836
47837 /**
47838  * @class Roo.htmleditor.BlockFigure
47839  * Block that has an image and a figcaption
47840  * @cfg {String} image_src the url for the image
47841  * @cfg {String} align (left|right) alignment for the block default left
47842  * @cfg {String} caption the text to appear below  (and in the alt tag)
47843  * @cfg {String} caption_display (block|none) display or not the caption
47844  * @cfg {String|number} image_width the width of the image number or %?
47845  * @cfg {String|number} image_height the height of the image number or %?
47846  * 
47847  * @constructor
47848  * Create a new Filter.
47849  * @param {Object} config Configuration options
47850  */
47851
47852 Roo.htmleditor.BlockFigure = function(cfg)
47853 {
47854     if (cfg.node) {
47855         this.readElement(cfg.node);
47856         this.updateElement(cfg.node);
47857     }
47858     Roo.apply(this, cfg);
47859 }
47860 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
47861  
47862     
47863     // setable values.
47864     image_src: '',
47865     align: 'center',
47866     caption : '',
47867     caption_display : 'block',
47868     width : '100%',
47869     cls : '',
47870     href: '',
47871     video_url : '',
47872     
47873     // margin: '2%', not used
47874     
47875     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
47876
47877     
47878     // used by context menu
47879     friendly_name : 'Image with caption',
47880     deleteTitle : "Delete Image and Caption",
47881     
47882     contextMenu : function(toolbar)
47883     {
47884         
47885         var block = function() {
47886             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
47887         };
47888         
47889         
47890         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
47891         
47892         var syncValue = toolbar.editorcore.syncValue;
47893         
47894         var fields = {};
47895         
47896         return [
47897              {
47898                 xtype : 'TextItem',
47899                 text : "Source: ",
47900                 xns : rooui.Toolbar  //Boostrap?
47901             },
47902             {
47903                 xtype : 'Button',
47904                 text: 'Change Image URL',
47905                  
47906                 listeners : {
47907                     click: function (btn, state)
47908                     {
47909                         var b = block();
47910                         
47911                         Roo.MessageBox.show({
47912                             title : "Image Source URL",
47913                             msg : "Enter the url for the image",
47914                             buttons: Roo.MessageBox.OKCANCEL,
47915                             fn: function(btn, val){
47916                                 if (btn != 'ok') {
47917                                     return;
47918                                 }
47919                                 b.image_src = val;
47920                                 b.updateElement();
47921                                 syncValue();
47922                                 toolbar.editorcore.onEditorEvent();
47923                             },
47924                             minWidth:250,
47925                             prompt:true,
47926                             //multiline: multiline,
47927                             modal : true,
47928                             value : b.image_src
47929                         });
47930                     }
47931                 },
47932                 xns : rooui.Toolbar
47933             },
47934          
47935             {
47936                 xtype : 'Button',
47937                 text: 'Change Link URL',
47938                  
47939                 listeners : {
47940                     click: function (btn, state)
47941                     {
47942                         var b = block();
47943                         
47944                         Roo.MessageBox.show({
47945                             title : "Link URL",
47946                             msg : "Enter the url for the link - leave blank to have no link",
47947                             buttons: Roo.MessageBox.OKCANCEL,
47948                             fn: function(btn, val){
47949                                 if (btn != 'ok') {
47950                                     return;
47951                                 }
47952                                 b.href = val;
47953                                 b.updateElement();
47954                                 syncValue();
47955                                 toolbar.editorcore.onEditorEvent();
47956                             },
47957                             minWidth:250,
47958                             prompt:true,
47959                             //multiline: multiline,
47960                             modal : true,
47961                             value : b.href
47962                         });
47963                     }
47964                 },
47965                 xns : rooui.Toolbar
47966             },
47967             {
47968                 xtype : 'Button',
47969                 text: 'Show Video URL',
47970                  
47971                 listeners : {
47972                     click: function (btn, state)
47973                     {
47974                         Roo.MessageBox.alert("Video URL",
47975                             block().video_url == '' ? 'This image is not linked ot a video' :
47976                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
47977                     }
47978                 },
47979                 xns : rooui.Toolbar
47980             },
47981             
47982             
47983             {
47984                 xtype : 'TextItem',
47985                 text : "Width: ",
47986                 xns : rooui.Toolbar  //Boostrap?
47987             },
47988             {
47989                 xtype : 'ComboBox',
47990                 allowBlank : false,
47991                 displayField : 'val',
47992                 editable : true,
47993                 listWidth : 100,
47994                 triggerAction : 'all',
47995                 typeAhead : true,
47996                 valueField : 'val',
47997                 width : 70,
47998                 name : 'width',
47999                 listeners : {
48000                     select : function (combo, r, index)
48001                     {
48002                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48003                         var b = block();
48004                         b.width = r.get('val');
48005                         b.updateElement();
48006                         syncValue();
48007                         toolbar.editorcore.onEditorEvent();
48008                     }
48009                 },
48010                 xns : rooui.form,
48011                 store : {
48012                     xtype : 'SimpleStore',
48013                     data : [
48014                         ['auto'],
48015                         ['50%'],
48016                         ['80%'],
48017                         ['100%']
48018                     ],
48019                     fields : [ 'val'],
48020                     xns : Roo.data
48021                 }
48022             },
48023             {
48024                 xtype : 'TextItem',
48025                 text : "Align: ",
48026                 xns : rooui.Toolbar  //Boostrap?
48027             },
48028             {
48029                 xtype : 'ComboBox',
48030                 allowBlank : false,
48031                 displayField : 'val',
48032                 editable : true,
48033                 listWidth : 100,
48034                 triggerAction : 'all',
48035                 typeAhead : true,
48036                 valueField : 'val',
48037                 width : 70,
48038                 name : 'align',
48039                 listeners : {
48040                     select : function (combo, r, index)
48041                     {
48042                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48043                         var b = block();
48044                         b.align = r.get('val');
48045                         b.updateElement();
48046                         syncValue();
48047                         toolbar.editorcore.onEditorEvent();
48048                     }
48049                 },
48050                 xns : rooui.form,
48051                 store : {
48052                     xtype : 'SimpleStore',
48053                     data : [
48054                         ['left'],
48055                         ['right'],
48056                         ['center']
48057                     ],
48058                     fields : [ 'val'],
48059                     xns : Roo.data
48060                 }
48061             },
48062             
48063             
48064             {
48065                 xtype : 'Button',
48066                 text: 'Hide Caption',
48067                 name : 'caption_display',
48068                 pressed : false,
48069                 enableToggle : true,
48070                 setValue : function(v) {
48071                     this.toggle(v == 'block' ? false : true);
48072                 },
48073                 listeners : {
48074                     toggle: function (btn, state)
48075                     {
48076                         var b  = block();
48077                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
48078                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
48079                         b.updateElement();
48080                         syncValue();
48081                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48082                         toolbar.editorcore.onEditorEvent();
48083                     }
48084                 },
48085                 xns : rooui.Toolbar
48086             }
48087         ];
48088         
48089     },
48090     /**
48091      * create a DomHelper friendly object - for use with
48092      * Roo.DomHelper.markup / overwrite / etc..
48093      */
48094     toObject : function()
48095     {
48096         var d = document.createElement('div');
48097         d.innerHTML = this.caption;
48098         
48099         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
48100         
48101         var iw = this.align == 'center' ? this.width : '100%';
48102         var img =   {
48103             tag : 'img',
48104             contenteditable : 'false',
48105             src : this.image_src,
48106             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
48107             style: {
48108                 width : iw,
48109                 maxWidth : iw + ' !important', // this is not getting rendered?
48110                 margin : m 
48111                 
48112                 
48113             }
48114         };
48115         /*
48116         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
48117                     '<a href="{2}">' + 
48118                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
48119                     '</a>' + 
48120                 '</div>',
48121         */
48122                 
48123         if (this.href.length > 0) {
48124             img = {
48125                 tag : 'a',
48126                 href: this.href,
48127                 contenteditable : 'true',
48128                 cn : [
48129                     img
48130                 ]
48131             };
48132         }
48133         
48134         
48135         if (this.video_url.length > 0) {
48136             img = {
48137                 tag : 'div',
48138                 cls : this.cls,
48139                 frameborder : 0,
48140                 allowfullscreen : true,
48141                 width : 420,  // these are for video tricks - that we replace the outer
48142                 height : 315,
48143                 src : this.video_url,
48144                 cn : [
48145                     img
48146                 ]
48147             };
48148         }
48149         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
48150         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
48151         
48152         return  {
48153             tag: 'figure',
48154             'data-block' : 'Figure',
48155             
48156             contenteditable : 'false',
48157             
48158             style : {
48159                 display: 'block',
48160                 float :  this.align ,
48161                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
48162                 width : this.align == 'center' ? '100%' : this.width,
48163                 margin:  '0px',
48164                 padding: '10px',
48165                 textAlign : this.align   // seems to work for email..
48166                 
48167             },
48168            
48169             
48170             align : this.align,
48171             cn : [
48172                 img,
48173               
48174                 {
48175                     tag: 'figcaption',
48176                     'data-display' : this.caption_display,
48177                     style : {
48178                         textAlign : 'left',
48179                         fontSize : '16px',
48180                         lineHeight : '24px',
48181                         display : this.caption_display,
48182                         maxWidth : this.width + ' !important',
48183                         margin: m,
48184                         width: this.width
48185                     
48186                          
48187                     },
48188                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
48189                     cn : [
48190                         {
48191                             tag: 'div',
48192                             style  : {
48193                                 marginTop : '16px',
48194                                 textAlign : 'left'
48195                             },
48196                             align: 'left',
48197                             cn : [
48198                                 {
48199                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
48200                                     tag : 'i',
48201                                     contenteditable : true,
48202                                     html : captionhtml
48203                                 }
48204                                 
48205                             ]
48206                         }
48207                         
48208                     ]
48209                     
48210                 }
48211             ]
48212         };
48213          
48214     },
48215     
48216     readElement : function(node)
48217     {
48218         // this should not really come from the link...
48219         this.video_url = this.getVal(node, 'div', 'src');
48220         this.cls = this.getVal(node, 'div', 'class');
48221         this.href = this.getVal(node, 'a', 'href');
48222         
48223         
48224         this.image_src = this.getVal(node, 'img', 'src');
48225          
48226         this.align = this.getVal(node, 'figure', 'align');
48227         var figcaption = this.getVal(node, 'figcaption', false);
48228         this.caption = this.getVal(figcaption, 'i', 'html');
48229
48230         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
48231         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
48232         this.width = this.getVal(node, 'figcaption', 'style', 'width');
48233         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
48234         
48235     },
48236     removeNode : function()
48237     {
48238         return this.node;
48239     }
48240     
48241   
48242    
48243      
48244     
48245     
48246     
48247     
48248 })
48249
48250  
48251
48252 /**
48253  * @class Roo.htmleditor.BlockTable
48254  * Block that manages a table
48255  * 
48256  * @constructor
48257  * Create a new Filter.
48258  * @param {Object} config Configuration options
48259  */
48260
48261 Roo.htmleditor.BlockTable = function(cfg)
48262 {
48263     if (cfg.node) {
48264         this.readElement(cfg.node);
48265         this.updateElement(cfg.node);
48266     }
48267     Roo.apply(this, cfg);
48268     if (!cfg.node) {
48269         this.rows = [];
48270         for(var r = 0; r < this.no_row; r++) {
48271             this.rows[r] = [];
48272             for(var c = 0; c < this.no_col; c++) {
48273                 this.rows[r][c] = this.emptyCell();
48274             }
48275         }
48276     }
48277     
48278     
48279 }
48280 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
48281  
48282     rows : false,
48283     no_col : 1,
48284     no_row : 1,
48285     
48286     
48287     width: '100%',
48288     
48289     // used by context menu
48290     friendly_name : 'Table',
48291     deleteTitle : 'Delete Table',
48292     // context menu is drawn once..
48293     
48294     contextMenu : function(toolbar)
48295     {
48296         
48297         var block = function() {
48298             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48299         };
48300         
48301         
48302         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48303         
48304         var syncValue = toolbar.editorcore.syncValue;
48305         
48306         var fields = {};
48307         
48308         return [
48309             {
48310                 xtype : 'TextItem',
48311                 text : "Width: ",
48312                 xns : rooui.Toolbar  //Boostrap?
48313             },
48314             {
48315                 xtype : 'ComboBox',
48316                 allowBlank : false,
48317                 displayField : 'val',
48318                 editable : true,
48319                 listWidth : 100,
48320                 triggerAction : 'all',
48321                 typeAhead : true,
48322                 valueField : 'val',
48323                 width : 100,
48324                 name : 'width',
48325                 listeners : {
48326                     select : function (combo, r, index)
48327                     {
48328                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48329                         var b = block();
48330                         b.width = r.get('val');
48331                         b.updateElement();
48332                         syncValue();
48333                         toolbar.editorcore.onEditorEvent();
48334                     }
48335                 },
48336                 xns : rooui.form,
48337                 store : {
48338                     xtype : 'SimpleStore',
48339                     data : [
48340                         ['100%'],
48341                         ['auto']
48342                     ],
48343                     fields : [ 'val'],
48344                     xns : Roo.data
48345                 }
48346             },
48347             // -------- Cols
48348             
48349             {
48350                 xtype : 'TextItem',
48351                 text : "Columns: ",
48352                 xns : rooui.Toolbar  //Boostrap?
48353             },
48354          
48355             {
48356                 xtype : 'Button',
48357                 text: '-',
48358                 listeners : {
48359                     click : function (_self, e)
48360                     {
48361                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48362                         block().removeColumn();
48363                         syncValue();
48364                         toolbar.editorcore.onEditorEvent();
48365                     }
48366                 },
48367                 xns : rooui.Toolbar
48368             },
48369             {
48370                 xtype : 'Button',
48371                 text: '+',
48372                 listeners : {
48373                     click : function (_self, e)
48374                     {
48375                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48376                         block().addColumn();
48377                         syncValue();
48378                         toolbar.editorcore.onEditorEvent();
48379                     }
48380                 },
48381                 xns : rooui.Toolbar
48382             },
48383             // -------- ROWS
48384             {
48385                 xtype : 'TextItem',
48386                 text : "Rows: ",
48387                 xns : rooui.Toolbar  //Boostrap?
48388             },
48389          
48390             {
48391                 xtype : 'Button',
48392                 text: '-',
48393                 listeners : {
48394                     click : function (_self, e)
48395                     {
48396                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48397                         block().removeRow();
48398                         syncValue();
48399                         toolbar.editorcore.onEditorEvent();
48400                     }
48401                 },
48402                 xns : rooui.Toolbar
48403             },
48404             {
48405                 xtype : 'Button',
48406                 text: '+',
48407                 listeners : {
48408                     click : function (_self, e)
48409                     {
48410                         block().addRow();
48411                         syncValue();
48412                         toolbar.editorcore.onEditorEvent();
48413                     }
48414                 },
48415                 xns : rooui.Toolbar
48416             },
48417             // -------- ROWS
48418             {
48419                 xtype : 'Button',
48420                 text: 'Reset Column Widths',
48421                 listeners : {
48422                     
48423                     click : function (_self, e)
48424                     {
48425                         block().resetWidths();
48426                         syncValue();
48427                         toolbar.editorcore.onEditorEvent();
48428                     }
48429                 },
48430                 xns : rooui.Toolbar
48431             } 
48432             
48433             
48434             
48435         ];
48436         
48437     },
48438     
48439     
48440   /**
48441      * create a DomHelper friendly object - for use with
48442      * Roo.DomHelper.markup / overwrite / etc..
48443      * ?? should it be called with option to hide all editing features?
48444      */
48445     toObject : function()
48446     {
48447         
48448         var ret = {
48449             tag : 'table',
48450             contenteditable : 'false', // this stops cell selection from picking the table.
48451             'data-block' : 'Table',
48452             style : {
48453                 width:  this.width,
48454                 border : 'solid 1px #000', // ??? hard coded?
48455                 'border-collapse' : 'collapse' 
48456             },
48457             cn : [
48458                 { tag : 'tbody' , cn : [] }
48459             ]
48460         };
48461         
48462         // do we have a head = not really 
48463         var ncols = 0;
48464         Roo.each(this.rows, function( row ) {
48465             var tr = {
48466                 tag: 'tr',
48467                 style : {
48468                     margin: '6px',
48469                     border : 'solid 1px #000',
48470                     textAlign : 'left' 
48471                 },
48472                 cn : [ ]
48473             };
48474             
48475             ret.cn[0].cn.push(tr);
48476             // does the row have any properties? ?? height?
48477             var nc = 0;
48478             Roo.each(row, function( cell ) {
48479                 
48480                 var td = {
48481                     tag : 'td',
48482                     contenteditable :  'true',
48483                     'data-block' : 'Td',
48484                     html : cell.html,
48485                     style : cell.style
48486                 };
48487                 if (cell.colspan > 1) {
48488                     td.colspan = cell.colspan ;
48489                     nc += cell.colspan;
48490                 } else {
48491                     nc++;
48492                 }
48493                 if (cell.rowspan > 1) {
48494                     td.rowspan = cell.rowspan ;
48495                 }
48496                 
48497                 
48498                 // widths ?
48499                 tr.cn.push(td);
48500                     
48501                 
48502             }, this);
48503             ncols = Math.max(nc, ncols);
48504             
48505             
48506         }, this);
48507         // add the header row..
48508         
48509         ncols++;
48510          
48511         
48512         return ret;
48513          
48514     },
48515     
48516     readElement : function(node)
48517     {
48518         node  = node ? node : this.node ;
48519         this.width = this.getVal(node, true, 'style', 'width') || '100%';
48520         
48521         this.rows = [];
48522         this.no_row = 0;
48523         var trs = Array.from(node.rows);
48524         trs.forEach(function(tr) {
48525             var row =  [];
48526             this.rows.push(row);
48527             
48528             this.no_row++;
48529             var no_column = 0;
48530             Array.from(tr.cells).forEach(function(td) {
48531                 
48532                 var add = {
48533                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
48534                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
48535                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
48536                     html : td.innerHTML
48537                 };
48538                 no_column += add.colspan;
48539                      
48540                 
48541                 row.push(add);
48542                 
48543                 
48544             },this);
48545             this.no_col = Math.max(this.no_col, no_column);
48546             
48547             
48548         },this);
48549         
48550         
48551     },
48552     normalizeRows: function()
48553     {
48554         var ret= [];
48555         var rid = -1;
48556         this.rows.forEach(function(row) {
48557             rid++;
48558             ret[rid] = [];
48559             row = this.normalizeRow(row);
48560             var cid = 0;
48561             row.forEach(function(c) {
48562                 while (typeof(ret[rid][cid]) != 'undefined') {
48563                     cid++;
48564                 }
48565                 if (typeof(ret[rid]) == 'undefined') {
48566                     ret[rid] = [];
48567                 }
48568                 ret[rid][cid] = c;
48569                 c.row = rid;
48570                 c.col = cid;
48571                 if (c.rowspan < 2) {
48572                     return;
48573                 }
48574                 
48575                 for(var i = 1 ;i < c.rowspan; i++) {
48576                     if (typeof(ret[rid+i]) == 'undefined') {
48577                         ret[rid+i] = [];
48578                     }
48579                     ret[rid+i][cid] = c;
48580                 }
48581             });
48582         }, this);
48583         return ret;
48584     
48585     },
48586     
48587     normalizeRow: function(row)
48588     {
48589         var ret= [];
48590         row.forEach(function(c) {
48591             if (c.colspan < 2) {
48592                 ret.push(c);
48593                 return;
48594             }
48595             for(var i =0 ;i < c.colspan; i++) {
48596                 ret.push(c);
48597             }
48598         });
48599         return ret;
48600     
48601     },
48602     
48603     deleteColumn : function(sel)
48604     {
48605         if (!sel || sel.type != 'col') {
48606             return;
48607         }
48608         if (this.no_col < 2) {
48609             return;
48610         }
48611         
48612         this.rows.forEach(function(row) {
48613             var cols = this.normalizeRow(row);
48614             var col = cols[sel.col];
48615             if (col.colspan > 1) {
48616                 col.colspan --;
48617             } else {
48618                 row.remove(col);
48619             }
48620             
48621         }, this);
48622         this.no_col--;
48623         
48624     },
48625     removeColumn : function()
48626     {
48627         this.deleteColumn({
48628             type: 'col',
48629             col : this.no_col-1
48630         });
48631         this.updateElement();
48632     },
48633     
48634      
48635     addColumn : function()
48636     {
48637         
48638         this.rows.forEach(function(row) {
48639             row.push(this.emptyCell());
48640            
48641         }, this);
48642         this.updateElement();
48643     },
48644     
48645     deleteRow : function(sel)
48646     {
48647         if (!sel || sel.type != 'row') {
48648             return;
48649         }
48650         
48651         if (this.no_row < 2) {
48652             return;
48653         }
48654         
48655         var rows = this.normalizeRows();
48656         
48657         
48658         rows[sel.row].forEach(function(col) {
48659             if (col.rowspan > 1) {
48660                 col.rowspan--;
48661             } else {
48662                 col.remove = 1; // flage it as removed.
48663             }
48664             
48665         }, this);
48666         var newrows = [];
48667         this.rows.forEach(function(row) {
48668             newrow = [];
48669             row.forEach(function(c) {
48670                 if (typeof(c.remove) == 'undefined') {
48671                     newrow.push(c);
48672                 }
48673                 
48674             });
48675             if (newrow.length > 0) {
48676                 newrows.push(row);
48677             }
48678         });
48679         this.rows =  newrows;
48680         
48681         
48682         
48683         this.no_row--;
48684         this.updateElement();
48685         
48686     },
48687     removeRow : function()
48688     {
48689         this.deleteRow({
48690             type: 'row',
48691             row : this.no_row-1
48692         });
48693         
48694     },
48695     
48696      
48697     addRow : function()
48698     {
48699         
48700         var row = [];
48701         for (var i = 0; i < this.no_col; i++ ) {
48702             
48703             row.push(this.emptyCell());
48704            
48705         }
48706         this.rows.push(row);
48707         this.updateElement();
48708         
48709     },
48710      
48711     // the default cell object... at present...
48712     emptyCell : function() {
48713         return (new Roo.htmleditor.BlockTd({})).toObject();
48714         
48715      
48716     },
48717     
48718     removeNode : function()
48719     {
48720         return this.node;
48721     },
48722     
48723     
48724     
48725     resetWidths : function()
48726     {
48727         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
48728             var nn = Roo.htmleditor.Block.factory(n);
48729             nn.width = '';
48730             nn.updateElement(n);
48731         });
48732     }
48733     
48734     
48735     
48736     
48737 })
48738
48739 /**
48740  *
48741  * editing a TD?
48742  *
48743  * since selections really work on the table cell, then editing really should work from there
48744  *
48745  * The original plan was to support merging etc... - but that may not be needed yet..
48746  *
48747  * So this simple version will support:
48748  *   add/remove cols
48749  *   adjust the width +/-
48750  *   reset the width...
48751  *   
48752  *
48753  */
48754
48755
48756  
48757
48758 /**
48759  * @class Roo.htmleditor.BlockTable
48760  * Block that manages a table
48761  * 
48762  * @constructor
48763  * Create a new Filter.
48764  * @param {Object} config Configuration options
48765  */
48766
48767 Roo.htmleditor.BlockTd = function(cfg)
48768 {
48769     if (cfg.node) {
48770         this.readElement(cfg.node);
48771         this.updateElement(cfg.node);
48772     }
48773     Roo.apply(this, cfg);
48774      
48775     
48776     
48777 }
48778 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
48779  
48780     node : false,
48781     
48782     width: '',
48783     textAlign : 'left',
48784     valign : 'top',
48785     
48786     colspan : 1,
48787     rowspan : 1,
48788     
48789     
48790     // used by context menu
48791     friendly_name : 'Table Cell',
48792     deleteTitle : false, // use our customer delete
48793     
48794     // context menu is drawn once..
48795     
48796     contextMenu : function(toolbar)
48797     {
48798         
48799         var cell = function() {
48800             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48801         };
48802         
48803         var table = function() {
48804             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
48805         };
48806         
48807         var lr = false;
48808         var saveSel = function()
48809         {
48810             lr = toolbar.editorcore.getSelection().getRangeAt(0);
48811         }
48812         var restoreSel = function()
48813         {
48814             if (lr) {
48815                 (function() {
48816                     toolbar.editorcore.focus();
48817                     var cr = toolbar.editorcore.getSelection();
48818                     cr.removeAllRanges();
48819                     cr.addRange(lr);
48820                     toolbar.editorcore.onEditorEvent();
48821                 }).defer(10, this);
48822                 
48823                 
48824             }
48825         }
48826         
48827         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48828         
48829         var syncValue = toolbar.editorcore.syncValue;
48830         
48831         var fields = {};
48832         
48833         return [
48834             {
48835                 xtype : 'Button',
48836                 text : 'Edit Table',
48837                 listeners : {
48838                     click : function() {
48839                         var t = toolbar.tb.selectedNode.closest('table');
48840                         toolbar.editorcore.selectNode(t);
48841                         toolbar.editorcore.onEditorEvent();                        
48842                     }
48843                 }
48844                 
48845             },
48846               
48847            
48848              
48849             {
48850                 xtype : 'TextItem',
48851                 text : "Column Width: ",
48852                  xns : rooui.Toolbar 
48853                
48854             },
48855             {
48856                 xtype : 'Button',
48857                 text: '-',
48858                 listeners : {
48859                     click : function (_self, e)
48860                     {
48861                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48862                         cell().shrinkColumn();
48863                         syncValue();
48864                          toolbar.editorcore.onEditorEvent();
48865                     }
48866                 },
48867                 xns : rooui.Toolbar
48868             },
48869             {
48870                 xtype : 'Button',
48871                 text: '+',
48872                 listeners : {
48873                     click : function (_self, e)
48874                     {
48875                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48876                         cell().growColumn();
48877                         syncValue();
48878                         toolbar.editorcore.onEditorEvent();
48879                     }
48880                 },
48881                 xns : rooui.Toolbar
48882             },
48883             
48884             {
48885                 xtype : 'TextItem',
48886                 text : "Vertical Align: ",
48887                 xns : rooui.Toolbar  //Boostrap?
48888             },
48889             {
48890                 xtype : 'ComboBox',
48891                 allowBlank : false,
48892                 displayField : 'val',
48893                 editable : true,
48894                 listWidth : 100,
48895                 triggerAction : 'all',
48896                 typeAhead : true,
48897                 valueField : 'val',
48898                 width : 100,
48899                 name : 'valign',
48900                 listeners : {
48901                     select : function (combo, r, index)
48902                     {
48903                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48904                         var b = cell();
48905                         b.valign = r.get('val');
48906                         b.updateElement();
48907                         syncValue();
48908                         toolbar.editorcore.onEditorEvent();
48909                     }
48910                 },
48911                 xns : rooui.form,
48912                 store : {
48913                     xtype : 'SimpleStore',
48914                     data : [
48915                         ['top'],
48916                         ['middle'],
48917                         ['bottom'] // there are afew more... 
48918                     ],
48919                     fields : [ 'val'],
48920                     xns : Roo.data
48921                 }
48922             },
48923             
48924             {
48925                 xtype : 'TextItem',
48926                 text : "Merge Cells: ",
48927                  xns : rooui.Toolbar 
48928                
48929             },
48930             
48931             
48932             {
48933                 xtype : 'Button',
48934                 text: 'Right',
48935                 listeners : {
48936                     click : function (_self, e)
48937                     {
48938                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48939                         cell().mergeRight();
48940                         //block().growColumn();
48941                         syncValue();
48942                         toolbar.editorcore.onEditorEvent();
48943                     }
48944                 },
48945                 xns : rooui.Toolbar
48946             },
48947              
48948             {
48949                 xtype : 'Button',
48950                 text: 'Below',
48951                 listeners : {
48952                     click : function (_self, e)
48953                     {
48954                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48955                         cell().mergeBelow();
48956                         //block().growColumn();
48957                         syncValue();
48958                         toolbar.editorcore.onEditorEvent();
48959                     }
48960                 },
48961                 xns : rooui.Toolbar
48962             },
48963             {
48964                 xtype : 'TextItem',
48965                 text : "| ",
48966                  xns : rooui.Toolbar 
48967                
48968             },
48969             
48970             {
48971                 xtype : 'Button',
48972                 text: 'Split',
48973                 listeners : {
48974                     click : function (_self, e)
48975                     {
48976                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48977                         cell().split();
48978                         syncValue();
48979                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48980                         toolbar.editorcore.onEditorEvent();
48981                                              
48982                     }
48983                 },
48984                 xns : rooui.Toolbar
48985             },
48986             {
48987                 xtype : 'Fill',
48988                 xns : rooui.Toolbar 
48989                
48990             },
48991         
48992           
48993             {
48994                 xtype : 'Button',
48995                 text: 'Delete',
48996                  
48997                 xns : rooui.Toolbar,
48998                 menu : {
48999                     xtype : 'Menu',
49000                     xns : rooui.menu,
49001                     items : [
49002                         {
49003                             xtype : 'Item',
49004                             html: 'Column',
49005                             listeners : {
49006                                 click : function (_self, e)
49007                                 {
49008                                     var t = table();
49009                                     
49010                                     cell().deleteColumn();
49011                                     syncValue();
49012                                     toolbar.editorcore.selectNode(t.node);
49013                                     toolbar.editorcore.onEditorEvent();   
49014                                 }
49015                             },
49016                             xns : rooui.menu
49017                         },
49018                         {
49019                             xtype : 'Item',
49020                             html: 'Row',
49021                             listeners : {
49022                                 click : function (_self, e)
49023                                 {
49024                                     var t = table();
49025                                     cell().deleteRow();
49026                                     syncValue();
49027                                     
49028                                     toolbar.editorcore.selectNode(t.node);
49029                                     toolbar.editorcore.onEditorEvent();   
49030                                                          
49031                                 }
49032                             },
49033                             xns : rooui.menu
49034                         },
49035                        {
49036                             xtype : 'Separator',
49037                             xns : rooui.menu
49038                         },
49039                         {
49040                             xtype : 'Item',
49041                             html: 'Table',
49042                             listeners : {
49043                                 click : function (_self, e)
49044                                 {
49045                                     var t = table();
49046                                     var nn = t.node.nextSibling || t.node.previousSibling;
49047                                     t.node.parentNode.removeChild(t.node);
49048                                     if (nn) { 
49049                                         toolbar.editorcore.selectNode(nn, true);
49050                                     }
49051                                     toolbar.editorcore.onEditorEvent();   
49052                                                          
49053                                 }
49054                             },
49055                             xns : rooui.menu
49056                         }
49057                     ]
49058                 }
49059             }
49060             
49061             // align... << fixme
49062             
49063         ];
49064         
49065     },
49066     
49067     
49068   /**
49069      * create a DomHelper friendly object - for use with
49070      * Roo.DomHelper.markup / overwrite / etc..
49071      * ?? should it be called with option to hide all editing features?
49072      */
49073  /**
49074      * create a DomHelper friendly object - for use with
49075      * Roo.DomHelper.markup / overwrite / etc..
49076      * ?? should it be called with option to hide all editing features?
49077      */
49078     toObject : function()
49079     {
49080         
49081         var ret = {
49082             tag : 'td',
49083             contenteditable : 'true', // this stops cell selection from picking the table.
49084             'data-block' : 'Td',
49085             valign : this.valign,
49086             style : {  
49087                 'text-align' :  this.textAlign,
49088                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
49089                 'border-collapse' : 'collapse',
49090                 padding : '6px', // 8 for desktop / 4 for mobile
49091                 'vertical-align': this.valign
49092             },
49093             html : this.html
49094         };
49095         if (this.width != '') {
49096             ret.width = this.width;
49097             ret.style.width = this.width;
49098         }
49099         
49100         
49101         if (this.colspan > 1) {
49102             ret.colspan = this.colspan ;
49103         } 
49104         if (this.rowspan > 1) {
49105             ret.rowspan = this.rowspan ;
49106         }
49107         
49108            
49109         
49110         return ret;
49111          
49112     },
49113     
49114     readElement : function(node)
49115     {
49116         node  = node ? node : this.node ;
49117         this.width = node.style.width;
49118         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
49119         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
49120         this.html = node.innerHTML;
49121         
49122         
49123     },
49124      
49125     // the default cell object... at present...
49126     emptyCell : function() {
49127         return {
49128             colspan :  1,
49129             rowspan :  1,
49130             textAlign : 'left',
49131             html : "&nbsp;" // is this going to be editable now?
49132         };
49133      
49134     },
49135     
49136     removeNode : function()
49137     {
49138         return this.node.closest('table');
49139          
49140     },
49141     
49142     cellData : false,
49143     
49144     colWidths : false,
49145     
49146     toTableArray  : function()
49147     {
49148         var ret = [];
49149         var tab = this.node.closest('tr').closest('table');
49150         Array.from(tab.rows).forEach(function(r, ri){
49151             ret[ri] = [];
49152         });
49153         var rn = 0;
49154         this.colWidths = [];
49155         var all_auto = true;
49156         Array.from(tab.rows).forEach(function(r, ri){
49157             
49158             var cn = 0;
49159             Array.from(r.cells).forEach(function(ce, ci){
49160                 var c =  {
49161                     cell : ce,
49162                     row : rn,
49163                     col: cn,
49164                     colspan : ce.colSpan,
49165                     rowspan : ce.rowSpan
49166                 };
49167                 if (ce.isEqualNode(this.node)) {
49168                     this.cellData = c;
49169                 }
49170                 // if we have been filled up by a row?
49171                 if (typeof(ret[rn][cn]) != 'undefined') {
49172                     while(typeof(ret[rn][cn]) != 'undefined') {
49173                         cn++;
49174                     }
49175                     c.col = cn;
49176                 }
49177                 
49178                 if (typeof(this.colWidths[cn]) == 'undefined') {
49179                     this.colWidths[cn] =   ce.style.width;
49180                     if (this.colWidths[cn] != '') {
49181                         all_auto = false;
49182                     }
49183                 }
49184                 
49185                 
49186                 if (c.colspan < 2 && c.rowspan < 2 ) {
49187                     ret[rn][cn] = c;
49188                     cn++;
49189                     return;
49190                 }
49191                 for(var j = 0; j < c.rowspan; j++) {
49192                     if (typeof(ret[rn+j]) == 'undefined') {
49193                         continue; // we have a problem..
49194                     }
49195                     ret[rn+j][cn] = c;
49196                     for(var i = 0; i < c.colspan; i++) {
49197                         ret[rn+j][cn+i] = c;
49198                     }
49199                 }
49200                 
49201                 cn += c.colspan;
49202             }, this);
49203             rn++;
49204         }, this);
49205         
49206         // initalize widths.?
49207         // either all widths or no widths..
49208         if (all_auto) {
49209             this.colWidths[0] = false; // no widths flag.
49210         }
49211         
49212         
49213         return ret;
49214         
49215     },
49216     
49217     
49218     
49219     
49220     mergeRight: function()
49221     {
49222          
49223         // get the contents of the next cell along..
49224         var tr = this.node.closest('tr');
49225         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
49226         if (i >= tr.childNodes.length - 1) {
49227             return; // no cells on right to merge with.
49228         }
49229         var table = this.toTableArray();
49230         
49231         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
49232             return; // nothing right?
49233         }
49234         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
49235         // right cell - must be same rowspan and on the same row.
49236         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
49237             return; // right hand side is not same rowspan.
49238         }
49239         
49240         
49241         
49242         this.node.innerHTML += ' ' + rc.cell.innerHTML;
49243         tr.removeChild(rc.cell);
49244         this.colspan += rc.colspan;
49245         this.node.setAttribute('colspan', this.colspan);
49246
49247     },
49248     
49249     
49250     mergeBelow : function()
49251     {
49252         var table = this.toTableArray();
49253         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
49254             return; // no row below
49255         }
49256         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
49257             return; // nothing right?
49258         }
49259         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
49260         
49261         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
49262             return; // right hand side is not same rowspan.
49263         }
49264         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
49265         rc.cell.parentNode.removeChild(rc.cell);
49266         this.rowspan += rc.rowspan;
49267         this.node.setAttribute('rowspan', this.rowspan);
49268     },
49269     
49270     split: function()
49271     {
49272         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
49273             return;
49274         }
49275         var table = this.toTableArray();
49276         var cd = this.cellData;
49277         this.rowspan = 1;
49278         this.colspan = 1;
49279         
49280         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
49281             
49282             
49283             
49284             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
49285                 if (r == cd.row && c == cd.col) {
49286                     this.node.removeAttribute('rowspan');
49287                     this.node.removeAttribute('colspan');
49288                     continue;
49289                 }
49290                  
49291                 var ntd = this.node.cloneNode(); // which col/row should be 0..
49292                 ntd.removeAttribute('id'); //
49293                 //ntd.style.width  = '';
49294                 ntd.innerHTML = '';
49295                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
49296             }
49297             
49298         }
49299         this.redrawAllCells(table);
49300         
49301          
49302         
49303     },
49304     
49305     
49306     
49307     redrawAllCells: function(table)
49308     {
49309         
49310          
49311         var tab = this.node.closest('tr').closest('table');
49312         var ctr = tab.rows[0].parentNode;
49313         Array.from(tab.rows).forEach(function(r, ri){
49314             
49315             Array.from(r.cells).forEach(function(ce, ci){
49316                 ce.parentNode.removeChild(ce);
49317             });
49318             r.parentNode.removeChild(r);
49319         });
49320         for(var r = 0 ; r < table.length; r++) {
49321             var re = tab.rows[r];
49322             
49323             var re = tab.ownerDocument.createElement('tr');
49324             ctr.appendChild(re);
49325             for(var c = 0 ; c < table[r].length; c++) {
49326                 if (table[r][c].cell === false) {
49327                     continue;
49328                 }
49329                 
49330                 re.appendChild(table[r][c].cell);
49331                  
49332                 table[r][c].cell = false;
49333             }
49334         }
49335         
49336     },
49337     updateWidths : function(table)
49338     {
49339         for(var r = 0 ; r < table.length; r++) {
49340            
49341             for(var c = 0 ; c < table[r].length; c++) {
49342                 if (table[r][c].cell === false) {
49343                     continue;
49344                 }
49345                 
49346                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
49347                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49348                     el.width = Math.floor(this.colWidths[c])  +'%';
49349                     el.updateElement(el.node);
49350                 }
49351                 table[r][c].cell = false; // done
49352             }
49353         }
49354     },
49355     normalizeWidths : function(table)
49356     {
49357     
49358         if (this.colWidths[0] === false) {
49359             var nw = 100.0 / this.colWidths.length;
49360             this.colWidths.forEach(function(w,i) {
49361                 this.colWidths[i] = nw;
49362             },this);
49363             return;
49364         }
49365     
49366         var t = 0, missing = [];
49367         
49368         this.colWidths.forEach(function(w,i) {
49369             //if you mix % and
49370             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
49371             var add =  this.colWidths[i];
49372             if (add > 0) {
49373                 t+=add;
49374                 return;
49375             }
49376             missing.push(i);
49377             
49378             
49379         },this);
49380         var nc = this.colWidths.length;
49381         if (missing.length) {
49382             var mult = (nc - missing.length) / (1.0 * nc);
49383             var t = mult * t;
49384             var ew = (100 -t) / (1.0 * missing.length);
49385             this.colWidths.forEach(function(w,i) {
49386                 if (w > 0) {
49387                     this.colWidths[i] = w * mult;
49388                     return;
49389                 }
49390                 
49391                 this.colWidths[i] = ew;
49392             }, this);
49393             // have to make up numbers..
49394              
49395         }
49396         // now we should have all the widths..
49397         
49398     
49399     },
49400     
49401     shrinkColumn : function()
49402     {
49403         var table = this.toTableArray();
49404         this.normalizeWidths(table);
49405         var col = this.cellData.col;
49406         var nw = this.colWidths[col] * 0.8;
49407         if (nw < 5) {
49408             return;
49409         }
49410         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
49411         this.colWidths.forEach(function(w,i) {
49412             if (i == col) {
49413                  this.colWidths[i] = nw;
49414                 return;
49415             }
49416             this.colWidths[i] += otherAdd
49417         }, this);
49418         this.updateWidths(table);
49419          
49420     },
49421     growColumn : function()
49422     {
49423         var table = this.toTableArray();
49424         this.normalizeWidths(table);
49425         var col = this.cellData.col;
49426         var nw = this.colWidths[col] * 1.2;
49427         if (nw > 90) {
49428             return;
49429         }
49430         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
49431         this.colWidths.forEach(function(w,i) {
49432             if (i == col) {
49433                 this.colWidths[i] = nw;
49434                 return;
49435             }
49436             this.colWidths[i] -= otherSub
49437         }, this);
49438         this.updateWidths(table);
49439          
49440     },
49441     deleteRow : function()
49442     {
49443         // delete this rows 'tr'
49444         // if any of the cells in this row have a rowspan > 1 && row!= this row..
49445         // then reduce the rowspan.
49446         var table = this.toTableArray();
49447         // this.cellData.row;
49448         for (var i =0;i< table[this.cellData.row].length ; i++) {
49449             var c = table[this.cellData.row][i];
49450             if (c.row != this.cellData.row) {
49451                 
49452                 c.rowspan--;
49453                 c.cell.setAttribute('rowspan', c.rowspan);
49454                 continue;
49455             }
49456             if (c.rowspan > 1) {
49457                 c.rowspan--;
49458                 c.cell.setAttribute('rowspan', c.rowspan);
49459             }
49460         }
49461         table.splice(this.cellData.row,1);
49462         this.redrawAllCells(table);
49463         
49464     },
49465     deleteColumn : function()
49466     {
49467         var table = this.toTableArray();
49468         
49469         for (var i =0;i< table.length ; i++) {
49470             var c = table[i][this.cellData.col];
49471             if (c.col != this.cellData.col) {
49472                 table[i][this.cellData.col].colspan--;
49473             } else if (c.colspan > 1) {
49474                 c.colspan--;
49475                 c.cell.setAttribute('colspan', c.colspan);
49476             }
49477             table[i].splice(this.cellData.col,1);
49478         }
49479         
49480         this.redrawAllCells(table);
49481     }
49482     
49483     
49484     
49485     
49486 })
49487
49488 //<script type="text/javascript">
49489
49490 /*
49491  * Based  Ext JS Library 1.1.1
49492  * Copyright(c) 2006-2007, Ext JS, LLC.
49493  * LGPL
49494  *
49495  */
49496  
49497 /**
49498  * @class Roo.HtmlEditorCore
49499  * @extends Roo.Component
49500  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
49501  *
49502  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
49503  */
49504
49505 Roo.HtmlEditorCore = function(config){
49506     
49507     
49508     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
49509     
49510     
49511     this.addEvents({
49512         /**
49513          * @event initialize
49514          * Fires when the editor is fully initialized (including the iframe)
49515          * @param {Roo.HtmlEditorCore} this
49516          */
49517         initialize: true,
49518         /**
49519          * @event activate
49520          * Fires when the editor is first receives the focus. Any insertion must wait
49521          * until after this event.
49522          * @param {Roo.HtmlEditorCore} this
49523          */
49524         activate: true,
49525          /**
49526          * @event beforesync
49527          * Fires before the textarea is updated with content from the editor iframe. Return false
49528          * to cancel the sync.
49529          * @param {Roo.HtmlEditorCore} this
49530          * @param {String} html
49531          */
49532         beforesync: true,
49533          /**
49534          * @event beforepush
49535          * Fires before the iframe editor is updated with content from the textarea. Return false
49536          * to cancel the push.
49537          * @param {Roo.HtmlEditorCore} this
49538          * @param {String} html
49539          */
49540         beforepush: true,
49541          /**
49542          * @event sync
49543          * Fires when the textarea is updated with content from the editor iframe.
49544          * @param {Roo.HtmlEditorCore} this
49545          * @param {String} html
49546          */
49547         sync: true,
49548          /**
49549          * @event push
49550          * Fires when the iframe editor is updated with content from the textarea.
49551          * @param {Roo.HtmlEditorCore} this
49552          * @param {String} html
49553          */
49554         push: true,
49555         
49556         /**
49557          * @event editorevent
49558          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
49559          * @param {Roo.HtmlEditorCore} this
49560          */
49561         editorevent: true 
49562          
49563         
49564     });
49565     
49566     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
49567     
49568     // defaults : white / black...
49569     this.applyBlacklists();
49570     
49571     
49572     
49573 };
49574
49575
49576 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
49577
49578
49579      /**
49580      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
49581      */
49582     
49583     owner : false,
49584     
49585      /**
49586      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
49587      *                        Roo.resizable.
49588      */
49589     resizable : false,
49590      /**
49591      * @cfg {Number} height (in pixels)
49592      */   
49593     height: 300,
49594    /**
49595      * @cfg {Number} width (in pixels)
49596      */   
49597     width: 500,
49598      /**
49599      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
49600      *         if you are doing an email editor, this probably needs disabling, it's designed
49601      */
49602     autoClean: true,
49603     
49604     /**
49605      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
49606      */
49607     enableBlocks : true,
49608     /**
49609      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
49610      * 
49611      */
49612     stylesheets: false,
49613      /**
49614      * @cfg {String} language default en - language of text (usefull for rtl languages)
49615      * 
49616      */
49617     language: 'en',
49618     
49619     /**
49620      * @cfg {boolean} allowComments - default false - allow comments in HTML source
49621      *          - by default they are stripped - if you are editing email you may need this.
49622      */
49623     allowComments: false,
49624     // id of frame..
49625     frameId: false,
49626     
49627     // private properties
49628     validationEvent : false,
49629     deferHeight: true,
49630     initialized : false,
49631     activated : false,
49632     sourceEditMode : false,
49633     onFocus : Roo.emptyFn,
49634     iframePad:3,
49635     hideMode:'offsets',
49636     
49637     clearUp: true,
49638     
49639     // blacklist + whitelisted elements..
49640     black: false,
49641     white: false,
49642      
49643     bodyCls : '',
49644
49645     
49646     undoManager : false,
49647     /**
49648      * Protected method that will not generally be called directly. It
49649      * is called when the editor initializes the iframe with HTML contents. Override this method if you
49650      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
49651      */
49652     getDocMarkup : function(){
49653         // body styles..
49654         var st = '';
49655         
49656         // inherit styels from page...?? 
49657         if (this.stylesheets === false) {
49658             
49659             Roo.get(document.head).select('style').each(function(node) {
49660                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49661             });
49662             
49663             Roo.get(document.head).select('link').each(function(node) { 
49664                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49665             });
49666             
49667         } else if (!this.stylesheets.length) {
49668                 // simple..
49669                 st = '<style type="text/css">' +
49670                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49671                    '</style>';
49672         } else {
49673             for (var i in this.stylesheets) {
49674                 if (typeof(this.stylesheets[i]) != 'string') {
49675                     continue;
49676                 }
49677                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
49678             }
49679             
49680         }
49681         
49682         st +=  '<style type="text/css">' +
49683             'IMG { cursor: pointer } ' +
49684         '</style>';
49685         
49686         st += '<meta name="google" content="notranslate">';
49687         
49688         var cls = 'notranslate roo-htmleditor-body';
49689         
49690         if(this.bodyCls.length){
49691             cls += ' ' + this.bodyCls;
49692         }
49693         
49694         return '<html  class="notranslate" translate="no"><head>' + st  +
49695             //<style type="text/css">' +
49696             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49697             //'</style>' +
49698             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
49699     },
49700
49701     // private
49702     onRender : function(ct, position)
49703     {
49704         var _t = this;
49705         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
49706         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
49707         
49708         
49709         this.el.dom.style.border = '0 none';
49710         this.el.dom.setAttribute('tabIndex', -1);
49711         this.el.addClass('x-hidden hide');
49712         
49713         
49714         
49715         if(Roo.isIE){ // fix IE 1px bogus margin
49716             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
49717         }
49718        
49719         
49720         this.frameId = Roo.id();
49721         
49722          
49723         
49724         var iframe = this.owner.wrap.createChild({
49725             tag: 'iframe',
49726             cls: 'form-control', // bootstrap..
49727             id: this.frameId,
49728             name: this.frameId,
49729             frameBorder : 'no',
49730             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
49731         }, this.el
49732         );
49733         
49734         
49735         this.iframe = iframe.dom;
49736
49737         this.assignDocWin();
49738         
49739         this.doc.designMode = 'on';
49740        
49741         this.doc.open();
49742         this.doc.write(this.getDocMarkup());
49743         this.doc.close();
49744
49745         
49746         var task = { // must defer to wait for browser to be ready
49747             run : function(){
49748                 //console.log("run task?" + this.doc.readyState);
49749                 this.assignDocWin();
49750                 if(this.doc.body || this.doc.readyState == 'complete'){
49751                     try {
49752                         this.doc.designMode="on";
49753                         
49754                     } catch (e) {
49755                         return;
49756                     }
49757                     Roo.TaskMgr.stop(task);
49758                     this.initEditor.defer(10, this);
49759                 }
49760             },
49761             interval : 10,
49762             duration: 10000,
49763             scope: this
49764         };
49765         Roo.TaskMgr.start(task);
49766
49767     },
49768
49769     // private
49770     onResize : function(w, h)
49771     {
49772          Roo.log('resize: ' +w + ',' + h );
49773         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
49774         if(!this.iframe){
49775             return;
49776         }
49777         if(typeof w == 'number'){
49778             
49779             this.iframe.style.width = w + 'px';
49780         }
49781         if(typeof h == 'number'){
49782             
49783             this.iframe.style.height = h + 'px';
49784             if(this.doc){
49785                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
49786             }
49787         }
49788         
49789     },
49790
49791     /**
49792      * Toggles the editor between standard and source edit mode.
49793      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
49794      */
49795     toggleSourceEdit : function(sourceEditMode){
49796         
49797         this.sourceEditMode = sourceEditMode === true;
49798         
49799         if(this.sourceEditMode){
49800  
49801             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
49802             
49803         }else{
49804             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
49805             //this.iframe.className = '';
49806             this.deferFocus();
49807         }
49808         //this.setSize(this.owner.wrap.getSize());
49809         //this.fireEvent('editmodechange', this, this.sourceEditMode);
49810     },
49811
49812     
49813   
49814
49815     /**
49816      * Protected method that will not generally be called directly. If you need/want
49817      * custom HTML cleanup, this is the method you should override.
49818      * @param {String} html The HTML to be cleaned
49819      * return {String} The cleaned HTML
49820      */
49821     cleanHtml : function(html)
49822     {
49823         html = String(html);
49824         if(html.length > 5){
49825             if(Roo.isSafari){ // strip safari nonsense
49826                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
49827             }
49828         }
49829         if(html == '&nbsp;'){
49830             html = '';
49831         }
49832         return html;
49833     },
49834
49835     /**
49836      * HTML Editor -> Textarea
49837      * Protected method that will not generally be called directly. Syncs the contents
49838      * of the editor iframe with the textarea.
49839      */
49840     syncValue : function()
49841     {
49842         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
49843         if(this.initialized){
49844             
49845             this.undoManager.addEvent();
49846
49847             
49848             var bd = (this.doc.body || this.doc.documentElement);
49849            
49850             
49851             var sel = this.win.getSelection();
49852             
49853             var div = document.createElement('div');
49854             div.innerHTML = bd.innerHTML;
49855             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
49856             if (gtx.length > 0) {
49857                 var rm = gtx.item(0).parentNode;
49858                 rm.parentNode.removeChild(rm);
49859             }
49860             
49861            
49862             if (this.enableBlocks) {
49863                 new Roo.htmleditor.FilterBlock({ node : div });
49864             }
49865             //?? tidy?
49866             var tidy = new Roo.htmleditor.TidySerializer({
49867                 inner:  true
49868             });
49869             var html  = tidy.serialize(div);
49870             
49871             
49872             if(Roo.isSafari){
49873                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
49874                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
49875                 if(m && m[1]){
49876                     html = '<div style="'+m[0]+'">' + html + '</div>';
49877                 }
49878             }
49879             html = this.cleanHtml(html);
49880             // fix up the special chars.. normaly like back quotes in word...
49881             // however we do not want to do this with chinese..
49882             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
49883                 
49884                 var cc = match.charCodeAt();
49885
49886                 // Get the character value, handling surrogate pairs
49887                 if (match.length == 2) {
49888                     // It's a surrogate pair, calculate the Unicode code point
49889                     var high = match.charCodeAt(0) - 0xD800;
49890                     var low  = match.charCodeAt(1) - 0xDC00;
49891                     cc = (high * 0x400) + low + 0x10000;
49892                 }  else if (
49893                     (cc >= 0x4E00 && cc < 0xA000 ) ||
49894                     (cc >= 0x3400 && cc < 0x4E00 ) ||
49895                     (cc >= 0xf900 && cc < 0xfb00 )
49896                 ) {
49897                         return match;
49898                 }  
49899          
49900                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
49901                 return "&#" + cc + ";";
49902                 
49903                 
49904             });
49905             
49906             
49907              
49908             if(this.owner.fireEvent('beforesync', this, html) !== false){
49909                 this.el.dom.value = html;
49910                 this.owner.fireEvent('sync', this, html);
49911             }
49912         }
49913     },
49914
49915     /**
49916      * TEXTAREA -> EDITABLE
49917      * Protected method that will not generally be called directly. Pushes the value of the textarea
49918      * into the iframe editor.
49919      */
49920     pushValue : function()
49921     {
49922         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
49923         if(this.initialized){
49924             var v = this.el.dom.value.trim();
49925             
49926             
49927             if(this.owner.fireEvent('beforepush', this, v) !== false){
49928                 var d = (this.doc.body || this.doc.documentElement);
49929                 d.innerHTML = v;
49930                  
49931                 this.el.dom.value = d.innerHTML;
49932                 this.owner.fireEvent('push', this, v);
49933             }
49934             if (this.autoClean) {
49935                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
49936                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
49937             }
49938             if (this.enableBlocks) {
49939                 Roo.htmleditor.Block.initAll(this.doc.body);
49940             }
49941             
49942             this.updateLanguage();
49943             
49944             var lc = this.doc.body.lastChild;
49945             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
49946                 // add an extra line at the end.
49947                 this.doc.body.appendChild(this.doc.createElement('br'));
49948             }
49949             
49950             
49951         }
49952     },
49953
49954     // private
49955     deferFocus : function(){
49956         this.focus.defer(10, this);
49957     },
49958
49959     // doc'ed in Field
49960     focus : function(){
49961         if(this.win && !this.sourceEditMode){
49962             this.win.focus();
49963         }else{
49964             this.el.focus();
49965         }
49966     },
49967     
49968     assignDocWin: function()
49969     {
49970         var iframe = this.iframe;
49971         
49972          if(Roo.isIE){
49973             this.doc = iframe.contentWindow.document;
49974             this.win = iframe.contentWindow;
49975         } else {
49976 //            if (!Roo.get(this.frameId)) {
49977 //                return;
49978 //            }
49979 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
49980 //            this.win = Roo.get(this.frameId).dom.contentWindow;
49981             
49982             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
49983                 return;
49984             }
49985             
49986             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
49987             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
49988         }
49989     },
49990     
49991     // private
49992     initEditor : function(){
49993         //console.log("INIT EDITOR");
49994         this.assignDocWin();
49995         
49996         
49997         
49998         this.doc.designMode="on";
49999         this.doc.open();
50000         this.doc.write(this.getDocMarkup());
50001         this.doc.close();
50002         
50003         var dbody = (this.doc.body || this.doc.documentElement);
50004         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
50005         // this copies styles from the containing element into thsi one..
50006         // not sure why we need all of this..
50007         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
50008         
50009         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
50010         //ss['background-attachment'] = 'fixed'; // w3c
50011         dbody.bgProperties = 'fixed'; // ie
50012         dbody.setAttribute("translate", "no");
50013         
50014         //Roo.DomHelper.applyStyles(dbody, ss);
50015         Roo.EventManager.on(this.doc, {
50016              
50017             'mouseup': this.onEditorEvent,
50018             'dblclick': this.onEditorEvent,
50019             'click': this.onEditorEvent,
50020             'keyup': this.onEditorEvent,
50021             
50022             buffer:100,
50023             scope: this
50024         });
50025         Roo.EventManager.on(this.doc, {
50026             'paste': this.onPasteEvent,
50027             scope : this
50028         });
50029         if(Roo.isGecko){
50030             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
50031         }
50032         //??? needed???
50033         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
50034             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
50035         }
50036         this.initialized = true;
50037
50038         
50039         // initialize special key events - enter
50040         new Roo.htmleditor.KeyEnter({core : this});
50041         
50042          
50043         
50044         this.owner.fireEvent('initialize', this);
50045         this.pushValue();
50046     },
50047     // this is to prevent a href clicks resulting in a redirect?
50048    
50049     onPasteEvent : function(e,v)
50050     {
50051         // I think we better assume paste is going to be a dirty load of rubish from word..
50052         
50053         // even pasting into a 'email version' of this widget will have to clean up that mess.
50054         var cd = (e.browserEvent.clipboardData || window.clipboardData);
50055         
50056         // check what type of paste - if it's an image, then handle it differently.
50057         if (cd.files && cd.files.length > 0) {
50058             // pasting images?
50059             var urlAPI = (window.createObjectURL && window) || 
50060                 (window.URL && URL.revokeObjectURL && URL) || 
50061                 (window.webkitURL && webkitURL);
50062     
50063             var url = urlAPI.createObjectURL( cd.files[0]);
50064             this.insertAtCursor('<img src=" + url + ">');
50065             return false;
50066         }
50067         if (cd.types.indexOf('text/html') < 0 ) {
50068             return false;
50069         }
50070         var images = [];
50071         var html = cd.getData('text/html'); // clipboard event
50072         if (cd.types.indexOf('text/rtf') > -1) {
50073             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
50074             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
50075         }
50076         Roo.log(images);
50077         //Roo.log(imgs);
50078         // fixme..
50079         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
50080                        .map(function(g) { return g.toDataURL(); })
50081                        .filter(function(g) { return g != 'about:blank'; });
50082         
50083         
50084         html = this.cleanWordChars(html);
50085         
50086         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
50087         
50088         
50089         var sn = this.getParentElement();
50090         // check if d contains a table, and prevent nesting??
50091         //Roo.log(d.getElementsByTagName('table'));
50092         //Roo.log(sn);
50093         //Roo.log(sn.closest('table'));
50094         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
50095             e.preventDefault();
50096             this.insertAtCursor("You can not nest tables");
50097             //Roo.log("prevent?"); // fixme - 
50098             return false;
50099         }
50100         
50101         if (images.length > 0) {
50102             Roo.each(d.getElementsByTagName('img'), function(img, i) {
50103                 img.setAttribute('src', images[i]);
50104             });
50105         }
50106         if (this.autoClean) {
50107             new Roo.htmleditor.FilterStyleToTag({ node : d });
50108             new Roo.htmleditor.FilterAttributes({
50109                 node : d,
50110                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan'],
50111                 attrib_clean : ['href', 'src' ] 
50112             });
50113             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
50114             // should be fonts..
50115             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
50116             new Roo.htmleditor.FilterParagraph({ node : d });
50117             new Roo.htmleditor.FilterSpan({ node : d });
50118             new Roo.htmleditor.FilterLongBr({ node : d });
50119         }
50120         if (this.enableBlocks) {
50121                 
50122             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50123                 if (img.closest('figure')) { // assume!! that it's aready
50124                     return;
50125                 }
50126                 var fig  = new Roo.htmleditor.BlockFigure({
50127                     image_src  : img.src
50128                 });
50129                 fig.updateElement(img); // replace it..
50130                 
50131             });
50132         }
50133         
50134         
50135         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
50136         if (this.enableBlocks) {
50137             Roo.htmleditor.Block.initAll(this.doc.body);
50138         }
50139         
50140         
50141         e.preventDefault();
50142         return false;
50143         // default behaveiour should be our local cleanup paste? (optional?)
50144         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
50145         //this.owner.fireEvent('paste', e, v);
50146     },
50147     // private
50148     onDestroy : function(){
50149         
50150         
50151         
50152         if(this.rendered){
50153             
50154             //for (var i =0; i < this.toolbars.length;i++) {
50155             //    // fixme - ask toolbars for heights?
50156             //    this.toolbars[i].onDestroy();
50157            // }
50158             
50159             //this.wrap.dom.innerHTML = '';
50160             //this.wrap.remove();
50161         }
50162     },
50163
50164     // private
50165     onFirstFocus : function(){
50166         
50167         this.assignDocWin();
50168         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
50169         
50170         this.activated = true;
50171          
50172     
50173         if(Roo.isGecko){ // prevent silly gecko errors
50174             this.win.focus();
50175             var s = this.win.getSelection();
50176             if(!s.focusNode || s.focusNode.nodeType != 3){
50177                 var r = s.getRangeAt(0);
50178                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
50179                 r.collapse(true);
50180                 this.deferFocus();
50181             }
50182             try{
50183                 this.execCmd('useCSS', true);
50184                 this.execCmd('styleWithCSS', false);
50185             }catch(e){}
50186         }
50187         this.owner.fireEvent('activate', this);
50188     },
50189
50190     // private
50191     adjustFont: function(btn){
50192         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
50193         //if(Roo.isSafari){ // safari
50194         //    adjust *= 2;
50195        // }
50196         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
50197         if(Roo.isSafari){ // safari
50198             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
50199             v =  (v < 10) ? 10 : v;
50200             v =  (v > 48) ? 48 : v;
50201             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
50202             
50203         }
50204         
50205         
50206         v = Math.max(1, v+adjust);
50207         
50208         this.execCmd('FontSize', v  );
50209     },
50210
50211     onEditorEvent : function(e)
50212     {
50213          
50214         
50215         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
50216             return; // we do not handle this.. (undo manager does..)
50217         }
50218         // in theory this detects if the last element is not a br, then we try and do that.
50219         // its so clicking in space at bottom triggers adding a br and moving the cursor.
50220         if (e &&
50221             e.target.nodeName == 'BODY' &&
50222             e.type == "mouseup" &&
50223             this.doc.body.lastChild
50224            ) {
50225             var lc = this.doc.body.lastChild;
50226             // gtx-trans is google translate plugin adding crap.
50227             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
50228                 lc = lc.previousSibling;
50229             }
50230             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
50231             // if last element is <BR> - then dont do anything.
50232             
50233                 var ns = this.doc.createElement('br');
50234                 this.doc.body.appendChild(ns);
50235                 range = this.doc.createRange();
50236                 range.setStartAfter(ns);
50237                 range.collapse(true);
50238                 var sel = this.win.getSelection();
50239                 sel.removeAllRanges();
50240                 sel.addRange(range);
50241             }
50242         }
50243         
50244         
50245         
50246         this.fireEditorEvent(e);
50247       //  this.updateToolbar();
50248         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
50249     },
50250     
50251     fireEditorEvent: function(e)
50252     {
50253         this.owner.fireEvent('editorevent', this, e);
50254     },
50255
50256     insertTag : function(tg)
50257     {
50258         // could be a bit smarter... -> wrap the current selected tRoo..
50259         if (tg.toLowerCase() == 'span' ||
50260             tg.toLowerCase() == 'code' ||
50261             tg.toLowerCase() == 'sup' ||
50262             tg.toLowerCase() == 'sub' 
50263             ) {
50264             
50265             range = this.createRange(this.getSelection());
50266             var wrappingNode = this.doc.createElement(tg.toLowerCase());
50267             wrappingNode.appendChild(range.extractContents());
50268             range.insertNode(wrappingNode);
50269
50270             return;
50271             
50272             
50273             
50274         }
50275         this.execCmd("formatblock",   tg);
50276         this.undoManager.addEvent(); 
50277     },
50278     
50279     insertText : function(txt)
50280     {
50281         
50282         
50283         var range = this.createRange();
50284         range.deleteContents();
50285                //alert(Sender.getAttribute('label'));
50286                
50287         range.insertNode(this.doc.createTextNode(txt));
50288         this.undoManager.addEvent();
50289     } ,
50290     
50291      
50292
50293     /**
50294      * Executes a Midas editor command on the editor document and performs necessary focus and
50295      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
50296      * @param {String} cmd The Midas command
50297      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50298      */
50299     relayCmd : function(cmd, value)
50300     {
50301         
50302         switch (cmd) {
50303             case 'justifyleft':
50304             case 'justifyright':
50305             case 'justifycenter':
50306                 // if we are in a cell, then we will adjust the
50307                 var n = this.getParentElement();
50308                 var td = n.closest('td');
50309                 if (td) {
50310                     var bl = Roo.htmleditor.Block.factory(td);
50311                     bl.textAlign = cmd.replace('justify','');
50312                     bl.updateElement();
50313                     this.owner.fireEvent('editorevent', this);
50314                     return;
50315                 }
50316                 this.execCmd('styleWithCSS', true); // 
50317                 break;
50318             case 'bold':
50319             case 'italic':
50320                 // if there is no selection, then we insert, and set the curson inside it..
50321                 this.execCmd('styleWithCSS', false); 
50322                 break;
50323                 
50324         
50325             default:
50326                 break;
50327         }
50328         
50329         
50330         this.win.focus();
50331         this.execCmd(cmd, value);
50332         this.owner.fireEvent('editorevent', this);
50333         //this.updateToolbar();
50334         this.owner.deferFocus();
50335     },
50336
50337     /**
50338      * Executes a Midas editor command directly on the editor document.
50339      * For visual commands, you should use {@link #relayCmd} instead.
50340      * <b>This should only be called after the editor is initialized.</b>
50341      * @param {String} cmd The Midas command
50342      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50343      */
50344     execCmd : function(cmd, value){
50345         this.doc.execCommand(cmd, false, value === undefined ? null : value);
50346         this.syncValue();
50347     },
50348  
50349  
50350    
50351     /**
50352      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
50353      * to insert tRoo.
50354      * @param {String} text | dom node.. 
50355      */
50356     insertAtCursor : function(text)
50357     {
50358         
50359         if(!this.activated){
50360             return;
50361         }
50362          
50363         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
50364             this.win.focus();
50365             
50366             
50367             // from jquery ui (MIT licenced)
50368             var range, node;
50369             var win = this.win;
50370             
50371             if (win.getSelection && win.getSelection().getRangeAt) {
50372                 
50373                 // delete the existing?
50374                 
50375                 this.createRange(this.getSelection()).deleteContents();
50376                 range = win.getSelection().getRangeAt(0);
50377                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
50378                 range.insertNode(node);
50379                 range = range.cloneRange();
50380                 range.collapse(false);
50381                  
50382                 win.getSelection().removeAllRanges();
50383                 win.getSelection().addRange(range);
50384                 
50385                 
50386                 
50387             } else if (win.document.selection && win.document.selection.createRange) {
50388                 // no firefox support
50389                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50390                 win.document.selection.createRange().pasteHTML(txt);
50391             
50392             } else {
50393                 // no firefox support
50394                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50395                 this.execCmd('InsertHTML', txt);
50396             } 
50397             this.syncValue();
50398             
50399             this.deferFocus();
50400         }
50401     },
50402  // private
50403     mozKeyPress : function(e){
50404         if(e.ctrlKey){
50405             var c = e.getCharCode(), cmd;
50406           
50407             if(c > 0){
50408                 c = String.fromCharCode(c).toLowerCase();
50409                 switch(c){
50410                     case 'b':
50411                         cmd = 'bold';
50412                         break;
50413                     case 'i':
50414                         cmd = 'italic';
50415                         break;
50416                     
50417                     case 'u':
50418                         cmd = 'underline';
50419                         break;
50420                     
50421                     //case 'v':
50422                       //  this.cleanUpPaste.defer(100, this);
50423                       //  return;
50424                         
50425                 }
50426                 if(cmd){
50427                     
50428                     this.relayCmd(cmd);
50429                     //this.win.focus();
50430                     //this.execCmd(cmd);
50431                     //this.deferFocus();
50432                     e.preventDefault();
50433                 }
50434                 
50435             }
50436         }
50437     },
50438
50439     // private
50440     fixKeys : function(){ // load time branching for fastest keydown performance
50441         
50442         
50443         if(Roo.isIE){
50444             return function(e){
50445                 var k = e.getKey(), r;
50446                 if(k == e.TAB){
50447                     e.stopEvent();
50448                     r = this.doc.selection.createRange();
50449                     if(r){
50450                         r.collapse(true);
50451                         r.pasteHTML('&#160;&#160;&#160;&#160;');
50452                         this.deferFocus();
50453                     }
50454                     return;
50455                 }
50456                 /// this is handled by Roo.htmleditor.KeyEnter
50457                  /*
50458                 if(k == e.ENTER){
50459                     r = this.doc.selection.createRange();
50460                     if(r){
50461                         var target = r.parentElement();
50462                         if(!target || target.tagName.toLowerCase() != 'li'){
50463                             e.stopEvent();
50464                             r.pasteHTML('<br/>');
50465                             r.collapse(false);
50466                             r.select();
50467                         }
50468                     }
50469                 }
50470                 */
50471                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50472                 //    this.cleanUpPaste.defer(100, this);
50473                 //    return;
50474                 //}
50475                 
50476                 
50477             };
50478         }else if(Roo.isOpera){
50479             return function(e){
50480                 var k = e.getKey();
50481                 if(k == e.TAB){
50482                     e.stopEvent();
50483                     this.win.focus();
50484                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
50485                     this.deferFocus();
50486                 }
50487                
50488                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50489                 //    this.cleanUpPaste.defer(100, this);
50490                  //   return;
50491                 //}
50492                 
50493             };
50494         }else if(Roo.isSafari){
50495             return function(e){
50496                 var k = e.getKey();
50497                 
50498                 if(k == e.TAB){
50499                     e.stopEvent();
50500                     this.execCmd('InsertText','\t');
50501                     this.deferFocus();
50502                     return;
50503                 }
50504                  this.mozKeyPress(e);
50505                 
50506                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50507                  //   this.cleanUpPaste.defer(100, this);
50508                  //   return;
50509                // }
50510                 
50511              };
50512         }
50513     }(),
50514     
50515     getAllAncestors: function()
50516     {
50517         var p = this.getSelectedNode();
50518         var a = [];
50519         if (!p) {
50520             a.push(p); // push blank onto stack..
50521             p = this.getParentElement();
50522         }
50523         
50524         
50525         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
50526             a.push(p);
50527             p = p.parentNode;
50528         }
50529         a.push(this.doc.body);
50530         return a;
50531     },
50532     lastSel : false,
50533     lastSelNode : false,
50534     
50535     
50536     getSelection : function() 
50537     {
50538         this.assignDocWin();
50539         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
50540     },
50541     /**
50542      * Select a dom node
50543      * @param {DomElement} node the node to select
50544      */
50545     selectNode : function(node, collapse)
50546     {
50547         var nodeRange = node.ownerDocument.createRange();
50548         try {
50549             nodeRange.selectNode(node);
50550         } catch (e) {
50551             nodeRange.selectNodeContents(node);
50552         }
50553         if (collapse === true) {
50554             nodeRange.collapse(true);
50555         }
50556         //
50557         var s = this.win.getSelection();
50558         s.removeAllRanges();
50559         s.addRange(nodeRange);
50560     },
50561     
50562     getSelectedNode: function() 
50563     {
50564         // this may only work on Gecko!!!
50565         
50566         // should we cache this!!!!
50567         
50568          
50569          
50570         var range = this.createRange(this.getSelection()).cloneRange();
50571         
50572         if (Roo.isIE) {
50573             var parent = range.parentElement();
50574             while (true) {
50575                 var testRange = range.duplicate();
50576                 testRange.moveToElementText(parent);
50577                 if (testRange.inRange(range)) {
50578                     break;
50579                 }
50580                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
50581                     break;
50582                 }
50583                 parent = parent.parentElement;
50584             }
50585             return parent;
50586         }
50587         
50588         // is ancestor a text element.
50589         var ac =  range.commonAncestorContainer;
50590         if (ac.nodeType == 3) {
50591             ac = ac.parentNode;
50592         }
50593         
50594         var ar = ac.childNodes;
50595          
50596         var nodes = [];
50597         var other_nodes = [];
50598         var has_other_nodes = false;
50599         for (var i=0;i<ar.length;i++) {
50600             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
50601                 continue;
50602             }
50603             // fullly contained node.
50604             
50605             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
50606                 nodes.push(ar[i]);
50607                 continue;
50608             }
50609             
50610             // probably selected..
50611             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
50612                 other_nodes.push(ar[i]);
50613                 continue;
50614             }
50615             // outer..
50616             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
50617                 continue;
50618             }
50619             
50620             
50621             has_other_nodes = true;
50622         }
50623         if (!nodes.length && other_nodes.length) {
50624             nodes= other_nodes;
50625         }
50626         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
50627             return false;
50628         }
50629         
50630         return nodes[0];
50631     },
50632     
50633     
50634     createRange: function(sel)
50635     {
50636         // this has strange effects when using with 
50637         // top toolbar - not sure if it's a great idea.
50638         //this.editor.contentWindow.focus();
50639         if (typeof sel != "undefined") {
50640             try {
50641                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
50642             } catch(e) {
50643                 return this.doc.createRange();
50644             }
50645         } else {
50646             return this.doc.createRange();
50647         }
50648     },
50649     getParentElement: function()
50650     {
50651         
50652         this.assignDocWin();
50653         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
50654         
50655         var range = this.createRange(sel);
50656          
50657         try {
50658             var p = range.commonAncestorContainer;
50659             while (p.nodeType == 3) { // text node
50660                 p = p.parentNode;
50661             }
50662             return p;
50663         } catch (e) {
50664             return null;
50665         }
50666     
50667     },
50668     /***
50669      *
50670      * Range intersection.. the hard stuff...
50671      *  '-1' = before
50672      *  '0' = hits..
50673      *  '1' = after.
50674      *         [ -- selected range --- ]
50675      *   [fail]                        [fail]
50676      *
50677      *    basically..
50678      *      if end is before start or  hits it. fail.
50679      *      if start is after end or hits it fail.
50680      *
50681      *   if either hits (but other is outside. - then it's not 
50682      *   
50683      *    
50684      **/
50685     
50686     
50687     // @see http://www.thismuchiknow.co.uk/?p=64.
50688     rangeIntersectsNode : function(range, node)
50689     {
50690         var nodeRange = node.ownerDocument.createRange();
50691         try {
50692             nodeRange.selectNode(node);
50693         } catch (e) {
50694             nodeRange.selectNodeContents(node);
50695         }
50696     
50697         var rangeStartRange = range.cloneRange();
50698         rangeStartRange.collapse(true);
50699     
50700         var rangeEndRange = range.cloneRange();
50701         rangeEndRange.collapse(false);
50702     
50703         var nodeStartRange = nodeRange.cloneRange();
50704         nodeStartRange.collapse(true);
50705     
50706         var nodeEndRange = nodeRange.cloneRange();
50707         nodeEndRange.collapse(false);
50708     
50709         return rangeStartRange.compareBoundaryPoints(
50710                  Range.START_TO_START, nodeEndRange) == -1 &&
50711                rangeEndRange.compareBoundaryPoints(
50712                  Range.START_TO_START, nodeStartRange) == 1;
50713         
50714          
50715     },
50716     rangeCompareNode : function(range, node)
50717     {
50718         var nodeRange = node.ownerDocument.createRange();
50719         try {
50720             nodeRange.selectNode(node);
50721         } catch (e) {
50722             nodeRange.selectNodeContents(node);
50723         }
50724         
50725         
50726         range.collapse(true);
50727     
50728         nodeRange.collapse(true);
50729      
50730         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
50731         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
50732          
50733         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
50734         
50735         var nodeIsBefore   =  ss == 1;
50736         var nodeIsAfter    = ee == -1;
50737         
50738         if (nodeIsBefore && nodeIsAfter) {
50739             return 0; // outer
50740         }
50741         if (!nodeIsBefore && nodeIsAfter) {
50742             return 1; //right trailed.
50743         }
50744         
50745         if (nodeIsBefore && !nodeIsAfter) {
50746             return 2;  // left trailed.
50747         }
50748         // fully contined.
50749         return 3;
50750     },
50751  
50752     cleanWordChars : function(input) {// change the chars to hex code
50753         
50754        var swapCodes  = [ 
50755             [    8211, "&#8211;" ], 
50756             [    8212, "&#8212;" ], 
50757             [    8216,  "'" ],  
50758             [    8217, "'" ],  
50759             [    8220, '"' ],  
50760             [    8221, '"' ],  
50761             [    8226, "*" ],  
50762             [    8230, "..." ]
50763         ]; 
50764         var output = input;
50765         Roo.each(swapCodes, function(sw) { 
50766             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
50767             
50768             output = output.replace(swapper, sw[1]);
50769         });
50770         
50771         return output;
50772     },
50773     
50774      
50775     
50776         
50777     
50778     cleanUpChild : function (node)
50779     {
50780         
50781         new Roo.htmleditor.FilterComment({node : node});
50782         new Roo.htmleditor.FilterAttributes({
50783                 node : node,
50784                 attrib_black : this.ablack,
50785                 attrib_clean : this.aclean,
50786                 style_white : this.cwhite,
50787                 style_black : this.cblack
50788         });
50789         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
50790         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
50791          
50792         
50793     },
50794     
50795     /**
50796      * Clean up MS wordisms...
50797      * @deprecated - use filter directly
50798      */
50799     cleanWord : function(node)
50800     {
50801         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
50802         
50803     },
50804    
50805     
50806     /**
50807
50808      * @deprecated - use filters
50809      */
50810     cleanTableWidths : function(node)
50811     {
50812         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
50813         
50814  
50815     },
50816     
50817      
50818         
50819     applyBlacklists : function()
50820     {
50821         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
50822         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
50823         
50824         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
50825         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
50826         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
50827         
50828         this.white = [];
50829         this.black = [];
50830         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
50831             if (b.indexOf(tag) > -1) {
50832                 return;
50833             }
50834             this.white.push(tag);
50835             
50836         }, this);
50837         
50838         Roo.each(w, function(tag) {
50839             if (b.indexOf(tag) > -1) {
50840                 return;
50841             }
50842             if (this.white.indexOf(tag) > -1) {
50843                 return;
50844             }
50845             this.white.push(tag);
50846             
50847         }, this);
50848         
50849         
50850         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
50851             if (w.indexOf(tag) > -1) {
50852                 return;
50853             }
50854             this.black.push(tag);
50855             
50856         }, this);
50857         
50858         Roo.each(b, function(tag) {
50859             if (w.indexOf(tag) > -1) {
50860                 return;
50861             }
50862             if (this.black.indexOf(tag) > -1) {
50863                 return;
50864             }
50865             this.black.push(tag);
50866             
50867         }, this);
50868         
50869         
50870         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
50871         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
50872         
50873         this.cwhite = [];
50874         this.cblack = [];
50875         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
50876             if (b.indexOf(tag) > -1) {
50877                 return;
50878             }
50879             this.cwhite.push(tag);
50880             
50881         }, this);
50882         
50883         Roo.each(w, function(tag) {
50884             if (b.indexOf(tag) > -1) {
50885                 return;
50886             }
50887             if (this.cwhite.indexOf(tag) > -1) {
50888                 return;
50889             }
50890             this.cwhite.push(tag);
50891             
50892         }, this);
50893         
50894         
50895         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
50896             if (w.indexOf(tag) > -1) {
50897                 return;
50898             }
50899             this.cblack.push(tag);
50900             
50901         }, this);
50902         
50903         Roo.each(b, function(tag) {
50904             if (w.indexOf(tag) > -1) {
50905                 return;
50906             }
50907             if (this.cblack.indexOf(tag) > -1) {
50908                 return;
50909             }
50910             this.cblack.push(tag);
50911             
50912         }, this);
50913     },
50914     
50915     setStylesheets : function(stylesheets)
50916     {
50917         if(typeof(stylesheets) == 'string'){
50918             Roo.get(this.iframe.contentDocument.head).createChild({
50919                 tag : 'link',
50920                 rel : 'stylesheet',
50921                 type : 'text/css',
50922                 href : stylesheets
50923             });
50924             
50925             return;
50926         }
50927         var _this = this;
50928      
50929         Roo.each(stylesheets, function(s) {
50930             if(!s.length){
50931                 return;
50932             }
50933             
50934             Roo.get(_this.iframe.contentDocument.head).createChild({
50935                 tag : 'link',
50936                 rel : 'stylesheet',
50937                 type : 'text/css',
50938                 href : s
50939             });
50940         });
50941
50942         
50943     },
50944     
50945     
50946     updateLanguage : function()
50947     {
50948         if (!this.iframe || !this.iframe.contentDocument) {
50949             return;
50950         }
50951         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
50952     },
50953     
50954     
50955     removeStylesheets : function()
50956     {
50957         var _this = this;
50958         
50959         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
50960             s.remove();
50961         });
50962     },
50963     
50964     setStyle : function(style)
50965     {
50966         Roo.get(this.iframe.contentDocument.head).createChild({
50967             tag : 'style',
50968             type : 'text/css',
50969             html : style
50970         });
50971
50972         return;
50973     }
50974     
50975     // hide stuff that is not compatible
50976     /**
50977      * @event blur
50978      * @hide
50979      */
50980     /**
50981      * @event change
50982      * @hide
50983      */
50984     /**
50985      * @event focus
50986      * @hide
50987      */
50988     /**
50989      * @event specialkey
50990      * @hide
50991      */
50992     /**
50993      * @cfg {String} fieldClass @hide
50994      */
50995     /**
50996      * @cfg {String} focusClass @hide
50997      */
50998     /**
50999      * @cfg {String} autoCreate @hide
51000      */
51001     /**
51002      * @cfg {String} inputType @hide
51003      */
51004     /**
51005      * @cfg {String} invalidClass @hide
51006      */
51007     /**
51008      * @cfg {String} invalidText @hide
51009      */
51010     /**
51011      * @cfg {String} msgFx @hide
51012      */
51013     /**
51014      * @cfg {String} validateOnBlur @hide
51015      */
51016 });
51017
51018 Roo.HtmlEditorCore.white = [
51019         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
51020         
51021        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
51022        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
51023        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
51024        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
51025        'TABLE',   'UL',         'XMP', 
51026        
51027        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
51028       'THEAD',   'TR', 
51029      
51030       'DIR', 'MENU', 'OL', 'UL', 'DL',
51031        
51032       'EMBED',  'OBJECT'
51033 ];
51034
51035
51036 Roo.HtmlEditorCore.black = [
51037     //    'embed',  'object', // enable - backend responsiblity to clean thiese
51038         'APPLET', // 
51039         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
51040         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
51041         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
51042         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
51043         //'FONT' // CLEAN LATER..
51044         'COLGROUP', 'COL'   // messy tables.
51045         
51046         
51047 ];
51048 Roo.HtmlEditorCore.clean = [ // ?? needed???
51049      'SCRIPT', 'STYLE', 'TITLE', 'XML'
51050 ];
51051 Roo.HtmlEditorCore.tag_remove = [
51052     'FONT', 'TBODY'  
51053 ];
51054 // attributes..
51055
51056 Roo.HtmlEditorCore.ablack = [
51057     'on'
51058 ];
51059     
51060 Roo.HtmlEditorCore.aclean = [ 
51061     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
51062 ];
51063
51064 // protocols..
51065 Roo.HtmlEditorCore.pwhite= [
51066         'http',  'https',  'mailto'
51067 ];
51068
51069 // white listed style attributes.
51070 Roo.HtmlEditorCore.cwhite= [
51071       //  'text-align', /// default is to allow most things..
51072       
51073          
51074 //        'font-size'//??
51075 ];
51076
51077 // black listed style attributes.
51078 Roo.HtmlEditorCore.cblack= [
51079       //  'font-size' -- this can be set by the project 
51080 ];
51081
51082
51083
51084
51085     //<script type="text/javascript">
51086
51087 /*
51088  * Ext JS Library 1.1.1
51089  * Copyright(c) 2006-2007, Ext JS, LLC.
51090  * Licence LGPL
51091  * 
51092  */
51093  
51094  
51095 Roo.form.HtmlEditor = function(config){
51096     
51097     
51098     
51099     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
51100     
51101     if (!this.toolbars) {
51102         this.toolbars = [];
51103     }
51104     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
51105     
51106     
51107 };
51108
51109 /**
51110  * @class Roo.form.HtmlEditor
51111  * @extends Roo.form.Field
51112  * Provides a lightweight HTML Editor component.
51113  *
51114  * This has been tested on Fireforx / Chrome.. IE may not be so great..
51115  * 
51116  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
51117  * supported by this editor.</b><br/><br/>
51118  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
51119  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
51120  */
51121 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
51122     /**
51123      * @cfg {Boolean} clearUp
51124      */
51125     clearUp : true,
51126       /**
51127      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
51128      */
51129     toolbars : false,
51130    
51131      /**
51132      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
51133      *                        Roo.resizable.
51134      */
51135     resizable : false,
51136      /**
51137      * @cfg {Number} height (in pixels)
51138      */   
51139     height: 300,
51140    /**
51141      * @cfg {Number} width (in pixels)
51142      */   
51143     width: 500,
51144     
51145     /**
51146      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
51147      * 
51148      */
51149     stylesheets: false,
51150     
51151     
51152      /**
51153      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
51154      * 
51155      */
51156     cblack: false,
51157     /**
51158      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
51159      * 
51160      */
51161     cwhite: false,
51162     
51163      /**
51164      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
51165      * 
51166      */
51167     black: false,
51168     /**
51169      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
51170      * 
51171      */
51172     white: false,
51173     /**
51174      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
51175      */
51176     allowComments: false,
51177     /**
51178      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
51179      */
51180     enableBlocks : true,
51181     
51182     /**
51183      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
51184      *         if you are doing an email editor, this probably needs disabling, it's designed
51185      */
51186     autoClean: true,
51187     /**
51188      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
51189      */
51190     bodyCls : '',
51191     /**
51192      * @cfg {String} language default en - language of text (usefull for rtl languages)
51193      * 
51194      */
51195     language: 'en',
51196     
51197      
51198     // id of frame..
51199     frameId: false,
51200     
51201     // private properties
51202     validationEvent : false,
51203     deferHeight: true,
51204     initialized : false,
51205     activated : false,
51206     
51207     onFocus : Roo.emptyFn,
51208     iframePad:3,
51209     hideMode:'offsets',
51210     
51211     actionMode : 'container', // defaults to hiding it...
51212     
51213     defaultAutoCreate : { // modified by initCompnoent..
51214         tag: "textarea",
51215         style:"width:500px;height:300px;",
51216         autocomplete: "new-password"
51217     },
51218
51219     // private
51220     initComponent : function(){
51221         this.addEvents({
51222             /**
51223              * @event initialize
51224              * Fires when the editor is fully initialized (including the iframe)
51225              * @param {HtmlEditor} this
51226              */
51227             initialize: true,
51228             /**
51229              * @event activate
51230              * Fires when the editor is first receives the focus. Any insertion must wait
51231              * until after this event.
51232              * @param {HtmlEditor} this
51233              */
51234             activate: true,
51235              /**
51236              * @event beforesync
51237              * Fires before the textarea is updated with content from the editor iframe. Return false
51238              * to cancel the sync.
51239              * @param {HtmlEditor} this
51240              * @param {String} html
51241              */
51242             beforesync: true,
51243              /**
51244              * @event beforepush
51245              * Fires before the iframe editor is updated with content from the textarea. Return false
51246              * to cancel the push.
51247              * @param {HtmlEditor} this
51248              * @param {String} html
51249              */
51250             beforepush: true,
51251              /**
51252              * @event sync
51253              * Fires when the textarea is updated with content from the editor iframe.
51254              * @param {HtmlEditor} this
51255              * @param {String} html
51256              */
51257             sync: true,
51258              /**
51259              * @event push
51260              * Fires when the iframe editor is updated with content from the textarea.
51261              * @param {HtmlEditor} this
51262              * @param {String} html
51263              */
51264             push: true,
51265              /**
51266              * @event editmodechange
51267              * Fires when the editor switches edit modes
51268              * @param {HtmlEditor} this
51269              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
51270              */
51271             editmodechange: true,
51272             /**
51273              * @event editorevent
51274              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
51275              * @param {HtmlEditor} this
51276              */
51277             editorevent: true,
51278             /**
51279              * @event firstfocus
51280              * Fires when on first focus - needed by toolbars..
51281              * @param {HtmlEditor} this
51282              */
51283             firstfocus: true,
51284             /**
51285              * @event autosave
51286              * Auto save the htmlEditor value as a file into Events
51287              * @param {HtmlEditor} this
51288              */
51289             autosave: true,
51290             /**
51291              * @event savedpreview
51292              * preview the saved version of htmlEditor
51293              * @param {HtmlEditor} this
51294              */
51295             savedpreview: true,
51296             
51297             /**
51298             * @event stylesheetsclick
51299             * Fires when press the Sytlesheets button
51300             * @param {Roo.HtmlEditorCore} this
51301             */
51302             stylesheetsclick: true,
51303             /**
51304             * @event paste
51305             * Fires when press user pastes into the editor
51306             * @param {Roo.HtmlEditorCore} this
51307             */
51308             paste: true 
51309         });
51310         this.defaultAutoCreate =  {
51311             tag: "textarea",
51312             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
51313             autocomplete: "new-password"
51314         };
51315     },
51316
51317     /**
51318      * Protected method that will not generally be called directly. It
51319      * is called when the editor creates its toolbar. Override this method if you need to
51320      * add custom toolbar buttons.
51321      * @param {HtmlEditor} editor
51322      */
51323     createToolbar : function(editor){
51324         Roo.log("create toolbars");
51325         if (!editor.toolbars || !editor.toolbars.length) {
51326             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
51327         }
51328         
51329         for (var i =0 ; i < editor.toolbars.length;i++) {
51330             editor.toolbars[i] = Roo.factory(
51331                     typeof(editor.toolbars[i]) == 'string' ?
51332                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
51333                 Roo.form.HtmlEditor);
51334             editor.toolbars[i].init(editor);
51335         }
51336          
51337         
51338     },
51339     /**
51340      * get the Context selected node
51341      * @returns {DomElement|boolean} selected node if active or false if none
51342      * 
51343      */
51344     getSelectedNode : function()
51345     {
51346         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
51347             return false;
51348         }
51349         return this.toolbars[1].tb.selectedNode;
51350     
51351     },
51352     // private
51353     onRender : function(ct, position)
51354     {
51355         var _t = this;
51356         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
51357         
51358         this.wrap = this.el.wrap({
51359             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
51360         });
51361         
51362         this.editorcore.onRender(ct, position);
51363          
51364         if (this.resizable) {
51365             this.resizeEl = new Roo.Resizable(this.wrap, {
51366                 pinned : true,
51367                 wrap: true,
51368                 dynamic : true,
51369                 minHeight : this.height,
51370                 height: this.height,
51371                 handles : this.resizable,
51372                 width: this.width,
51373                 listeners : {
51374                     resize : function(r, w, h) {
51375                         _t.onResize(w,h); // -something
51376                     }
51377                 }
51378             });
51379             
51380         }
51381         this.createToolbar(this);
51382        
51383         
51384         if(!this.width){
51385             this.setSize(this.wrap.getSize());
51386         }
51387         if (this.resizeEl) {
51388             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
51389             // should trigger onReize..
51390         }
51391         
51392         this.keyNav = new Roo.KeyNav(this.el, {
51393             
51394             "tab" : function(e){
51395                 e.preventDefault();
51396                 
51397                 var value = this.getValue();
51398                 
51399                 var start = this.el.dom.selectionStart;
51400                 var end = this.el.dom.selectionEnd;
51401                 
51402                 if(!e.shiftKey){
51403                     
51404                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
51405                     this.el.dom.setSelectionRange(end + 1, end + 1);
51406                     return;
51407                 }
51408                 
51409                 var f = value.substring(0, start).split("\t");
51410                 
51411                 if(f.pop().length != 0){
51412                     return;
51413                 }
51414                 
51415                 this.setValue(f.join("\t") + value.substring(end));
51416                 this.el.dom.setSelectionRange(start - 1, start - 1);
51417                 
51418             },
51419             
51420             "home" : function(e){
51421                 e.preventDefault();
51422                 
51423                 var curr = this.el.dom.selectionStart;
51424                 var lines = this.getValue().split("\n");
51425                 
51426                 if(!lines.length){
51427                     return;
51428                 }
51429                 
51430                 if(e.ctrlKey){
51431                     this.el.dom.setSelectionRange(0, 0);
51432                     return;
51433                 }
51434                 
51435                 var pos = 0;
51436                 
51437                 for (var i = 0; i < lines.length;i++) {
51438                     pos += lines[i].length;
51439                     
51440                     if(i != 0){
51441                         pos += 1;
51442                     }
51443                     
51444                     if(pos < curr){
51445                         continue;
51446                     }
51447                     
51448                     pos -= lines[i].length;
51449                     
51450                     break;
51451                 }
51452                 
51453                 if(!e.shiftKey){
51454                     this.el.dom.setSelectionRange(pos, pos);
51455                     return;
51456                 }
51457                 
51458                 this.el.dom.selectionStart = pos;
51459                 this.el.dom.selectionEnd = curr;
51460             },
51461             
51462             "end" : function(e){
51463                 e.preventDefault();
51464                 
51465                 var curr = this.el.dom.selectionStart;
51466                 var lines = this.getValue().split("\n");
51467                 
51468                 if(!lines.length){
51469                     return;
51470                 }
51471                 
51472                 if(e.ctrlKey){
51473                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
51474                     return;
51475                 }
51476                 
51477                 var pos = 0;
51478                 
51479                 for (var i = 0; i < lines.length;i++) {
51480                     
51481                     pos += lines[i].length;
51482                     
51483                     if(i != 0){
51484                         pos += 1;
51485                     }
51486                     
51487                     if(pos < curr){
51488                         continue;
51489                     }
51490                     
51491                     break;
51492                 }
51493                 
51494                 if(!e.shiftKey){
51495                     this.el.dom.setSelectionRange(pos, pos);
51496                     return;
51497                 }
51498                 
51499                 this.el.dom.selectionStart = curr;
51500                 this.el.dom.selectionEnd = pos;
51501             },
51502
51503             scope : this,
51504
51505             doRelay : function(foo, bar, hname){
51506                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
51507             },
51508
51509             forceKeyDown: true
51510         });
51511         
51512 //        if(this.autosave && this.w){
51513 //            this.autoSaveFn = setInterval(this.autosave, 1000);
51514 //        }
51515     },
51516
51517     // private
51518     onResize : function(w, h)
51519     {
51520         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
51521         var ew = false;
51522         var eh = false;
51523         
51524         if(this.el ){
51525             if(typeof w == 'number'){
51526                 var aw = w - this.wrap.getFrameWidth('lr');
51527                 this.el.setWidth(this.adjustWidth('textarea', aw));
51528                 ew = aw;
51529             }
51530             if(typeof h == 'number'){
51531                 var tbh = 0;
51532                 for (var i =0; i < this.toolbars.length;i++) {
51533                     // fixme - ask toolbars for heights?
51534                     tbh += this.toolbars[i].tb.el.getHeight();
51535                     if (this.toolbars[i].footer) {
51536                         tbh += this.toolbars[i].footer.el.getHeight();
51537                     }
51538                 }
51539                 
51540                 
51541                 
51542                 
51543                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
51544                 ah -= 5; // knock a few pixes off for look..
51545 //                Roo.log(ah);
51546                 this.el.setHeight(this.adjustWidth('textarea', ah));
51547                 var eh = ah;
51548             }
51549         }
51550         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
51551         this.editorcore.onResize(ew,eh);
51552         
51553     },
51554
51555     /**
51556      * Toggles the editor between standard and source edit mode.
51557      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
51558      */
51559     toggleSourceEdit : function(sourceEditMode)
51560     {
51561         this.editorcore.toggleSourceEdit(sourceEditMode);
51562         
51563         if(this.editorcore.sourceEditMode){
51564             Roo.log('editor - showing textarea');
51565             
51566 //            Roo.log('in');
51567 //            Roo.log(this.syncValue());
51568             this.editorcore.syncValue();
51569             this.el.removeClass('x-hidden');
51570             this.el.dom.removeAttribute('tabIndex');
51571             this.el.focus();
51572             this.el.dom.scrollTop = 0;
51573             
51574             
51575             for (var i = 0; i < this.toolbars.length; i++) {
51576                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51577                     this.toolbars[i].tb.hide();
51578                     this.toolbars[i].footer.hide();
51579                 }
51580             }
51581             
51582         }else{
51583             Roo.log('editor - hiding textarea');
51584 //            Roo.log('out')
51585 //            Roo.log(this.pushValue()); 
51586             this.editorcore.pushValue();
51587             
51588             this.el.addClass('x-hidden');
51589             this.el.dom.setAttribute('tabIndex', -1);
51590             
51591             for (var i = 0; i < this.toolbars.length; i++) {
51592                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51593                     this.toolbars[i].tb.show();
51594                     this.toolbars[i].footer.show();
51595                 }
51596             }
51597             
51598             //this.deferFocus();
51599         }
51600         
51601         this.setSize(this.wrap.getSize());
51602         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
51603         
51604         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
51605     },
51606  
51607     // private (for BoxComponent)
51608     adjustSize : Roo.BoxComponent.prototype.adjustSize,
51609
51610     // private (for BoxComponent)
51611     getResizeEl : function(){
51612         return this.wrap;
51613     },
51614
51615     // private (for BoxComponent)
51616     getPositionEl : function(){
51617         return this.wrap;
51618     },
51619
51620     // private
51621     initEvents : function(){
51622         this.originalValue = this.getValue();
51623     },
51624
51625     /**
51626      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51627      * @method
51628      */
51629     markInvalid : Roo.emptyFn,
51630     /**
51631      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51632      * @method
51633      */
51634     clearInvalid : Roo.emptyFn,
51635
51636     setValue : function(v){
51637         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
51638         this.editorcore.pushValue();
51639     },
51640
51641     /**
51642      * update the language in the body - really done by core
51643      * @param {String} language - eg. en / ar / zh-CN etc..
51644      */
51645     updateLanguage : function(lang)
51646     {
51647         this.language = lang;
51648         this.editorcore.language = lang;
51649         this.editorcore.updateLanguage();
51650      
51651     },
51652     // private
51653     deferFocus : function(){
51654         this.focus.defer(10, this);
51655     },
51656
51657     // doc'ed in Field
51658     focus : function(){
51659         this.editorcore.focus();
51660         
51661     },
51662       
51663
51664     // private
51665     onDestroy : function(){
51666         
51667         
51668         
51669         if(this.rendered){
51670             
51671             for (var i =0; i < this.toolbars.length;i++) {
51672                 // fixme - ask toolbars for heights?
51673                 this.toolbars[i].onDestroy();
51674             }
51675             
51676             this.wrap.dom.innerHTML = '';
51677             this.wrap.remove();
51678         }
51679     },
51680
51681     // private
51682     onFirstFocus : function(){
51683         //Roo.log("onFirstFocus");
51684         this.editorcore.onFirstFocus();
51685          for (var i =0; i < this.toolbars.length;i++) {
51686             this.toolbars[i].onFirstFocus();
51687         }
51688         
51689     },
51690     
51691     // private
51692     syncValue : function()
51693     {
51694         this.editorcore.syncValue();
51695     },
51696     
51697     pushValue : function()
51698     {
51699         this.editorcore.pushValue();
51700     },
51701     
51702     setStylesheets : function(stylesheets)
51703     {
51704         this.editorcore.setStylesheets(stylesheets);
51705     },
51706     
51707     removeStylesheets : function()
51708     {
51709         this.editorcore.removeStylesheets();
51710     }
51711      
51712     
51713     // hide stuff that is not compatible
51714     /**
51715      * @event blur
51716      * @hide
51717      */
51718     /**
51719      * @event change
51720      * @hide
51721      */
51722     /**
51723      * @event focus
51724      * @hide
51725      */
51726     /**
51727      * @event specialkey
51728      * @hide
51729      */
51730     /**
51731      * @cfg {String} fieldClass @hide
51732      */
51733     /**
51734      * @cfg {String} focusClass @hide
51735      */
51736     /**
51737      * @cfg {String} autoCreate @hide
51738      */
51739     /**
51740      * @cfg {String} inputType @hide
51741      */
51742     /**
51743      * @cfg {String} invalidClass @hide
51744      */
51745     /**
51746      * @cfg {String} invalidText @hide
51747      */
51748     /**
51749      * @cfg {String} msgFx @hide
51750      */
51751     /**
51752      * @cfg {String} validateOnBlur @hide
51753      */
51754 });
51755  
51756     /*
51757  * Based on
51758  * Ext JS Library 1.1.1
51759  * Copyright(c) 2006-2007, Ext JS, LLC.
51760  *  
51761  
51762  */
51763
51764 /**
51765  * @class Roo.form.HtmlEditor.ToolbarStandard
51766  * Basic Toolbar
51767
51768  * Usage:
51769  *
51770  new Roo.form.HtmlEditor({
51771     ....
51772     toolbars : [
51773         new Roo.form.HtmlEditorToolbar1({
51774             disable : { fonts: 1 , format: 1, ..., ... , ...],
51775             btns : [ .... ]
51776         })
51777     }
51778      
51779  * 
51780  * @cfg {Object} disable List of elements to disable..
51781  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
51782  * 
51783  * 
51784  * NEEDS Extra CSS? 
51785  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
51786  */
51787  
51788 Roo.form.HtmlEditor.ToolbarStandard = function(config)
51789 {
51790     
51791     Roo.apply(this, config);
51792     
51793     // default disabled, based on 'good practice'..
51794     this.disable = this.disable || {};
51795     Roo.applyIf(this.disable, {
51796         fontSize : true,
51797         colors : true,
51798         specialElements : true
51799     });
51800     
51801     
51802     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
51803     // dont call parent... till later.
51804 }
51805
51806 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
51807     
51808     tb: false,
51809     
51810     rendered: false,
51811     
51812     editor : false,
51813     editorcore : false,
51814     /**
51815      * @cfg {Object} disable  List of toolbar elements to disable
51816          
51817      */
51818     disable : false,
51819     
51820     
51821      /**
51822      * @cfg {String} createLinkText The default text for the create link prompt
51823      */
51824     createLinkText : 'Please enter the URL for the link:',
51825     /**
51826      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
51827      */
51828     defaultLinkValue : 'http:/'+'/',
51829    
51830     
51831       /**
51832      * @cfg {Array} fontFamilies An array of available font families
51833      */
51834     fontFamilies : [
51835         'Arial',
51836         'Courier New',
51837         'Tahoma',
51838         'Times New Roman',
51839         'Verdana'
51840     ],
51841     
51842     specialChars : [
51843            "&#169;",
51844           "&#174;",     
51845           "&#8482;",    
51846           "&#163;" ,    
51847          // "&#8212;",    
51848           "&#8230;",    
51849           "&#247;" ,    
51850         //  "&#225;" ,     ?? a acute?
51851            "&#8364;"    , //Euro
51852        //   "&#8220;"    ,
51853         //  "&#8221;"    ,
51854         //  "&#8226;"    ,
51855           "&#176;"  //   , // degrees
51856
51857          // "&#233;"     , // e ecute
51858          // "&#250;"     , // u ecute?
51859     ],
51860     
51861     specialElements : [
51862         {
51863             text: "Insert Table",
51864             xtype: 'MenuItem',
51865             xns : Roo.Menu,
51866             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
51867                 
51868         },
51869         {    
51870             text: "Insert Image",
51871             xtype: 'MenuItem',
51872             xns : Roo.Menu,
51873             ihtml : '<img src="about:blank"/>'
51874             
51875         }
51876         
51877          
51878     ],
51879     
51880     
51881     inputElements : [ 
51882             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
51883             "input:submit", "input:button", "select", "textarea", "label" ],
51884     formats : [
51885         ["p"] ,  
51886         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
51887         ["pre"],[ "code"], 
51888         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
51889         ['div'],['span'],
51890         ['sup'],['sub']
51891     ],
51892     
51893     cleanStyles : [
51894         "font-size"
51895     ],
51896      /**
51897      * @cfg {String} defaultFont default font to use.
51898      */
51899     defaultFont: 'tahoma',
51900    
51901     fontSelect : false,
51902     
51903     
51904     formatCombo : false,
51905     
51906     init : function(editor)
51907     {
51908         this.editor = editor;
51909         this.editorcore = editor.editorcore ? editor.editorcore : editor;
51910         var editorcore = this.editorcore;
51911         
51912         var _t = this;
51913         
51914         var fid = editorcore.frameId;
51915         var etb = this;
51916         function btn(id, toggle, handler){
51917             var xid = fid + '-'+ id ;
51918             return {
51919                 id : xid,
51920                 cmd : id,
51921                 cls : 'x-btn-icon x-edit-'+id,
51922                 enableToggle:toggle !== false,
51923                 scope: _t, // was editor...
51924                 handler:handler||_t.relayBtnCmd,
51925                 clickEvent:'mousedown',
51926                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
51927                 tabIndex:-1
51928             };
51929         }
51930         
51931         
51932         
51933         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
51934         this.tb = tb;
51935          // stop form submits
51936         tb.el.on('click', function(e){
51937             e.preventDefault(); // what does this do?
51938         });
51939
51940         if(!this.disable.font) { // && !Roo.isSafari){
51941             /* why no safari for fonts 
51942             editor.fontSelect = tb.el.createChild({
51943                 tag:'select',
51944                 tabIndex: -1,
51945                 cls:'x-font-select',
51946                 html: this.createFontOptions()
51947             });
51948             
51949             editor.fontSelect.on('change', function(){
51950                 var font = editor.fontSelect.dom.value;
51951                 editor.relayCmd('fontname', font);
51952                 editor.deferFocus();
51953             }, editor);
51954             
51955             tb.add(
51956                 editor.fontSelect.dom,
51957                 '-'
51958             );
51959             */
51960             
51961         };
51962         if(!this.disable.formats){
51963             this.formatCombo = new Roo.form.ComboBox({
51964                 store: new Roo.data.SimpleStore({
51965                     id : 'tag',
51966                     fields: ['tag'],
51967                     data : this.formats // from states.js
51968                 }),
51969                 blockFocus : true,
51970                 name : '',
51971                 //autoCreate : {tag: "div",  size: "20"},
51972                 displayField:'tag',
51973                 typeAhead: false,
51974                 mode: 'local',
51975                 editable : false,
51976                 triggerAction: 'all',
51977                 emptyText:'Add tag',
51978                 selectOnFocus:true,
51979                 width:135,
51980                 listeners : {
51981                     'select': function(c, r, i) {
51982                         editorcore.insertTag(r.get('tag'));
51983                         editor.focus();
51984                     }
51985                 }
51986
51987             });
51988             tb.addField(this.formatCombo);
51989             
51990         }
51991         
51992         if(!this.disable.format){
51993             tb.add(
51994                 btn('bold'),
51995                 btn('italic'),
51996                 btn('underline'),
51997                 btn('strikethrough')
51998             );
51999         };
52000         if(!this.disable.fontSize){
52001             tb.add(
52002                 '-',
52003                 
52004                 
52005                 btn('increasefontsize', false, editorcore.adjustFont),
52006                 btn('decreasefontsize', false, editorcore.adjustFont)
52007             );
52008         };
52009         
52010         
52011         if(!this.disable.colors){
52012             tb.add(
52013                 '-', {
52014                     id:editorcore.frameId +'-forecolor',
52015                     cls:'x-btn-icon x-edit-forecolor',
52016                     clickEvent:'mousedown',
52017                     tooltip: this.buttonTips['forecolor'] || undefined,
52018                     tabIndex:-1,
52019                     menu : new Roo.menu.ColorMenu({
52020                         allowReselect: true,
52021                         focus: Roo.emptyFn,
52022                         value:'000000',
52023                         plain:true,
52024                         selectHandler: function(cp, color){
52025                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
52026                             editor.deferFocus();
52027                         },
52028                         scope: editorcore,
52029                         clickEvent:'mousedown'
52030                     })
52031                 }, {
52032                     id:editorcore.frameId +'backcolor',
52033                     cls:'x-btn-icon x-edit-backcolor',
52034                     clickEvent:'mousedown',
52035                     tooltip: this.buttonTips['backcolor'] || undefined,
52036                     tabIndex:-1,
52037                     menu : new Roo.menu.ColorMenu({
52038                         focus: Roo.emptyFn,
52039                         value:'FFFFFF',
52040                         plain:true,
52041                         allowReselect: true,
52042                         selectHandler: function(cp, color){
52043                             if(Roo.isGecko){
52044                                 editorcore.execCmd('useCSS', false);
52045                                 editorcore.execCmd('hilitecolor', color);
52046                                 editorcore.execCmd('useCSS', true);
52047                                 editor.deferFocus();
52048                             }else{
52049                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
52050                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
52051                                 editor.deferFocus();
52052                             }
52053                         },
52054                         scope:editorcore,
52055                         clickEvent:'mousedown'
52056                     })
52057                 }
52058             );
52059         };
52060         // now add all the items...
52061         
52062
52063         if(!this.disable.alignments){
52064             tb.add(
52065                 '-',
52066                 btn('justifyleft'),
52067                 btn('justifycenter'),
52068                 btn('justifyright')
52069             );
52070         };
52071
52072         //if(!Roo.isSafari){
52073             if(!this.disable.links){
52074                 tb.add(
52075                     '-',
52076                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
52077                 );
52078             };
52079
52080             if(!this.disable.lists){
52081                 tb.add(
52082                     '-',
52083                     btn('insertorderedlist'),
52084                     btn('insertunorderedlist')
52085                 );
52086             }
52087             if(!this.disable.sourceEdit){
52088                 tb.add(
52089                     '-',
52090                     btn('sourceedit', true, function(btn){
52091                         this.toggleSourceEdit(btn.pressed);
52092                     })
52093                 );
52094             }
52095         //}
52096         
52097         var smenu = { };
52098         // special menu.. - needs to be tidied up..
52099         if (!this.disable.special) {
52100             smenu = {
52101                 text: "&#169;",
52102                 cls: 'x-edit-none',
52103                 
52104                 menu : {
52105                     items : []
52106                 }
52107             };
52108             for (var i =0; i < this.specialChars.length; i++) {
52109                 smenu.menu.items.push({
52110                     
52111                     html: this.specialChars[i],
52112                     handler: function(a,b) {
52113                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
52114                         //editor.insertAtCursor(a.html);
52115                         
52116                     },
52117                     tabIndex:-1
52118                 });
52119             }
52120             
52121             
52122             tb.add(smenu);
52123             
52124             
52125         }
52126         
52127         var cmenu = { };
52128         if (!this.disable.cleanStyles) {
52129             cmenu = {
52130                 cls: 'x-btn-icon x-btn-clear',
52131                 
52132                 menu : {
52133                     items : []
52134                 }
52135             };
52136             for (var i =0; i < this.cleanStyles.length; i++) {
52137                 cmenu.menu.items.push({
52138                     actiontype : this.cleanStyles[i],
52139                     html: 'Remove ' + this.cleanStyles[i],
52140                     handler: function(a,b) {
52141 //                        Roo.log(a);
52142 //                        Roo.log(b);
52143                         var c = Roo.get(editorcore.doc.body);
52144                         c.select('[style]').each(function(s) {
52145                             s.dom.style.removeProperty(a.actiontype);
52146                         });
52147                         editorcore.syncValue();
52148                     },
52149                     tabIndex:-1
52150                 });
52151             }
52152             cmenu.menu.items.push({
52153                 actiontype : 'tablewidths',
52154                 html: 'Remove Table Widths',
52155                 handler: function(a,b) {
52156                     editorcore.cleanTableWidths();
52157                     editorcore.syncValue();
52158                 },
52159                 tabIndex:-1
52160             });
52161             cmenu.menu.items.push({
52162                 actiontype : 'word',
52163                 html: 'Remove MS Word Formating',
52164                 handler: function(a,b) {
52165                     editorcore.cleanWord();
52166                     editorcore.syncValue();
52167                 },
52168                 tabIndex:-1
52169             });
52170             
52171             cmenu.menu.items.push({
52172                 actiontype : 'all',
52173                 html: 'Remove All Styles',
52174                 handler: function(a,b) {
52175                     
52176                     var c = Roo.get(editorcore.doc.body);
52177                     c.select('[style]').each(function(s) {
52178                         s.dom.removeAttribute('style');
52179                     });
52180                     editorcore.syncValue();
52181                 },
52182                 tabIndex:-1
52183             });
52184             
52185             cmenu.menu.items.push({
52186                 actiontype : 'all',
52187                 html: 'Remove All CSS Classes',
52188                 handler: function(a,b) {
52189                     
52190                     var c = Roo.get(editorcore.doc.body);
52191                     c.select('[class]').each(function(s) {
52192                         s.dom.removeAttribute('class');
52193                     });
52194                     editorcore.cleanWord();
52195                     editorcore.syncValue();
52196                 },
52197                 tabIndex:-1
52198             });
52199             
52200              cmenu.menu.items.push({
52201                 actiontype : 'tidy',
52202                 html: 'Tidy HTML Source',
52203                 handler: function(a,b) {
52204                     new Roo.htmleditor.Tidy(editorcore.doc.body);
52205                     editorcore.syncValue();
52206                 },
52207                 tabIndex:-1
52208             });
52209             
52210             
52211             tb.add(cmenu);
52212         }
52213          
52214         if (!this.disable.specialElements) {
52215             var semenu = {
52216                 text: "Other;",
52217                 cls: 'x-edit-none',
52218                 menu : {
52219                     items : []
52220                 }
52221             };
52222             for (var i =0; i < this.specialElements.length; i++) {
52223                 semenu.menu.items.push(
52224                     Roo.apply({ 
52225                         handler: function(a,b) {
52226                             editor.insertAtCursor(this.ihtml);
52227                         }
52228                     }, this.specialElements[i])
52229                 );
52230                     
52231             }
52232             
52233             tb.add(semenu);
52234             
52235             
52236         }
52237          
52238         
52239         if (this.btns) {
52240             for(var i =0; i< this.btns.length;i++) {
52241                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
52242                 b.cls =  'x-edit-none';
52243                 
52244                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
52245                     b.cls += ' x-init-enable';
52246                 }
52247                 
52248                 b.scope = editorcore;
52249                 tb.add(b);
52250             }
52251         
52252         }
52253         
52254         
52255         
52256         // disable everything...
52257         
52258         this.tb.items.each(function(item){
52259             
52260            if(
52261                 item.id != editorcore.frameId+ '-sourceedit' && 
52262                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
52263             ){
52264                 
52265                 item.disable();
52266             }
52267         });
52268         this.rendered = true;
52269         
52270         // the all the btns;
52271         editor.on('editorevent', this.updateToolbar, this);
52272         // other toolbars need to implement this..
52273         //editor.on('editmodechange', this.updateToolbar, this);
52274     },
52275     
52276     
52277     relayBtnCmd : function(btn) {
52278         this.editorcore.relayCmd(btn.cmd);
52279     },
52280     // private used internally
52281     createLink : function(){
52282         //Roo.log("create link?");
52283         var ec = this.editorcore;
52284         var ar = ec.getAllAncestors();
52285         var n = false;
52286         for(var i = 0;i< ar.length;i++) {
52287             if (ar[i] && ar[i].nodeName == 'A') {
52288                 n = ar[i];
52289                 break;
52290             }
52291         }
52292         
52293         (function() {
52294             
52295             Roo.MessageBox.show({
52296                 title : "Add / Edit Link URL",
52297                 msg : "Enter the url for the link",
52298                 buttons: Roo.MessageBox.OKCANCEL,
52299                 fn: function(btn, url){
52300                     if (btn != 'ok') {
52301                         return;
52302                     }
52303                     if(url && url != 'http:/'+'/'){
52304                         if (n) {
52305                             n.setAttribute('href', url);
52306                         } else {
52307                             ec.relayCmd('createlink', url);
52308                         }
52309                     }
52310                 },
52311                 minWidth:250,
52312                 prompt:true,
52313                 //multiline: multiline,
52314                 modal : true,
52315                 value :  n  ? n.getAttribute('href') : '' 
52316             });
52317             
52318              
52319         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
52320         
52321     },
52322
52323     
52324     /**
52325      * Protected method that will not generally be called directly. It triggers
52326      * a toolbar update by reading the markup state of the current selection in the editor.
52327      */
52328     updateToolbar: function(){
52329
52330         if(!this.editorcore.activated){
52331             this.editor.onFirstFocus();
52332             return;
52333         }
52334
52335         var btns = this.tb.items.map, 
52336             doc = this.editorcore.doc,
52337             frameId = this.editorcore.frameId;
52338
52339         if(!this.disable.font && !Roo.isSafari){
52340             /*
52341             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
52342             if(name != this.fontSelect.dom.value){
52343                 this.fontSelect.dom.value = name;
52344             }
52345             */
52346         }
52347         if(!this.disable.format){
52348             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
52349             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
52350             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
52351             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
52352         }
52353         if(!this.disable.alignments){
52354             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
52355             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
52356             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
52357         }
52358         if(!Roo.isSafari && !this.disable.lists){
52359             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
52360             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
52361         }
52362         
52363         var ans = this.editorcore.getAllAncestors();
52364         if (this.formatCombo) {
52365             
52366             
52367             var store = this.formatCombo.store;
52368             this.formatCombo.setValue("");
52369             for (var i =0; i < ans.length;i++) {
52370                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
52371                     // select it..
52372                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
52373                     break;
52374                 }
52375             }
52376         }
52377         
52378         
52379         
52380         // hides menus... - so this cant be on a menu...
52381         Roo.menu.MenuMgr.hideAll();
52382
52383         //this.editorsyncValue();
52384     },
52385    
52386     
52387     createFontOptions : function(){
52388         var buf = [], fs = this.fontFamilies, ff, lc;
52389         
52390         
52391         
52392         for(var i = 0, len = fs.length; i< len; i++){
52393             ff = fs[i];
52394             lc = ff.toLowerCase();
52395             buf.push(
52396                 '<option value="',lc,'" style="font-family:',ff,';"',
52397                     (this.defaultFont == lc ? ' selected="true">' : '>'),
52398                     ff,
52399                 '</option>'
52400             );
52401         }
52402         return buf.join('');
52403     },
52404     
52405     toggleSourceEdit : function(sourceEditMode){
52406         
52407         Roo.log("toolbar toogle");
52408         if(sourceEditMode === undefined){
52409             sourceEditMode = !this.sourceEditMode;
52410         }
52411         this.sourceEditMode = sourceEditMode === true;
52412         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
52413         // just toggle the button?
52414         if(btn.pressed !== this.sourceEditMode){
52415             btn.toggle(this.sourceEditMode);
52416             return;
52417         }
52418         
52419         if(sourceEditMode){
52420             Roo.log("disabling buttons");
52421             this.tb.items.each(function(item){
52422                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
52423                     item.disable();
52424                 }
52425             });
52426           
52427         }else{
52428             Roo.log("enabling buttons");
52429             if(this.editorcore.initialized){
52430                 this.tb.items.each(function(item){
52431                     item.enable();
52432                 });
52433                 // initialize 'blocks'
52434                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
52435                     Roo.htmleditor.Block.factory(e).updateElement(e);
52436                 },this);
52437             
52438             }
52439             
52440         }
52441         Roo.log("calling toggole on editor");
52442         // tell the editor that it's been pressed..
52443         this.editor.toggleSourceEdit(sourceEditMode);
52444        
52445     },
52446      /**
52447      * Object collection of toolbar tooltips for the buttons in the editor. The key
52448      * is the command id associated with that button and the value is a valid QuickTips object.
52449      * For example:
52450 <pre><code>
52451 {
52452     bold : {
52453         title: 'Bold (Ctrl+B)',
52454         text: 'Make the selected text bold.',
52455         cls: 'x-html-editor-tip'
52456     },
52457     italic : {
52458         title: 'Italic (Ctrl+I)',
52459         text: 'Make the selected text italic.',
52460         cls: 'x-html-editor-tip'
52461     },
52462     ...
52463 </code></pre>
52464     * @type Object
52465      */
52466     buttonTips : {
52467         bold : {
52468             title: 'Bold (Ctrl+B)',
52469             text: 'Make the selected text bold.',
52470             cls: 'x-html-editor-tip'
52471         },
52472         italic : {
52473             title: 'Italic (Ctrl+I)',
52474             text: 'Make the selected text italic.',
52475             cls: 'x-html-editor-tip'
52476         },
52477         underline : {
52478             title: 'Underline (Ctrl+U)',
52479             text: 'Underline the selected text.',
52480             cls: 'x-html-editor-tip'
52481         },
52482         strikethrough : {
52483             title: 'Strikethrough',
52484             text: 'Strikethrough the selected text.',
52485             cls: 'x-html-editor-tip'
52486         },
52487         increasefontsize : {
52488             title: 'Grow Text',
52489             text: 'Increase the font size.',
52490             cls: 'x-html-editor-tip'
52491         },
52492         decreasefontsize : {
52493             title: 'Shrink Text',
52494             text: 'Decrease the font size.',
52495             cls: 'x-html-editor-tip'
52496         },
52497         backcolor : {
52498             title: 'Text Highlight Color',
52499             text: 'Change the background color of the selected text.',
52500             cls: 'x-html-editor-tip'
52501         },
52502         forecolor : {
52503             title: 'Font Color',
52504             text: 'Change the color of the selected text.',
52505             cls: 'x-html-editor-tip'
52506         },
52507         justifyleft : {
52508             title: 'Align Text Left',
52509             text: 'Align text to the left.',
52510             cls: 'x-html-editor-tip'
52511         },
52512         justifycenter : {
52513             title: 'Center Text',
52514             text: 'Center text in the editor.',
52515             cls: 'x-html-editor-tip'
52516         },
52517         justifyright : {
52518             title: 'Align Text Right',
52519             text: 'Align text to the right.',
52520             cls: 'x-html-editor-tip'
52521         },
52522         insertunorderedlist : {
52523             title: 'Bullet List',
52524             text: 'Start a bulleted list.',
52525             cls: 'x-html-editor-tip'
52526         },
52527         insertorderedlist : {
52528             title: 'Numbered List',
52529             text: 'Start a numbered list.',
52530             cls: 'x-html-editor-tip'
52531         },
52532         createlink : {
52533             title: 'Hyperlink',
52534             text: 'Make the selected text a hyperlink.',
52535             cls: 'x-html-editor-tip'
52536         },
52537         sourceedit : {
52538             title: 'Source Edit',
52539             text: 'Switch to source editing mode.',
52540             cls: 'x-html-editor-tip'
52541         }
52542     },
52543     // private
52544     onDestroy : function(){
52545         if(this.rendered){
52546             
52547             this.tb.items.each(function(item){
52548                 if(item.menu){
52549                     item.menu.removeAll();
52550                     if(item.menu.el){
52551                         item.menu.el.destroy();
52552                     }
52553                 }
52554                 item.destroy();
52555             });
52556              
52557         }
52558     },
52559     onFirstFocus: function() {
52560         this.tb.items.each(function(item){
52561            item.enable();
52562         });
52563     }
52564 };
52565
52566
52567
52568
52569 // <script type="text/javascript">
52570 /*
52571  * Based on
52572  * Ext JS Library 1.1.1
52573  * Copyright(c) 2006-2007, Ext JS, LLC.
52574  *  
52575  
52576  */
52577
52578  
52579 /**
52580  * @class Roo.form.HtmlEditor.ToolbarContext
52581  * Context Toolbar
52582  * 
52583  * Usage:
52584  *
52585  new Roo.form.HtmlEditor({
52586     ....
52587     toolbars : [
52588         { xtype: 'ToolbarStandard', styles : {} }
52589         { xtype: 'ToolbarContext', disable : {} }
52590     ]
52591 })
52592
52593      
52594  * 
52595  * @config : {Object} disable List of elements to disable.. (not done yet.)
52596  * @config : {Object} styles  Map of styles available.
52597  * 
52598  */
52599
52600 Roo.form.HtmlEditor.ToolbarContext = function(config)
52601 {
52602     
52603     Roo.apply(this, config);
52604     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52605     // dont call parent... till later.
52606     this.styles = this.styles || {};
52607 }
52608
52609  
52610
52611 Roo.form.HtmlEditor.ToolbarContext.types = {
52612     'IMG' : [
52613         {
52614             name : 'width',
52615             title: "Width",
52616             width: 40
52617         },
52618         {
52619             name : 'height',
52620             title: "Height",
52621             width: 40
52622         },
52623         {
52624             name : 'align',
52625             title: "Align",
52626             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52627             width : 80
52628             
52629         },
52630         {
52631             name : 'border',
52632             title: "Border",
52633             width: 40
52634         },
52635         {
52636             name : 'alt',
52637             title: "Alt",
52638             width: 120
52639         },
52640         {
52641             name : 'src',
52642             title: "Src",
52643             width: 220
52644         }
52645         
52646     ],
52647     
52648     'FIGURE' : [
52649         {
52650             name : 'align',
52651             title: "Align",
52652             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52653             width : 80  
52654         }
52655     ],
52656     'A' : [
52657         {
52658             name : 'name',
52659             title: "Name",
52660             width: 50
52661         },
52662         {
52663             name : 'target',
52664             title: "Target",
52665             width: 120
52666         },
52667         {
52668             name : 'href',
52669             title: "Href",
52670             width: 220
52671         } // border?
52672         
52673     ],
52674     
52675     'INPUT' : [
52676         {
52677             name : 'name',
52678             title: "name",
52679             width: 120
52680         },
52681         {
52682             name : 'value',
52683             title: "Value",
52684             width: 120
52685         },
52686         {
52687             name : 'width',
52688             title: "Width",
52689             width: 40
52690         }
52691     ],
52692     'LABEL' : [
52693          {
52694             name : 'for',
52695             title: "For",
52696             width: 120
52697         }
52698     ],
52699     'TEXTAREA' : [
52700         {
52701             name : 'name',
52702             title: "name",
52703             width: 120
52704         },
52705         {
52706             name : 'rows',
52707             title: "Rows",
52708             width: 20
52709         },
52710         {
52711             name : 'cols',
52712             title: "Cols",
52713             width: 20
52714         }
52715     ],
52716     'SELECT' : [
52717         {
52718             name : 'name',
52719             title: "name",
52720             width: 120
52721         },
52722         {
52723             name : 'selectoptions',
52724             title: "Options",
52725             width: 200
52726         }
52727     ],
52728     
52729     // should we really allow this??
52730     // should this just be 
52731     'BODY' : [
52732         
52733         {
52734             name : 'title',
52735             title: "Title",
52736             width: 200,
52737             disabled : true
52738         }
52739     ],
52740  
52741     '*' : [
52742         // empty.
52743     ]
52744
52745 };
52746
52747 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
52748 Roo.form.HtmlEditor.ToolbarContext.stores = false;
52749
52750 Roo.form.HtmlEditor.ToolbarContext.options = {
52751         'font-family'  : [ 
52752                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
52753                 [ 'Courier New', 'Courier New'],
52754                 [ 'Tahoma', 'Tahoma'],
52755                 [ 'Times New Roman,serif', 'Times'],
52756                 [ 'Verdana','Verdana' ]
52757         ]
52758 };
52759
52760 // fixme - these need to be configurable..
52761  
52762
52763 //Roo.form.HtmlEditor.ToolbarContext.types
52764
52765
52766 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
52767     
52768     tb: false,
52769     
52770     rendered: false,
52771     
52772     editor : false,
52773     editorcore : false,
52774     /**
52775      * @cfg {Object} disable  List of toolbar elements to disable
52776          
52777      */
52778     disable : false,
52779     /**
52780      * @cfg {Object} styles List of styles 
52781      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
52782      *
52783      * These must be defined in the page, so they get rendered correctly..
52784      * .headline { }
52785      * TD.underline { }
52786      * 
52787      */
52788     styles : false,
52789     
52790     options: false,
52791     
52792     toolbars : false,
52793     
52794     init : function(editor)
52795     {
52796         this.editor = editor;
52797         this.editorcore = editor.editorcore ? editor.editorcore : editor;
52798         var editorcore = this.editorcore;
52799         
52800         var fid = editorcore.frameId;
52801         var etb = this;
52802         function btn(id, toggle, handler){
52803             var xid = fid + '-'+ id ;
52804             return {
52805                 id : xid,
52806                 cmd : id,
52807                 cls : 'x-btn-icon x-edit-'+id,
52808                 enableToggle:toggle !== false,
52809                 scope: editorcore, // was editor...
52810                 handler:handler||editorcore.relayBtnCmd,
52811                 clickEvent:'mousedown',
52812                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52813                 tabIndex:-1
52814             };
52815         }
52816         // create a new element.
52817         var wdiv = editor.wrap.createChild({
52818                 tag: 'div'
52819             }, editor.wrap.dom.firstChild.nextSibling, true);
52820         
52821         // can we do this more than once??
52822         
52823          // stop form submits
52824       
52825  
52826         // disable everything...
52827         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
52828         this.toolbars = {};
52829         // block toolbars are built in updateToolbar when needed.
52830         for (var i in  ty) {
52831             
52832             this.toolbars[i] = this.buildToolbar(ty[i],i);
52833         }
52834         this.tb = this.toolbars.BODY;
52835         this.tb.el.show();
52836         this.buildFooter();
52837         this.footer.show();
52838         editor.on('hide', function( ) { this.footer.hide() }, this);
52839         editor.on('show', function( ) { this.footer.show() }, this);
52840         
52841          
52842         this.rendered = true;
52843         
52844         // the all the btns;
52845         editor.on('editorevent', this.updateToolbar, this);
52846         // other toolbars need to implement this..
52847         //editor.on('editmodechange', this.updateToolbar, this);
52848     },
52849     
52850     
52851     
52852     /**
52853      * Protected method that will not generally be called directly. It triggers
52854      * a toolbar update by reading the markup state of the current selection in the editor.
52855      *
52856      * Note you can force an update by calling on('editorevent', scope, false)
52857      */
52858     updateToolbar: function(editor ,ev, sel)
52859     {
52860         
52861         if (ev) {
52862             ev.stopEvent(); // se if we can stop this looping with mutiple events.
52863         }
52864         
52865         //Roo.log(ev);
52866         // capture mouse up - this is handy for selecting images..
52867         // perhaps should go somewhere else...
52868         if(!this.editorcore.activated){
52869              this.editor.onFirstFocus();
52870             return;
52871         }
52872         //Roo.log(ev ? ev.target : 'NOTARGET');
52873         
52874         
52875         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
52876         // selectNode - might want to handle IE?
52877         
52878         
52879         
52880         if (ev &&
52881             (ev.type == 'mouseup' || ev.type == 'click' ) &&
52882             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
52883             // they have click on an image...
52884             // let's see if we can change the selection...
52885             sel = ev.target;
52886             
52887             // this triggers looping?
52888             //this.editorcore.selectNode(sel);
52889              
52890         }
52891         
52892         // this forces an id..
52893         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
52894              e.classList.remove('roo-ed-selection');
52895         });
52896         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
52897         //Roo.get(node).addClass('roo-ed-selection');
52898       
52899         //var updateFooter = sel ? false : true; 
52900         
52901         
52902         var ans = this.editorcore.getAllAncestors();
52903         
52904         // pick
52905         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
52906         
52907         if (!sel) { 
52908             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
52909             sel = sel ? sel : this.editorcore.doc.body;
52910             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
52911             
52912         }
52913         
52914         var tn = sel.tagName.toUpperCase();
52915         var lastSel = this.tb.selectedNode;
52916         this.tb.selectedNode = sel;
52917         var left_label = tn;
52918         
52919         // ok see if we are editing a block?
52920         
52921         var db = false;
52922         // you are not actually selecting the block.
52923         if (sel && sel.hasAttribute('data-block')) {
52924             db = sel;
52925         } else if (sel && sel.closest('[data-block]')) {
52926             
52927             db = sel.closest('[data-block]');
52928             //var cepar = sel.closest('[contenteditable=true]');
52929             //if (db && cepar && cepar.tagName != 'BODY') {
52930             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
52931             //}   
52932         }
52933         
52934         
52935         var block = false;
52936         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
52937         if (db && this.editorcore.enableBlocks) {
52938             block = Roo.htmleditor.Block.factory(db);
52939             
52940             
52941             if (block) {
52942                  db.className = (
52943                         db.classList.length > 0  ? db.className + ' ' : ''
52944                     )  + 'roo-ed-selection';
52945                  
52946                  // since we removed it earlier... its not there..
52947                 tn = 'BLOCK.' + db.getAttribute('data-block');
52948                 
52949                 //this.editorcore.selectNode(db);
52950                 if (typeof(this.toolbars[tn]) == 'undefined') {
52951                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
52952                 }
52953                 this.toolbars[tn].selectedNode = db;
52954                 left_label = block.friendly_name;
52955                 ans = this.editorcore.getAllAncestors();
52956             }
52957             
52958                 
52959             
52960         }
52961         
52962         
52963         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
52964             return; // no change?
52965         }
52966         
52967         
52968           
52969         this.tb.el.hide();
52970         ///console.log("show: " + tn);
52971         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
52972         
52973         this.tb.el.show();
52974         // update name
52975         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
52976         
52977         
52978         // update attributes
52979         if (block && this.tb.fields) {
52980              
52981             this.tb.fields.each(function(e) {
52982                 e.setValue(block[e.name]);
52983             });
52984             
52985             
52986         } else  if (this.tb.fields && this.tb.selectedNode) {
52987             this.tb.fields.each( function(e) {
52988                 if (e.stylename) {
52989                     e.setValue(this.tb.selectedNode.style[e.stylename]);
52990                     return;
52991                 } 
52992                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
52993             }, this);
52994             this.updateToolbarStyles(this.tb.selectedNode);  
52995         }
52996         
52997         
52998        
52999         Roo.menu.MenuMgr.hideAll();
53000
53001         
53002         
53003     
53004         // update the footer
53005         //
53006         this.updateFooter(ans);
53007              
53008     },
53009     
53010     updateToolbarStyles : function(sel)
53011     {
53012         var hasStyles = false;
53013         for(var i in this.styles) {
53014             hasStyles = true;
53015             break;
53016         }
53017         
53018         // update styles
53019         if (hasStyles && this.tb.hasStyles) { 
53020             var st = this.tb.fields.item(0);
53021             
53022             st.store.removeAll();
53023             var cn = sel.className.split(/\s+/);
53024             
53025             var avs = [];
53026             if (this.styles['*']) {
53027                 
53028                 Roo.each(this.styles['*'], function(v) {
53029                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53030                 });
53031             }
53032             if (this.styles[tn]) { 
53033                 Roo.each(this.styles[tn], function(v) {
53034                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53035                 });
53036             }
53037             
53038             st.store.loadData(avs);
53039             st.collapse();
53040             st.setValue(cn);
53041         }
53042     },
53043     
53044      
53045     updateFooter : function(ans)
53046     {
53047         var html = '';
53048         if (ans === false) {
53049             this.footDisp.dom.innerHTML = '';
53050             return;
53051         }
53052         
53053         this.footerEls = ans.reverse();
53054         Roo.each(this.footerEls, function(a,i) {
53055             if (!a) { return; }
53056             html += html.length ? ' &gt; '  :  '';
53057             
53058             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
53059             
53060         });
53061        
53062         // 
53063         var sz = this.footDisp.up('td').getSize();
53064         this.footDisp.dom.style.width = (sz.width -10) + 'px';
53065         this.footDisp.dom.style.marginLeft = '5px';
53066         
53067         this.footDisp.dom.style.overflow = 'hidden';
53068         
53069         this.footDisp.dom.innerHTML = html;
53070             
53071         
53072     },
53073    
53074        
53075     // private
53076     onDestroy : function(){
53077         if(this.rendered){
53078             
53079             this.tb.items.each(function(item){
53080                 if(item.menu){
53081                     item.menu.removeAll();
53082                     if(item.menu.el){
53083                         item.menu.el.destroy();
53084                     }
53085                 }
53086                 item.destroy();
53087             });
53088              
53089         }
53090     },
53091     onFirstFocus: function() {
53092         // need to do this for all the toolbars..
53093         this.tb.items.each(function(item){
53094            item.enable();
53095         });
53096     },
53097     buildToolbar: function(tlist, nm, friendly_name, block)
53098     {
53099         var editor = this.editor;
53100         var editorcore = this.editorcore;
53101          // create a new element.
53102         var wdiv = editor.wrap.createChild({
53103                 tag: 'div'
53104             }, editor.wrap.dom.firstChild.nextSibling, true);
53105         
53106        
53107         var tb = new Roo.Toolbar(wdiv);
53108         ///this.tb = tb; // << this sets the active toolbar..
53109         if (tlist === false && block) {
53110             tlist = block.contextMenu(this);
53111         }
53112         
53113         tb.hasStyles = false;
53114         tb.name = nm;
53115         
53116         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
53117         
53118         var styles = Array.from(this.styles);
53119         
53120         
53121         // styles...
53122         if (styles && styles.length) {
53123             tb.hasStyles = true;
53124             // this needs a multi-select checkbox...
53125             tb.addField( new Roo.form.ComboBox({
53126                 store: new Roo.data.SimpleStore({
53127                     id : 'val',
53128                     fields: ['val', 'selected'],
53129                     data : [] 
53130                 }),
53131                 name : '-roo-edit-className',
53132                 attrname : 'className',
53133                 displayField: 'val',
53134                 typeAhead: false,
53135                 mode: 'local',
53136                 editable : false,
53137                 triggerAction: 'all',
53138                 emptyText:'Select Style',
53139                 selectOnFocus:true,
53140                 width: 130,
53141                 listeners : {
53142                     'select': function(c, r, i) {
53143                         // initial support only for on class per el..
53144                         tb.selectedNode.className =  r ? r.get('val') : '';
53145                         editorcore.syncValue();
53146                     }
53147                 }
53148     
53149             }));
53150         }
53151         
53152         var tbc = Roo.form.HtmlEditor.ToolbarContext;
53153         
53154         
53155         for (var i = 0; i < tlist.length; i++) {
53156             
53157             // newer versions will use xtype cfg to create menus.
53158             if (typeof(tlist[i].xtype) != 'undefined') {
53159                 
53160                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
53161                 
53162                 
53163                 continue;
53164             }
53165             
53166             var item = tlist[i];
53167             tb.add(item.title + ":&nbsp;");
53168             
53169             
53170             //optname == used so you can configure the options available..
53171             var opts = item.opts ? item.opts : false;
53172             if (item.optname) { // use the b
53173                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
53174            
53175             }
53176             
53177             if (opts) {
53178                 // opts == pulldown..
53179                 tb.addField( new Roo.form.ComboBox({
53180                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
53181                         id : 'val',
53182                         fields: ['val', 'display'],
53183                         data : opts  
53184                     }),
53185                     name : '-roo-edit-' + tlist[i].name,
53186                     
53187                     attrname : tlist[i].name,
53188                     stylename : item.style ? item.style : false,
53189                     
53190                     displayField: item.displayField ? item.displayField : 'val',
53191                     valueField :  'val',
53192                     typeAhead: false,
53193                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
53194                     editable : false,
53195                     triggerAction: 'all',
53196                     emptyText:'Select',
53197                     selectOnFocus:true,
53198                     width: item.width ? item.width  : 130,
53199                     listeners : {
53200                         'select': function(c, r, i) {
53201                              
53202                             
53203                             if (c.stylename) {
53204                                 tb.selectedNode.style[c.stylename] =  r.get('val');
53205                                 editorcore.syncValue();
53206                                 return;
53207                             }
53208                             if (r === false) {
53209                                 tb.selectedNode.removeAttribute(c.attrname);
53210                                 editorcore.syncValue();
53211                                 return;
53212                             }
53213                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
53214                             editorcore.syncValue();
53215                         }
53216                     }
53217
53218                 }));
53219                 continue;
53220                     
53221                  
53222                 /*
53223                 tb.addField( new Roo.form.TextField({
53224                     name: i,
53225                     width: 100,
53226                     //allowBlank:false,
53227                     value: ''
53228                 }));
53229                 continue;
53230                 */
53231             }
53232             tb.addField( new Roo.form.TextField({
53233                 name: '-roo-edit-' + tlist[i].name,
53234                 attrname : tlist[i].name,
53235                 
53236                 width: item.width,
53237                 //allowBlank:true,
53238                 value: '',
53239                 listeners: {
53240                     'change' : function(f, nv, ov) {
53241                         
53242                          
53243                         tb.selectedNode.setAttribute(f.attrname, nv);
53244                         editorcore.syncValue();
53245                     }
53246                 }
53247             }));
53248              
53249         }
53250         
53251         var _this = this;
53252         var show_delete = !block || block.deleteTitle !== false;
53253         if(nm == 'BODY'){
53254             show_delete = false;
53255             tb.addSeparator();
53256         
53257             tb.addButton( {
53258                 text: 'Stylesheets',
53259
53260                 listeners : {
53261                     click : function ()
53262                     {
53263                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
53264                     }
53265                 }
53266             });
53267         }
53268         
53269         tb.addFill();
53270         if (show_delete) {
53271             tb.addButton({
53272                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
53273         
53274                 listeners : {
53275                     click : function ()
53276                     {
53277                         var sn = tb.selectedNode;
53278                         if (block) {
53279                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
53280                             
53281                         }
53282                         if (!sn) {
53283                             return;
53284                         }
53285                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
53286                         if (sn.hasAttribute('data-block')) {
53287                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
53288                             sn.parentNode.removeChild(sn);
53289                             
53290                         } else if (sn && sn.tagName != 'BODY') {
53291                             // remove and keep parents.
53292                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
53293                             a.replaceTag(sn);
53294                         }
53295                         
53296                         
53297                         var range = editorcore.createRange();
53298             
53299                         range.setStart(stn,0);
53300                         range.setEnd(stn,0); 
53301                         var selection = editorcore.getSelection();
53302                         selection.removeAllRanges();
53303                         selection.addRange(range);
53304                         
53305                         
53306                         //_this.updateToolbar(null, null, pn);
53307                         _this.updateToolbar(null, null, null);
53308                         _this.updateFooter(false);
53309                         
53310                     }
53311                 }
53312                 
53313                         
53314                     
53315                 
53316             });
53317         }    
53318         
53319         tb.el.on('click', function(e){
53320             e.preventDefault(); // what does this do?
53321         });
53322         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
53323         tb.el.hide();
53324         
53325         // dont need to disable them... as they will get hidden
53326         return tb;
53327          
53328         
53329     },
53330     buildFooter : function()
53331     {
53332         
53333         var fel = this.editor.wrap.createChild();
53334         this.footer = new Roo.Toolbar(fel);
53335         // toolbar has scrolly on left / right?
53336         var footDisp= new Roo.Toolbar.Fill();
53337         var _t = this;
53338         this.footer.add(
53339             {
53340                 text : '&lt;',
53341                 xtype: 'Button',
53342                 handler : function() {
53343                     _t.footDisp.scrollTo('left',0,true)
53344                 }
53345             }
53346         );
53347         this.footer.add( footDisp );
53348         this.footer.add( 
53349             {
53350                 text : '&gt;',
53351                 xtype: 'Button',
53352                 handler : function() {
53353                     // no animation..
53354                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
53355                 }
53356             }
53357         );
53358         var fel = Roo.get(footDisp.el);
53359         fel.addClass('x-editor-context');
53360         this.footDispWrap = fel; 
53361         this.footDispWrap.overflow  = 'hidden';
53362         
53363         this.footDisp = fel.createChild();
53364         this.footDispWrap.on('click', this.onContextClick, this)
53365         
53366         
53367     },
53368     // when the footer contect changes
53369     onContextClick : function (ev,dom)
53370     {
53371         ev.preventDefault();
53372         var  cn = dom.className;
53373         //Roo.log(cn);
53374         if (!cn.match(/x-ed-loc-/)) {
53375             return;
53376         }
53377         var n = cn.split('-').pop();
53378         var ans = this.footerEls;
53379         var sel = ans[n];
53380         
53381         this.editorcore.selectNode(sel);
53382         
53383         
53384         this.updateToolbar(null, null, sel);
53385         
53386         
53387     }
53388     
53389     
53390     
53391     
53392     
53393 });
53394
53395
53396
53397
53398
53399 /*
53400  * Based on:
53401  * Ext JS Library 1.1.1
53402  * Copyright(c) 2006-2007, Ext JS, LLC.
53403  *
53404  * Originally Released Under LGPL - original licence link has changed is not relivant.
53405  *
53406  * Fork - LGPL
53407  * <script type="text/javascript">
53408  */
53409  
53410 /**
53411  * @class Roo.form.BasicForm
53412  * @extends Roo.util.Observable
53413  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
53414  * @constructor
53415  * @param {String/HTMLElement/Roo.Element} el The form element or its id
53416  * @param {Object} config Configuration options
53417  */
53418 Roo.form.BasicForm = function(el, config){
53419     this.allItems = [];
53420     this.childForms = [];
53421     Roo.apply(this, config);
53422     /*
53423      * The Roo.form.Field items in this form.
53424      * @type MixedCollection
53425      */
53426      
53427      
53428     this.items = new Roo.util.MixedCollection(false, function(o){
53429         return o.id || (o.id = Roo.id());
53430     });
53431     this.addEvents({
53432         /**
53433          * @event beforeaction
53434          * Fires before any action is performed. Return false to cancel the action.
53435          * @param {Form} this
53436          * @param {Action} action The action to be performed
53437          */
53438         beforeaction: true,
53439         /**
53440          * @event actionfailed
53441          * Fires when an action fails.
53442          * @param {Form} this
53443          * @param {Action} action The action that failed
53444          */
53445         actionfailed : true,
53446         /**
53447          * @event actioncomplete
53448          * Fires when an action is completed.
53449          * @param {Form} this
53450          * @param {Action} action The action that completed
53451          */
53452         actioncomplete : true
53453     });
53454     if(el){
53455         this.initEl(el);
53456     }
53457     Roo.form.BasicForm.superclass.constructor.call(this);
53458     
53459     Roo.form.BasicForm.popover.apply();
53460 };
53461
53462 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
53463     /**
53464      * @cfg {String} method
53465      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
53466      */
53467     /**
53468      * @cfg {DataReader} reader
53469      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
53470      * This is optional as there is built-in support for processing JSON.
53471      */
53472     /**
53473      * @cfg {DataReader} errorReader
53474      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
53475      * This is completely optional as there is built-in support for processing JSON.
53476      */
53477     /**
53478      * @cfg {String} url
53479      * The URL to use for form actions if one isn't supplied in the action options.
53480      */
53481     /**
53482      * @cfg {Boolean} fileUpload
53483      * Set to true if this form is a file upload.
53484      */
53485      
53486     /**
53487      * @cfg {Object} baseParams
53488      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
53489      */
53490      /**
53491      
53492     /**
53493      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
53494      */
53495     timeout: 30,
53496
53497     // private
53498     activeAction : null,
53499
53500     /**
53501      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
53502      * or setValues() data instead of when the form was first created.
53503      */
53504     trackResetOnLoad : false,
53505     
53506     
53507     /**
53508      * childForms - used for multi-tab forms
53509      * @type {Array}
53510      */
53511     childForms : false,
53512     
53513     /**
53514      * allItems - full list of fields.
53515      * @type {Array}
53516      */
53517     allItems : false,
53518     
53519     /**
53520      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
53521      * element by passing it or its id or mask the form itself by passing in true.
53522      * @type Mixed
53523      */
53524     waitMsgTarget : false,
53525     
53526     /**
53527      * @type Boolean
53528      */
53529     disableMask : false,
53530     
53531     /**
53532      * @cfg {Boolean} errorMask (true|false) default false
53533      */
53534     errorMask : false,
53535     
53536     /**
53537      * @cfg {Number} maskOffset Default 100
53538      */
53539     maskOffset : 100,
53540
53541     // private
53542     initEl : function(el){
53543         this.el = Roo.get(el);
53544         this.id = this.el.id || Roo.id();
53545         this.el.on('submit', this.onSubmit, this);
53546         this.el.addClass('x-form');
53547     },
53548
53549     // private
53550     onSubmit : function(e){
53551         e.stopEvent();
53552     },
53553
53554     /**
53555      * Returns true if client-side validation on the form is successful.
53556      * @return Boolean
53557      */
53558     isValid : function(){
53559         var valid = true;
53560         var target = false;
53561         this.items.each(function(f){
53562             if(f.validate()){
53563                 return;
53564             }
53565             
53566             valid = false;
53567                 
53568             if(!target && f.el.isVisible(true)){
53569                 target = f;
53570             }
53571         });
53572         
53573         if(this.errorMask && !valid){
53574             Roo.form.BasicForm.popover.mask(this, target);
53575         }
53576         
53577         return valid;
53578     },
53579     /**
53580      * Returns array of invalid form fields.
53581      * @return Array
53582      */
53583     
53584     invalidFields : function()
53585     {
53586         var ret = [];
53587         this.items.each(function(f){
53588             if(f.validate()){
53589                 return;
53590             }
53591             ret.push(f);
53592             
53593         });
53594         
53595         return ret;
53596     },
53597     
53598     
53599     /**
53600      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
53601      * @return Boolean
53602      */
53603     isDirty : function(){
53604         var dirty = false;
53605         this.items.each(function(f){
53606            if(f.isDirty()){
53607                dirty = true;
53608                return false;
53609            }
53610         });
53611         return dirty;
53612     },
53613     
53614     /**
53615      * Returns true if any fields in this form have changed since their original load. (New version)
53616      * @return Boolean
53617      */
53618     
53619     hasChanged : function()
53620     {
53621         var dirty = false;
53622         this.items.each(function(f){
53623            if(f.hasChanged()){
53624                dirty = true;
53625                return false;
53626            }
53627         });
53628         return dirty;
53629         
53630     },
53631     /**
53632      * Resets all hasChanged to 'false' -
53633      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
53634      * So hasChanged storage is only to be used for this purpose
53635      * @return Boolean
53636      */
53637     resetHasChanged : function()
53638     {
53639         this.items.each(function(f){
53640            f.resetHasChanged();
53641         });
53642         
53643     },
53644     
53645     
53646     /**
53647      * Performs a predefined action (submit or load) or custom actions you define on this form.
53648      * @param {String} actionName The name of the action type
53649      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
53650      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
53651      * accept other config options):
53652      * <pre>
53653 Property          Type             Description
53654 ----------------  ---------------  ----------------------------------------------------------------------------------
53655 url               String           The url for the action (defaults to the form's url)
53656 method            String           The form method to use (defaults to the form's method, or POST if not defined)
53657 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
53658 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
53659                                    validate the form on the client (defaults to false)
53660      * </pre>
53661      * @return {BasicForm} this
53662      */
53663     doAction : function(action, options){
53664         if(typeof action == 'string'){
53665             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
53666         }
53667         if(this.fireEvent('beforeaction', this, action) !== false){
53668             this.beforeAction(action);
53669             action.run.defer(100, action);
53670         }
53671         return this;
53672     },
53673
53674     /**
53675      * Shortcut to do a submit action.
53676      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53677      * @return {BasicForm} this
53678      */
53679     submit : function(options){
53680         this.doAction('submit', options);
53681         return this;
53682     },
53683
53684     /**
53685      * Shortcut to do a load action.
53686      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53687      * @return {BasicForm} this
53688      */
53689     load : function(options){
53690         this.doAction('load', options);
53691         return this;
53692     },
53693
53694     /**
53695      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
53696      * @param {Record} record The record to edit
53697      * @return {BasicForm} this
53698      */
53699     updateRecord : function(record){
53700         record.beginEdit();
53701         var fs = record.fields;
53702         fs.each(function(f){
53703             var field = this.findField(f.name);
53704             if(field){
53705                 record.set(f.name, field.getValue());
53706             }
53707         }, this);
53708         record.endEdit();
53709         return this;
53710     },
53711
53712     /**
53713      * Loads an Roo.data.Record into this form.
53714      * @param {Record} record The record to load
53715      * @return {BasicForm} this
53716      */
53717     loadRecord : function(record){
53718         this.setValues(record.data);
53719         return this;
53720     },
53721
53722     // private
53723     beforeAction : function(action){
53724         var o = action.options;
53725         
53726         if(!this.disableMask) {
53727             if(this.waitMsgTarget === true){
53728                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
53729             }else if(this.waitMsgTarget){
53730                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
53731                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
53732             }else {
53733                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
53734             }
53735         }
53736         
53737          
53738     },
53739
53740     // private
53741     afterAction : function(action, success){
53742         this.activeAction = null;
53743         var o = action.options;
53744         
53745         if(!this.disableMask) {
53746             if(this.waitMsgTarget === true){
53747                 this.el.unmask();
53748             }else if(this.waitMsgTarget){
53749                 this.waitMsgTarget.unmask();
53750             }else{
53751                 Roo.MessageBox.updateProgress(1);
53752                 Roo.MessageBox.hide();
53753             }
53754         }
53755         
53756         if(success){
53757             if(o.reset){
53758                 this.reset();
53759             }
53760             Roo.callback(o.success, o.scope, [this, action]);
53761             this.fireEvent('actioncomplete', this, action);
53762             
53763         }else{
53764             
53765             // failure condition..
53766             // we have a scenario where updates need confirming.
53767             // eg. if a locking scenario exists..
53768             // we look for { errors : { needs_confirm : true }} in the response.
53769             if (
53770                 (typeof(action.result) != 'undefined')  &&
53771                 (typeof(action.result.errors) != 'undefined')  &&
53772                 (typeof(action.result.errors.needs_confirm) != 'undefined')
53773            ){
53774                 var _t = this;
53775                 Roo.MessageBox.confirm(
53776                     "Change requires confirmation",
53777                     action.result.errorMsg,
53778                     function(r) {
53779                         if (r != 'yes') {
53780                             return;
53781                         }
53782                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
53783                     }
53784                     
53785                 );
53786                 
53787                 
53788                 
53789                 return;
53790             }
53791             
53792             Roo.callback(o.failure, o.scope, [this, action]);
53793             // show an error message if no failed handler is set..
53794             if (!this.hasListener('actionfailed')) {
53795                 Roo.MessageBox.alert("Error",
53796                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
53797                         action.result.errorMsg :
53798                         "Saving Failed, please check your entries or try again"
53799                 );
53800             }
53801             
53802             this.fireEvent('actionfailed', this, action);
53803         }
53804         
53805     },
53806
53807     /**
53808      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
53809      * @param {String} id The value to search for
53810      * @return Field
53811      */
53812     findField : function(id){
53813         var field = this.items.get(id);
53814         if(!field){
53815             this.items.each(function(f){
53816                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
53817                     field = f;
53818                     return false;
53819                 }
53820             });
53821         }
53822         return field || null;
53823     },
53824
53825     /**
53826      * Add a secondary form to this one, 
53827      * Used to provide tabbed forms. One form is primary, with hidden values 
53828      * which mirror the elements from the other forms.
53829      * 
53830      * @param {Roo.form.Form} form to add.
53831      * 
53832      */
53833     addForm : function(form)
53834     {
53835        
53836         if (this.childForms.indexOf(form) > -1) {
53837             // already added..
53838             return;
53839         }
53840         this.childForms.push(form);
53841         var n = '';
53842         Roo.each(form.allItems, function (fe) {
53843             
53844             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
53845             if (this.findField(n)) { // already added..
53846                 return;
53847             }
53848             var add = new Roo.form.Hidden({
53849                 name : n
53850             });
53851             add.render(this.el);
53852             
53853             this.add( add );
53854         }, this);
53855         
53856     },
53857     /**
53858      * Mark fields in this form invalid in bulk.
53859      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
53860      * @return {BasicForm} this
53861      */
53862     markInvalid : function(errors){
53863         if(errors instanceof Array){
53864             for(var i = 0, len = errors.length; i < len; i++){
53865                 var fieldError = errors[i];
53866                 var f = this.findField(fieldError.id);
53867                 if(f){
53868                     f.markInvalid(fieldError.msg);
53869                 }
53870             }
53871         }else{
53872             var field, id;
53873             for(id in errors){
53874                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
53875                     field.markInvalid(errors[id]);
53876                 }
53877             }
53878         }
53879         Roo.each(this.childForms || [], function (f) {
53880             f.markInvalid(errors);
53881         });
53882         
53883         return this;
53884     },
53885
53886     /**
53887      * Set values for fields in this form in bulk.
53888      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
53889      * @return {BasicForm} this
53890      */
53891     setValues : function(values){
53892         if(values instanceof Array){ // array of objects
53893             for(var i = 0, len = values.length; i < len; i++){
53894                 var v = values[i];
53895                 var f = this.findField(v.id);
53896                 if(f){
53897                     f.setValue(v.value);
53898                     if(this.trackResetOnLoad){
53899                         f.originalValue = f.getValue();
53900                     }
53901                 }
53902             }
53903         }else{ // object hash
53904             var field, id;
53905             for(id in values){
53906                 if(typeof values[id] != 'function' && (field = this.findField(id))){
53907                     
53908                     if (field.setFromData && 
53909                         field.valueField && 
53910                         field.displayField &&
53911                         // combos' with local stores can 
53912                         // be queried via setValue()
53913                         // to set their value..
53914                         (field.store && !field.store.isLocal)
53915                         ) {
53916                         // it's a combo
53917                         var sd = { };
53918                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
53919                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
53920                         field.setFromData(sd);
53921                         
53922                     } else {
53923                         field.setValue(values[id]);
53924                     }
53925                     
53926                     
53927                     if(this.trackResetOnLoad){
53928                         field.originalValue = field.getValue();
53929                     }
53930                 }
53931             }
53932         }
53933         this.resetHasChanged();
53934         
53935         
53936         Roo.each(this.childForms || [], function (f) {
53937             f.setValues(values);
53938             f.resetHasChanged();
53939         });
53940                 
53941         return this;
53942     },
53943  
53944     /**
53945      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
53946      * they are returned as an array.
53947      * @param {Boolean} asString
53948      * @return {Object}
53949      */
53950     getValues : function(asString)
53951     {
53952         if (this.childForms) {
53953             // copy values from the child forms
53954             Roo.each(this.childForms, function (f) {
53955                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
53956             }, this);
53957         }
53958         
53959         // use formdata
53960         if (typeof(FormData) != 'undefined' && asString !== true) {
53961             // this relies on a 'recent' version of chrome apparently...
53962             try {
53963                 var fd = (new FormData(this.el.dom)).entries();
53964                 var ret = {};
53965                 var ent = fd.next();
53966                 while (!ent.done) {
53967                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
53968                     ent = fd.next();
53969                 };
53970                 return ret;
53971             } catch(e) {
53972                 
53973             }
53974             
53975         }
53976         
53977         
53978         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
53979         if(asString === true){
53980             return fs;
53981         }
53982         return Roo.urlDecode(fs);
53983     },
53984     
53985     /**
53986      * Returns the fields in this form as an object with key/value pairs. 
53987      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
53988      * Normally this will not return readOnly data 
53989      * @param {Boolean} with_readonly return readonly field data.
53990      * @return {Object}
53991      */
53992     getFieldValues : function(with_readonly)
53993     {
53994         if (this.childForms) {
53995             // copy values from the child forms
53996             // should this call getFieldValues - probably not as we do not currently copy
53997             // hidden fields when we generate..
53998             Roo.each(this.childForms, function (f) {
53999                 this.setValues(f.getFieldValues());
54000             }, this);
54001         }
54002         
54003         var ret = {};
54004         this.items.each(function(f){
54005             
54006             if (f.readOnly && with_readonly !== true) {
54007                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
54008                         // if a subform contains a copy of them.
54009                         // if you have subforms with the same editable data, you will need to copy the data back
54010                         // and forth.
54011             }
54012             
54013             if (!f.getName()) {
54014                 return;
54015             }
54016             var v = f.getValue();
54017             if (f.inputType =='radio') {
54018                 if (typeof(ret[f.getName()]) == 'undefined') {
54019                     ret[f.getName()] = ''; // empty..
54020                 }
54021                 
54022                 if (!f.el.dom.checked) {
54023                     return;
54024                     
54025                 }
54026                 v = f.el.dom.value;
54027                 
54028             }
54029             
54030             // not sure if this supported any more..
54031             if ((typeof(v) == 'object') && f.getRawValue) {
54032                 v = f.getRawValue() ; // dates..
54033             }
54034             // combo boxes where name != hiddenName...
54035             if (f.name != f.getName()) {
54036                 ret[f.name] = f.getRawValue();
54037             }
54038             ret[f.getName()] = v;
54039         });
54040         
54041         return ret;
54042     },
54043
54044     /**
54045      * Clears all invalid messages in this form.
54046      * @return {BasicForm} this
54047      */
54048     clearInvalid : function(){
54049         this.items.each(function(f){
54050            f.clearInvalid();
54051         });
54052         
54053         Roo.each(this.childForms || [], function (f) {
54054             f.clearInvalid();
54055         });
54056         
54057         
54058         return this;
54059     },
54060
54061     /**
54062      * Resets this form.
54063      * @return {BasicForm} this
54064      */
54065     reset : function(){
54066         this.items.each(function(f){
54067             f.reset();
54068         });
54069         
54070         Roo.each(this.childForms || [], function (f) {
54071             f.reset();
54072         });
54073         this.resetHasChanged();
54074         
54075         return this;
54076     },
54077
54078     /**
54079      * Add Roo.form components to this form.
54080      * @param {Field} field1
54081      * @param {Field} field2 (optional)
54082      * @param {Field} etc (optional)
54083      * @return {BasicForm} this
54084      */
54085     add : function(){
54086         this.items.addAll(Array.prototype.slice.call(arguments, 0));
54087         return this;
54088     },
54089
54090
54091     /**
54092      * Removes a field from the items collection (does NOT remove its markup).
54093      * @param {Field} field
54094      * @return {BasicForm} this
54095      */
54096     remove : function(field){
54097         this.items.remove(field);
54098         return this;
54099     },
54100
54101     /**
54102      * Looks at the fields in this form, checks them for an id attribute,
54103      * and calls applyTo on the existing dom element with that id.
54104      * @return {BasicForm} this
54105      */
54106     render : function(){
54107         this.items.each(function(f){
54108             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
54109                 f.applyTo(f.id);
54110             }
54111         });
54112         return this;
54113     },
54114
54115     /**
54116      * Calls {@link Ext#apply} for all fields in this form with the passed object.
54117      * @param {Object} values
54118      * @return {BasicForm} this
54119      */
54120     applyToFields : function(o){
54121         this.items.each(function(f){
54122            Roo.apply(f, o);
54123         });
54124         return this;
54125     },
54126
54127     /**
54128      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
54129      * @param {Object} values
54130      * @return {BasicForm} this
54131      */
54132     applyIfToFields : function(o){
54133         this.items.each(function(f){
54134            Roo.applyIf(f, o);
54135         });
54136         return this;
54137     }
54138 });
54139
54140 // back compat
54141 Roo.BasicForm = Roo.form.BasicForm;
54142
54143 Roo.apply(Roo.form.BasicForm, {
54144     
54145     popover : {
54146         
54147         padding : 5,
54148         
54149         isApplied : false,
54150         
54151         isMasked : false,
54152         
54153         form : false,
54154         
54155         target : false,
54156         
54157         intervalID : false,
54158         
54159         maskEl : false,
54160         
54161         apply : function()
54162         {
54163             if(this.isApplied){
54164                 return;
54165             }
54166             
54167             this.maskEl = {
54168                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
54169                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
54170                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
54171                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
54172             };
54173             
54174             this.maskEl.top.enableDisplayMode("block");
54175             this.maskEl.left.enableDisplayMode("block");
54176             this.maskEl.bottom.enableDisplayMode("block");
54177             this.maskEl.right.enableDisplayMode("block");
54178             
54179             Roo.get(document.body).on('click', function(){
54180                 this.unmask();
54181             }, this);
54182             
54183             Roo.get(document.body).on('touchstart', function(){
54184                 this.unmask();
54185             }, this);
54186             
54187             this.isApplied = true
54188         },
54189         
54190         mask : function(form, target)
54191         {
54192             this.form = form;
54193             
54194             this.target = target;
54195             
54196             if(!this.form.errorMask || !target.el){
54197                 return;
54198             }
54199             
54200             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
54201             
54202             var ot = this.target.el.calcOffsetsTo(scrollable);
54203             
54204             var scrollTo = ot[1] - this.form.maskOffset;
54205             
54206             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
54207             
54208             scrollable.scrollTo('top', scrollTo);
54209             
54210             var el = this.target.wrap || this.target.el;
54211             
54212             var box = el.getBox();
54213             
54214             this.maskEl.top.setStyle('position', 'absolute');
54215             this.maskEl.top.setStyle('z-index', 10000);
54216             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
54217             this.maskEl.top.setLeft(0);
54218             this.maskEl.top.setTop(0);
54219             this.maskEl.top.show();
54220             
54221             this.maskEl.left.setStyle('position', 'absolute');
54222             this.maskEl.left.setStyle('z-index', 10000);
54223             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
54224             this.maskEl.left.setLeft(0);
54225             this.maskEl.left.setTop(box.y - this.padding);
54226             this.maskEl.left.show();
54227
54228             this.maskEl.bottom.setStyle('position', 'absolute');
54229             this.maskEl.bottom.setStyle('z-index', 10000);
54230             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
54231             this.maskEl.bottom.setLeft(0);
54232             this.maskEl.bottom.setTop(box.bottom + this.padding);
54233             this.maskEl.bottom.show();
54234
54235             this.maskEl.right.setStyle('position', 'absolute');
54236             this.maskEl.right.setStyle('z-index', 10000);
54237             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
54238             this.maskEl.right.setLeft(box.right + this.padding);
54239             this.maskEl.right.setTop(box.y - this.padding);
54240             this.maskEl.right.show();
54241
54242             this.intervalID = window.setInterval(function() {
54243                 Roo.form.BasicForm.popover.unmask();
54244             }, 10000);
54245
54246             window.onwheel = function(){ return false;};
54247             
54248             (function(){ this.isMasked = true; }).defer(500, this);
54249             
54250         },
54251         
54252         unmask : function()
54253         {
54254             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
54255                 return;
54256             }
54257             
54258             this.maskEl.top.setStyle('position', 'absolute');
54259             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
54260             this.maskEl.top.hide();
54261
54262             this.maskEl.left.setStyle('position', 'absolute');
54263             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
54264             this.maskEl.left.hide();
54265
54266             this.maskEl.bottom.setStyle('position', 'absolute');
54267             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
54268             this.maskEl.bottom.hide();
54269
54270             this.maskEl.right.setStyle('position', 'absolute');
54271             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
54272             this.maskEl.right.hide();
54273             
54274             window.onwheel = function(){ return true;};
54275             
54276             if(this.intervalID){
54277                 window.clearInterval(this.intervalID);
54278                 this.intervalID = false;
54279             }
54280             
54281             this.isMasked = false;
54282             
54283         }
54284         
54285     }
54286     
54287 });/*
54288  * Based on:
54289  * Ext JS Library 1.1.1
54290  * Copyright(c) 2006-2007, Ext JS, LLC.
54291  *
54292  * Originally Released Under LGPL - original licence link has changed is not relivant.
54293  *
54294  * Fork - LGPL
54295  * <script type="text/javascript">
54296  */
54297
54298 /**
54299  * @class Roo.form.Form
54300  * @extends Roo.form.BasicForm
54301  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
54302  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
54303  * @constructor
54304  * @param {Object} config Configuration options
54305  */
54306 Roo.form.Form = function(config){
54307     var xitems =  [];
54308     if (config.items) {
54309         xitems = config.items;
54310         delete config.items;
54311     }
54312    
54313     
54314     Roo.form.Form.superclass.constructor.call(this, null, config);
54315     this.url = this.url || this.action;
54316     if(!this.root){
54317         this.root = new Roo.form.Layout(Roo.applyIf({
54318             id: Roo.id()
54319         }, config));
54320     }
54321     this.active = this.root;
54322     /**
54323      * Array of all the buttons that have been added to this form via {@link addButton}
54324      * @type Array
54325      */
54326     this.buttons = [];
54327     this.allItems = [];
54328     this.addEvents({
54329         /**
54330          * @event clientvalidation
54331          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
54332          * @param {Form} this
54333          * @param {Boolean} valid true if the form has passed client-side validation
54334          */
54335         clientvalidation: true,
54336         /**
54337          * @event rendered
54338          * Fires when the form is rendered
54339          * @param {Roo.form.Form} form
54340          */
54341         rendered : true
54342     });
54343     
54344     if (this.progressUrl) {
54345             // push a hidden field onto the list of fields..
54346             this.addxtype( {
54347                     xns: Roo.form, 
54348                     xtype : 'Hidden', 
54349                     name : 'UPLOAD_IDENTIFIER' 
54350             });
54351         }
54352         
54353     
54354     Roo.each(xitems, this.addxtype, this);
54355     
54356 };
54357
54358 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
54359      /**
54360      * @cfg {Roo.Button} buttons[] buttons at bottom of form
54361      */
54362     
54363     /**
54364      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
54365      */
54366     /**
54367      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
54368      */
54369     /**
54370      * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
54371      */
54372     buttonAlign:'center',
54373
54374     /**
54375      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
54376      */
54377     minButtonWidth:75,
54378
54379     /**
54380      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
54381      * This property cascades to child containers if not set.
54382      */
54383     labelAlign:'left',
54384
54385     /**
54386      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
54387      * fires a looping event with that state. This is required to bind buttons to the valid
54388      * state using the config value formBind:true on the button.
54389      */
54390     monitorValid : false,
54391
54392     /**
54393      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
54394      */
54395     monitorPoll : 200,
54396     
54397     /**
54398      * @cfg {String} progressUrl - Url to return progress data 
54399      */
54400     
54401     progressUrl : false,
54402     /**
54403      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
54404      * sending a formdata with extra parameters - eg uploaded elements.
54405      */
54406     
54407     formData : false,
54408     
54409     /**
54410      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
54411      * fields are added and the column is closed. If no fields are passed the column remains open
54412      * until end() is called.
54413      * @param {Object} config The config to pass to the column
54414      * @param {Field} field1 (optional)
54415      * @param {Field} field2 (optional)
54416      * @param {Field} etc (optional)
54417      * @return Column The column container object
54418      */
54419     column : function(c){
54420         var col = new Roo.form.Column(c);
54421         this.start(col);
54422         if(arguments.length > 1){ // duplicate code required because of Opera
54423             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54424             this.end();
54425         }
54426         return col;
54427     },
54428
54429     /**
54430      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
54431      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
54432      * until end() is called.
54433      * @param {Object} config The config to pass to the fieldset
54434      * @param {Field} field1 (optional)
54435      * @param {Field} field2 (optional)
54436      * @param {Field} etc (optional)
54437      * @return FieldSet The fieldset container object
54438      */
54439     fieldset : function(c){
54440         var fs = new Roo.form.FieldSet(c);
54441         this.start(fs);
54442         if(arguments.length > 1){ // duplicate code required because of Opera
54443             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54444             this.end();
54445         }
54446         return fs;
54447     },
54448
54449     /**
54450      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
54451      * fields are added and the container is closed. If no fields are passed the container remains open
54452      * until end() is called.
54453      * @param {Object} config The config to pass to the Layout
54454      * @param {Field} field1 (optional)
54455      * @param {Field} field2 (optional)
54456      * @param {Field} etc (optional)
54457      * @return Layout The container object
54458      */
54459     container : function(c){
54460         var l = new Roo.form.Layout(c);
54461         this.start(l);
54462         if(arguments.length > 1){ // duplicate code required because of Opera
54463             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54464             this.end();
54465         }
54466         return l;
54467     },
54468
54469     /**
54470      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
54471      * @param {Object} container A Roo.form.Layout or subclass of Layout
54472      * @return {Form} this
54473      */
54474     start : function(c){
54475         // cascade label info
54476         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
54477         this.active.stack.push(c);
54478         c.ownerCt = this.active;
54479         this.active = c;
54480         return this;
54481     },
54482
54483     /**
54484      * Closes the current open container
54485      * @return {Form} this
54486      */
54487     end : function(){
54488         if(this.active == this.root){
54489             return this;
54490         }
54491         this.active = this.active.ownerCt;
54492         return this;
54493     },
54494
54495     /**
54496      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
54497      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
54498      * as the label of the field.
54499      * @param {Field} field1
54500      * @param {Field} field2 (optional)
54501      * @param {Field} etc. (optional)
54502      * @return {Form} this
54503      */
54504     add : function(){
54505         this.active.stack.push.apply(this.active.stack, arguments);
54506         this.allItems.push.apply(this.allItems,arguments);
54507         var r = [];
54508         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
54509             if(a[i].isFormField){
54510                 r.push(a[i]);
54511             }
54512         }
54513         if(r.length > 0){
54514             Roo.form.Form.superclass.add.apply(this, r);
54515         }
54516         return this;
54517     },
54518     
54519
54520     
54521     
54522     
54523      /**
54524      * Find any element that has been added to a form, using it's ID or name
54525      * This can include framesets, columns etc. along with regular fields..
54526      * @param {String} id - id or name to find.
54527      
54528      * @return {Element} e - or false if nothing found.
54529      */
54530     findbyId : function(id)
54531     {
54532         var ret = false;
54533         if (!id) {
54534             return ret;
54535         }
54536         Roo.each(this.allItems, function(f){
54537             if (f.id == id || f.name == id ){
54538                 ret = f;
54539                 return false;
54540             }
54541         });
54542         return ret;
54543     },
54544
54545     
54546     
54547     /**
54548      * Render this form into the passed container. This should only be called once!
54549      * @param {String/HTMLElement/Element} container The element this component should be rendered into
54550      * @return {Form} this
54551      */
54552     render : function(ct)
54553     {
54554         
54555         
54556         
54557         ct = Roo.get(ct);
54558         var o = this.autoCreate || {
54559             tag: 'form',
54560             method : this.method || 'POST',
54561             id : this.id || Roo.id()
54562         };
54563         this.initEl(ct.createChild(o));
54564
54565         this.root.render(this.el);
54566         
54567        
54568              
54569         this.items.each(function(f){
54570             f.render('x-form-el-'+f.id);
54571         });
54572
54573         if(this.buttons.length > 0){
54574             // tables are required to maintain order and for correct IE layout
54575             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
54576                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
54577                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
54578             }}, null, true);
54579             var tr = tb.getElementsByTagName('tr')[0];
54580             for(var i = 0, len = this.buttons.length; i < len; i++) {
54581                 var b = this.buttons[i];
54582                 var td = document.createElement('td');
54583                 td.className = 'x-form-btn-td';
54584                 b.render(tr.appendChild(td));
54585             }
54586         }
54587         if(this.monitorValid){ // initialize after render
54588             this.startMonitoring();
54589         }
54590         this.fireEvent('rendered', this);
54591         return this;
54592     },
54593
54594     /**
54595      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
54596      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
54597      * object or a valid Roo.DomHelper element config
54598      * @param {Function} handler The function called when the button is clicked
54599      * @param {Object} scope (optional) The scope of the handler function
54600      * @return {Roo.Button}
54601      */
54602     addButton : function(config, handler, scope){
54603         var bc = {
54604             handler: handler,
54605             scope: scope,
54606             minWidth: this.minButtonWidth,
54607             hideParent:true
54608         };
54609         if(typeof config == "string"){
54610             bc.text = config;
54611         }else{
54612             Roo.apply(bc, config);
54613         }
54614         var btn = new Roo.Button(null, bc);
54615         this.buttons.push(btn);
54616         return btn;
54617     },
54618
54619      /**
54620      * Adds a series of form elements (using the xtype property as the factory method.
54621      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
54622      * @param {Object} config 
54623      */
54624     
54625     addxtype : function()
54626     {
54627         var ar = Array.prototype.slice.call(arguments, 0);
54628         var ret = false;
54629         for(var i = 0; i < ar.length; i++) {
54630             if (!ar[i]) {
54631                 continue; // skip -- if this happends something invalid got sent, we 
54632                 // should ignore it, as basically that interface element will not show up
54633                 // and that should be pretty obvious!!
54634             }
54635             
54636             if (Roo.form[ar[i].xtype]) {
54637                 ar[i].form = this;
54638                 var fe = Roo.factory(ar[i], Roo.form);
54639                 if (!ret) {
54640                     ret = fe;
54641                 }
54642                 fe.form = this;
54643                 if (fe.store) {
54644                     fe.store.form = this;
54645                 }
54646                 if (fe.isLayout) {  
54647                          
54648                     this.start(fe);
54649                     this.allItems.push(fe);
54650                     if (fe.items && fe.addxtype) {
54651                         fe.addxtype.apply(fe, fe.items);
54652                         delete fe.items;
54653                     }
54654                      this.end();
54655                     continue;
54656                 }
54657                 
54658                 
54659                  
54660                 this.add(fe);
54661               //  console.log('adding ' + ar[i].xtype);
54662             }
54663             if (ar[i].xtype == 'Button') {  
54664                 //console.log('adding button');
54665                 //console.log(ar[i]);
54666                 this.addButton(ar[i]);
54667                 this.allItems.push(fe);
54668                 continue;
54669             }
54670             
54671             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
54672                 alert('end is not supported on xtype any more, use items');
54673             //    this.end();
54674             //    //console.log('adding end');
54675             }
54676             
54677         }
54678         return ret;
54679     },
54680     
54681     /**
54682      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
54683      * option "monitorValid"
54684      */
54685     startMonitoring : function(){
54686         if(!this.bound){
54687             this.bound = true;
54688             Roo.TaskMgr.start({
54689                 run : this.bindHandler,
54690                 interval : this.monitorPoll || 200,
54691                 scope: this
54692             });
54693         }
54694     },
54695
54696     /**
54697      * Stops monitoring of the valid state of this form
54698      */
54699     stopMonitoring : function(){
54700         this.bound = false;
54701     },
54702
54703     // private
54704     bindHandler : function(){
54705         if(!this.bound){
54706             return false; // stops binding
54707         }
54708         var valid = true;
54709         this.items.each(function(f){
54710             if(!f.isValid(true)){
54711                 valid = false;
54712                 return false;
54713             }
54714         });
54715         for(var i = 0, len = this.buttons.length; i < len; i++){
54716             var btn = this.buttons[i];
54717             if(btn.formBind === true && btn.disabled === valid){
54718                 btn.setDisabled(!valid);
54719             }
54720         }
54721         this.fireEvent('clientvalidation', this, valid);
54722     }
54723     
54724     
54725     
54726     
54727     
54728     
54729     
54730     
54731 });
54732
54733
54734 // back compat
54735 Roo.Form = Roo.form.Form;
54736 /*
54737  * Based on:
54738  * Ext JS Library 1.1.1
54739  * Copyright(c) 2006-2007, Ext JS, LLC.
54740  *
54741  * Originally Released Under LGPL - original licence link has changed is not relivant.
54742  *
54743  * Fork - LGPL
54744  * <script type="text/javascript">
54745  */
54746
54747 // as we use this in bootstrap.
54748 Roo.namespace('Roo.form');
54749  /**
54750  * @class Roo.form.Action
54751  * Internal Class used to handle form actions
54752  * @constructor
54753  * @param {Roo.form.BasicForm} el The form element or its id
54754  * @param {Object} config Configuration options
54755  */
54756
54757  
54758  
54759 // define the action interface
54760 Roo.form.Action = function(form, options){
54761     this.form = form;
54762     this.options = options || {};
54763 };
54764 /**
54765  * Client Validation Failed
54766  * @const 
54767  */
54768 Roo.form.Action.CLIENT_INVALID = 'client';
54769 /**
54770  * Server Validation Failed
54771  * @const 
54772  */
54773 Roo.form.Action.SERVER_INVALID = 'server';
54774  /**
54775  * Connect to Server Failed
54776  * @const 
54777  */
54778 Roo.form.Action.CONNECT_FAILURE = 'connect';
54779 /**
54780  * Reading Data from Server Failed
54781  * @const 
54782  */
54783 Roo.form.Action.LOAD_FAILURE = 'load';
54784
54785 Roo.form.Action.prototype = {
54786     type : 'default',
54787     failureType : undefined,
54788     response : undefined,
54789     result : undefined,
54790
54791     // interface method
54792     run : function(options){
54793
54794     },
54795
54796     // interface method
54797     success : function(response){
54798
54799     },
54800
54801     // interface method
54802     handleResponse : function(response){
54803
54804     },
54805
54806     // default connection failure
54807     failure : function(response){
54808         
54809         this.response = response;
54810         this.failureType = Roo.form.Action.CONNECT_FAILURE;
54811         this.form.afterAction(this, false);
54812     },
54813
54814     processResponse : function(response){
54815         this.response = response;
54816         if(!response.responseText){
54817             return true;
54818         }
54819         this.result = this.handleResponse(response);
54820         return this.result;
54821     },
54822
54823     // utility functions used internally
54824     getUrl : function(appendParams){
54825         var url = this.options.url || this.form.url || this.form.el.dom.action;
54826         if(appendParams){
54827             var p = this.getParams();
54828             if(p){
54829                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
54830             }
54831         }
54832         return url;
54833     },
54834
54835     getMethod : function(){
54836         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
54837     },
54838
54839     getParams : function(){
54840         var bp = this.form.baseParams;
54841         var p = this.options.params;
54842         if(p){
54843             if(typeof p == "object"){
54844                 p = Roo.urlEncode(Roo.applyIf(p, bp));
54845             }else if(typeof p == 'string' && bp){
54846                 p += '&' + Roo.urlEncode(bp);
54847             }
54848         }else if(bp){
54849             p = Roo.urlEncode(bp);
54850         }
54851         return p;
54852     },
54853
54854     createCallback : function(){
54855         return {
54856             success: this.success,
54857             failure: this.failure,
54858             scope: this,
54859             timeout: (this.form.timeout*1000),
54860             upload: this.form.fileUpload ? this.success : undefined
54861         };
54862     }
54863 };
54864
54865 Roo.form.Action.Submit = function(form, options){
54866     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
54867 };
54868
54869 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
54870     type : 'submit',
54871
54872     haveProgress : false,
54873     uploadComplete : false,
54874     
54875     // uploadProgress indicator.
54876     uploadProgress : function()
54877     {
54878         if (!this.form.progressUrl) {
54879             return;
54880         }
54881         
54882         if (!this.haveProgress) {
54883             Roo.MessageBox.progress("Uploading", "Uploading");
54884         }
54885         if (this.uploadComplete) {
54886            Roo.MessageBox.hide();
54887            return;
54888         }
54889         
54890         this.haveProgress = true;
54891    
54892         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
54893         
54894         var c = new Roo.data.Connection();
54895         c.request({
54896             url : this.form.progressUrl,
54897             params: {
54898                 id : uid
54899             },
54900             method: 'GET',
54901             success : function(req){
54902                //console.log(data);
54903                 var rdata = false;
54904                 var edata;
54905                 try  {
54906                    rdata = Roo.decode(req.responseText)
54907                 } catch (e) {
54908                     Roo.log("Invalid data from server..");
54909                     Roo.log(edata);
54910                     return;
54911                 }
54912                 if (!rdata || !rdata.success) {
54913                     Roo.log(rdata);
54914                     Roo.MessageBox.alert(Roo.encode(rdata));
54915                     return;
54916                 }
54917                 var data = rdata.data;
54918                 
54919                 if (this.uploadComplete) {
54920                    Roo.MessageBox.hide();
54921                    return;
54922                 }
54923                    
54924                 if (data){
54925                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
54926                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
54927                     );
54928                 }
54929                 this.uploadProgress.defer(2000,this);
54930             },
54931        
54932             failure: function(data) {
54933                 Roo.log('progress url failed ');
54934                 Roo.log(data);
54935             },
54936             scope : this
54937         });
54938            
54939     },
54940     
54941     
54942     run : function()
54943     {
54944         // run get Values on the form, so it syncs any secondary forms.
54945         this.form.getValues();
54946         
54947         var o = this.options;
54948         var method = this.getMethod();
54949         var isPost = method == 'POST';
54950         if(o.clientValidation === false || this.form.isValid()){
54951             
54952             if (this.form.progressUrl) {
54953                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
54954                     (new Date() * 1) + '' + Math.random());
54955                     
54956             } 
54957             
54958             
54959             Roo.Ajax.request(Roo.apply(this.createCallback(), {
54960                 form:this.form.el.dom,
54961                 url:this.getUrl(!isPost),
54962                 method: method,
54963                 params:isPost ? this.getParams() : null,
54964                 isUpload: this.form.fileUpload,
54965                 formData : this.form.formData
54966             }));
54967             
54968             this.uploadProgress();
54969
54970         }else if (o.clientValidation !== false){ // client validation failed
54971             this.failureType = Roo.form.Action.CLIENT_INVALID;
54972             this.form.afterAction(this, false);
54973         }
54974     },
54975
54976     success : function(response)
54977     {
54978         this.uploadComplete= true;
54979         if (this.haveProgress) {
54980             Roo.MessageBox.hide();
54981         }
54982         
54983         
54984         var result = this.processResponse(response);
54985         if(result === true || result.success){
54986             this.form.afterAction(this, true);
54987             return;
54988         }
54989         if(result.errors){
54990             this.form.markInvalid(result.errors);
54991             this.failureType = Roo.form.Action.SERVER_INVALID;
54992         }
54993         this.form.afterAction(this, false);
54994     },
54995     failure : function(response)
54996     {
54997         this.uploadComplete= true;
54998         if (this.haveProgress) {
54999             Roo.MessageBox.hide();
55000         }
55001         
55002         this.response = response;
55003         this.failureType = Roo.form.Action.CONNECT_FAILURE;
55004         this.form.afterAction(this, false);
55005     },
55006     
55007     handleResponse : function(response){
55008         if(this.form.errorReader){
55009             var rs = this.form.errorReader.read(response);
55010             var errors = [];
55011             if(rs.records){
55012                 for(var i = 0, len = rs.records.length; i < len; i++) {
55013                     var r = rs.records[i];
55014                     errors[i] = r.data;
55015                 }
55016             }
55017             if(errors.length < 1){
55018                 errors = null;
55019             }
55020             return {
55021                 success : rs.success,
55022                 errors : errors
55023             };
55024         }
55025         var ret = false;
55026         try {
55027             ret = Roo.decode(response.responseText);
55028         } catch (e) {
55029             ret = {
55030                 success: false,
55031                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
55032                 errors : []
55033             };
55034         }
55035         return ret;
55036         
55037     }
55038 });
55039
55040
55041 Roo.form.Action.Load = function(form, options){
55042     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
55043     this.reader = this.form.reader;
55044 };
55045
55046 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
55047     type : 'load',
55048
55049     run : function(){
55050         
55051         Roo.Ajax.request(Roo.apply(
55052                 this.createCallback(), {
55053                     method:this.getMethod(),
55054                     url:this.getUrl(false),
55055                     params:this.getParams()
55056         }));
55057     },
55058
55059     success : function(response){
55060         
55061         var result = this.processResponse(response);
55062         if(result === true || !result.success || !result.data){
55063             this.failureType = Roo.form.Action.LOAD_FAILURE;
55064             this.form.afterAction(this, false);
55065             return;
55066         }
55067         this.form.clearInvalid();
55068         this.form.setValues(result.data);
55069         this.form.afterAction(this, true);
55070     },
55071
55072     handleResponse : function(response){
55073         if(this.form.reader){
55074             var rs = this.form.reader.read(response);
55075             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
55076             return {
55077                 success : rs.success,
55078                 data : data
55079             };
55080         }
55081         return Roo.decode(response.responseText);
55082     }
55083 });
55084
55085 Roo.form.Action.ACTION_TYPES = {
55086     'load' : Roo.form.Action.Load,
55087     'submit' : Roo.form.Action.Submit
55088 };/*
55089  * Based on:
55090  * Ext JS Library 1.1.1
55091  * Copyright(c) 2006-2007, Ext JS, LLC.
55092  *
55093  * Originally Released Under LGPL - original licence link has changed is not relivant.
55094  *
55095  * Fork - LGPL
55096  * <script type="text/javascript">
55097  */
55098  
55099 /**
55100  * @class Roo.form.Layout
55101  * @extends Roo.Component
55102  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55103  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
55104  * @constructor
55105  * @param {Object} config Configuration options
55106  */
55107 Roo.form.Layout = function(config){
55108     var xitems = [];
55109     if (config.items) {
55110         xitems = config.items;
55111         delete config.items;
55112     }
55113     Roo.form.Layout.superclass.constructor.call(this, config);
55114     this.stack = [];
55115     Roo.each(xitems, this.addxtype, this);
55116      
55117 };
55118
55119 Roo.extend(Roo.form.Layout, Roo.Component, {
55120     /**
55121      * @cfg {String/Object} autoCreate
55122      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
55123      */
55124     /**
55125      * @cfg {String/Object/Function} style
55126      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
55127      * a function which returns such a specification.
55128      */
55129     /**
55130      * @cfg {String} labelAlign
55131      * Valid values are "left," "top" and "right" (defaults to "left")
55132      */
55133     /**
55134      * @cfg {Number} labelWidth
55135      * Fixed width in pixels of all field labels (defaults to undefined)
55136      */
55137     /**
55138      * @cfg {Boolean} clear
55139      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
55140      */
55141     clear : true,
55142     /**
55143      * @cfg {String} labelSeparator
55144      * The separator to use after field labels (defaults to ':')
55145      */
55146     labelSeparator : ':',
55147     /**
55148      * @cfg {Boolean} hideLabels
55149      * True to suppress the display of field labels in this layout (defaults to false)
55150      */
55151     hideLabels : false,
55152
55153     // private
55154     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
55155     
55156     isLayout : true,
55157     
55158     // private
55159     onRender : function(ct, position){
55160         if(this.el){ // from markup
55161             this.el = Roo.get(this.el);
55162         }else {  // generate
55163             var cfg = this.getAutoCreate();
55164             this.el = ct.createChild(cfg, position);
55165         }
55166         if(this.style){
55167             this.el.applyStyles(this.style);
55168         }
55169         if(this.labelAlign){
55170             this.el.addClass('x-form-label-'+this.labelAlign);
55171         }
55172         if(this.hideLabels){
55173             this.labelStyle = "display:none";
55174             this.elementStyle = "padding-left:0;";
55175         }else{
55176             if(typeof this.labelWidth == 'number'){
55177                 this.labelStyle = "width:"+this.labelWidth+"px;";
55178                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
55179             }
55180             if(this.labelAlign == 'top'){
55181                 this.labelStyle = "width:auto;";
55182                 this.elementStyle = "padding-left:0;";
55183             }
55184         }
55185         var stack = this.stack;
55186         var slen = stack.length;
55187         if(slen > 0){
55188             if(!this.fieldTpl){
55189                 var t = new Roo.Template(
55190                     '<div class="x-form-item {5}">',
55191                         '<label for="{0}" style="{2}">{1}{4}</label>',
55192                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55193                         '</div>',
55194                     '</div><div class="x-form-clear-left"></div>'
55195                 );
55196                 t.disableFormats = true;
55197                 t.compile();
55198                 Roo.form.Layout.prototype.fieldTpl = t;
55199             }
55200             for(var i = 0; i < slen; i++) {
55201                 if(stack[i].isFormField){
55202                     this.renderField(stack[i]);
55203                 }else{
55204                     this.renderComponent(stack[i]);
55205                 }
55206             }
55207         }
55208         if(this.clear){
55209             this.el.createChild({cls:'x-form-clear'});
55210         }
55211     },
55212
55213     // private
55214     renderField : function(f){
55215         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
55216                f.id, //0
55217                f.fieldLabel, //1
55218                f.labelStyle||this.labelStyle||'', //2
55219                this.elementStyle||'', //3
55220                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
55221                f.itemCls||this.itemCls||''  //5
55222        ], true).getPrevSibling());
55223     },
55224
55225     // private
55226     renderComponent : function(c){
55227         c.render(c.isLayout ? this.el : this.el.createChild());    
55228     },
55229     /**
55230      * Adds a object form elements (using the xtype property as the factory method.)
55231      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
55232      * @param {Object} config 
55233      */
55234     addxtype : function(o)
55235     {
55236         // create the lement.
55237         o.form = this.form;
55238         var fe = Roo.factory(o, Roo.form);
55239         this.form.allItems.push(fe);
55240         this.stack.push(fe);
55241         
55242         if (fe.isFormField) {
55243             this.form.items.add(fe);
55244         }
55245          
55246         return fe;
55247     }
55248 });
55249
55250 /**
55251  * @class Roo.form.Column
55252  * @extends Roo.form.Layout
55253  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55254  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
55255  * @constructor
55256  * @param {Object} config Configuration options
55257  */
55258 Roo.form.Column = function(config){
55259     Roo.form.Column.superclass.constructor.call(this, config);
55260 };
55261
55262 Roo.extend(Roo.form.Column, Roo.form.Layout, {
55263     /**
55264      * @cfg {Number/String} width
55265      * The fixed width of the column in pixels or CSS value (defaults to "auto")
55266      */
55267     /**
55268      * @cfg {String/Object} autoCreate
55269      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
55270      */
55271
55272     // private
55273     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
55274
55275     // private
55276     onRender : function(ct, position){
55277         Roo.form.Column.superclass.onRender.call(this, ct, position);
55278         if(this.width){
55279             this.el.setWidth(this.width);
55280         }
55281     }
55282 });
55283
55284
55285 /**
55286  * @class Roo.form.Row
55287  * @extends Roo.form.Layout
55288  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55289  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
55290  * @constructor
55291  * @param {Object} config Configuration options
55292  */
55293
55294  
55295 Roo.form.Row = function(config){
55296     Roo.form.Row.superclass.constructor.call(this, config);
55297 };
55298  
55299 Roo.extend(Roo.form.Row, Roo.form.Layout, {
55300       /**
55301      * @cfg {Number/String} width
55302      * The fixed width of the column in pixels or CSS value (defaults to "auto")
55303      */
55304     /**
55305      * @cfg {Number/String} height
55306      * The fixed height of the column in pixels or CSS value (defaults to "auto")
55307      */
55308     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
55309     
55310     padWidth : 20,
55311     // private
55312     onRender : function(ct, position){
55313         //console.log('row render');
55314         if(!this.rowTpl){
55315             var t = new Roo.Template(
55316                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
55317                     '<label for="{0}" style="{2}">{1}{4}</label>',
55318                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55319                     '</div>',
55320                 '</div>'
55321             );
55322             t.disableFormats = true;
55323             t.compile();
55324             Roo.form.Layout.prototype.rowTpl = t;
55325         }
55326         this.fieldTpl = this.rowTpl;
55327         
55328         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
55329         var labelWidth = 100;
55330         
55331         if ((this.labelAlign != 'top')) {
55332             if (typeof this.labelWidth == 'number') {
55333                 labelWidth = this.labelWidth
55334             }
55335             this.padWidth =  20 + labelWidth;
55336             
55337         }
55338         
55339         Roo.form.Column.superclass.onRender.call(this, ct, position);
55340         if(this.width){
55341             this.el.setWidth(this.width);
55342         }
55343         if(this.height){
55344             this.el.setHeight(this.height);
55345         }
55346     },
55347     
55348     // private
55349     renderField : function(f){
55350         f.fieldEl = this.fieldTpl.append(this.el, [
55351                f.id, f.fieldLabel,
55352                f.labelStyle||this.labelStyle||'',
55353                this.elementStyle||'',
55354                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
55355                f.itemCls||this.itemCls||'',
55356                f.width ? f.width + this.padWidth : 160 + this.padWidth
55357        ],true);
55358     }
55359 });
55360  
55361
55362 /**
55363  * @class Roo.form.FieldSet
55364  * @extends Roo.form.Layout
55365  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
55366  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
55367  * @constructor
55368  * @param {Object} config Configuration options
55369  */
55370 Roo.form.FieldSet = function(config){
55371     Roo.form.FieldSet.superclass.constructor.call(this, config);
55372 };
55373
55374 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
55375     /**
55376      * @cfg {String} legend
55377      * The text to display as the legend for the FieldSet (defaults to '')
55378      */
55379     /**
55380      * @cfg {String/Object} autoCreate
55381      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
55382      */
55383
55384     // private
55385     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
55386
55387     // private
55388     onRender : function(ct, position){
55389         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
55390         if(this.legend){
55391             this.setLegend(this.legend);
55392         }
55393     },
55394
55395     // private
55396     setLegend : function(text){
55397         if(this.rendered){
55398             this.el.child('legend').update(text);
55399         }
55400     }
55401 });/*
55402  * Based on:
55403  * Ext JS Library 1.1.1
55404  * Copyright(c) 2006-2007, Ext JS, LLC.
55405  *
55406  * Originally Released Under LGPL - original licence link has changed is not relivant.
55407  *
55408  * Fork - LGPL
55409  * <script type="text/javascript">
55410  */
55411 /**
55412  * @class Roo.form.VTypes
55413  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
55414  * @static
55415  */
55416 Roo.form.VTypes = function(){
55417     // closure these in so they are only created once.
55418     var alpha = /^[a-zA-Z_]+$/;
55419     var alphanum = /^[a-zA-Z0-9_]+$/;
55420     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
55421     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
55422
55423     // All these messages and functions are configurable
55424     return {
55425         /**
55426          * The function used to validate email addresses
55427          * @param {String} value The email address
55428          */
55429         'email' : function(v){
55430             return email.test(v);
55431         },
55432         /**
55433          * The error text to display when the email validation function returns false
55434          * @type String
55435          */
55436         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
55437         /**
55438          * The keystroke filter mask to be applied on email input
55439          * @type RegExp
55440          */
55441         'emailMask' : /[a-z0-9_\.\-@]/i,
55442
55443         /**
55444          * The function used to validate URLs
55445          * @param {String} value The URL
55446          */
55447         'url' : function(v){
55448             return url.test(v);
55449         },
55450         /**
55451          * The error text to display when the url validation function returns false
55452          * @type String
55453          */
55454         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
55455         
55456         /**
55457          * The function used to validate alpha values
55458          * @param {String} value The value
55459          */
55460         'alpha' : function(v){
55461             return alpha.test(v);
55462         },
55463         /**
55464          * The error text to display when the alpha validation function returns false
55465          * @type String
55466          */
55467         'alphaText' : 'This field should only contain letters and _',
55468         /**
55469          * The keystroke filter mask to be applied on alpha input
55470          * @type RegExp
55471          */
55472         'alphaMask' : /[a-z_]/i,
55473
55474         /**
55475          * The function used to validate alphanumeric values
55476          * @param {String} value The value
55477          */
55478         'alphanum' : function(v){
55479             return alphanum.test(v);
55480         },
55481         /**
55482          * The error text to display when the alphanumeric validation function returns false
55483          * @type String
55484          */
55485         'alphanumText' : 'This field should only contain letters, numbers and _',
55486         /**
55487          * The keystroke filter mask to be applied on alphanumeric input
55488          * @type RegExp
55489          */
55490         'alphanumMask' : /[a-z0-9_]/i
55491     };
55492 }();//<script type="text/javascript">
55493
55494 /**
55495  * @class Roo.form.FCKeditor
55496  * @extends Roo.form.TextArea
55497  * Wrapper around the FCKEditor http://www.fckeditor.net
55498  * @constructor
55499  * Creates a new FCKeditor
55500  * @param {Object} config Configuration options
55501  */
55502 Roo.form.FCKeditor = function(config){
55503     Roo.form.FCKeditor.superclass.constructor.call(this, config);
55504     this.addEvents({
55505          /**
55506          * @event editorinit
55507          * Fired when the editor is initialized - you can add extra handlers here..
55508          * @param {FCKeditor} this
55509          * @param {Object} the FCK object.
55510          */
55511         editorinit : true
55512     });
55513     
55514     
55515 };
55516 Roo.form.FCKeditor.editors = { };
55517 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
55518 {
55519     //defaultAutoCreate : {
55520     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
55521     //},
55522     // private
55523     /**
55524      * @cfg {Object} fck options - see fck manual for details.
55525      */
55526     fckconfig : false,
55527     
55528     /**
55529      * @cfg {Object} fck toolbar set (Basic or Default)
55530      */
55531     toolbarSet : 'Basic',
55532     /**
55533      * @cfg {Object} fck BasePath
55534      */ 
55535     basePath : '/fckeditor/',
55536     
55537     
55538     frame : false,
55539     
55540     value : '',
55541     
55542    
55543     onRender : function(ct, position)
55544     {
55545         if(!this.el){
55546             this.defaultAutoCreate = {
55547                 tag: "textarea",
55548                 style:"width:300px;height:60px;",
55549                 autocomplete: "new-password"
55550             };
55551         }
55552         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
55553         /*
55554         if(this.grow){
55555             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
55556             if(this.preventScrollbars){
55557                 this.el.setStyle("overflow", "hidden");
55558             }
55559             this.el.setHeight(this.growMin);
55560         }
55561         */
55562         //console.log('onrender' + this.getId() );
55563         Roo.form.FCKeditor.editors[this.getId()] = this;
55564          
55565
55566         this.replaceTextarea() ;
55567         
55568     },
55569     
55570     getEditor : function() {
55571         return this.fckEditor;
55572     },
55573     /**
55574      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
55575      * @param {Mixed} value The value to set
55576      */
55577     
55578     
55579     setValue : function(value)
55580     {
55581         //console.log('setValue: ' + value);
55582         
55583         if(typeof(value) == 'undefined') { // not sure why this is happending...
55584             return;
55585         }
55586         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55587         
55588         //if(!this.el || !this.getEditor()) {
55589         //    this.value = value;
55590             //this.setValue.defer(100,this,[value]);    
55591         //    return;
55592         //} 
55593         
55594         if(!this.getEditor()) {
55595             return;
55596         }
55597         
55598         this.getEditor().SetData(value);
55599         
55600         //
55601
55602     },
55603
55604     /**
55605      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
55606      * @return {Mixed} value The field value
55607      */
55608     getValue : function()
55609     {
55610         
55611         if (this.frame && this.frame.dom.style.display == 'none') {
55612             return Roo.form.FCKeditor.superclass.getValue.call(this);
55613         }
55614         
55615         if(!this.el || !this.getEditor()) {
55616            
55617            // this.getValue.defer(100,this); 
55618             return this.value;
55619         }
55620        
55621         
55622         var value=this.getEditor().GetData();
55623         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55624         return Roo.form.FCKeditor.superclass.getValue.call(this);
55625         
55626
55627     },
55628
55629     /**
55630      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
55631      * @return {Mixed} value The field value
55632      */
55633     getRawValue : function()
55634     {
55635         if (this.frame && this.frame.dom.style.display == 'none') {
55636             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55637         }
55638         
55639         if(!this.el || !this.getEditor()) {
55640             //this.getRawValue.defer(100,this); 
55641             return this.value;
55642             return;
55643         }
55644         
55645         
55646         
55647         var value=this.getEditor().GetData();
55648         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
55649         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55650          
55651     },
55652     
55653     setSize : function(w,h) {
55654         
55655         
55656         
55657         //if (this.frame && this.frame.dom.style.display == 'none') {
55658         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55659         //    return;
55660         //}
55661         //if(!this.el || !this.getEditor()) {
55662         //    this.setSize.defer(100,this, [w,h]); 
55663         //    return;
55664         //}
55665         
55666         
55667         
55668         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55669         
55670         this.frame.dom.setAttribute('width', w);
55671         this.frame.dom.setAttribute('height', h);
55672         this.frame.setSize(w,h);
55673         
55674     },
55675     
55676     toggleSourceEdit : function(value) {
55677         
55678       
55679          
55680         this.el.dom.style.display = value ? '' : 'none';
55681         this.frame.dom.style.display = value ?  'none' : '';
55682         
55683     },
55684     
55685     
55686     focus: function(tag)
55687     {
55688         if (this.frame.dom.style.display == 'none') {
55689             return Roo.form.FCKeditor.superclass.focus.call(this);
55690         }
55691         if(!this.el || !this.getEditor()) {
55692             this.focus.defer(100,this, [tag]); 
55693             return;
55694         }
55695         
55696         
55697         
55698         
55699         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
55700         this.getEditor().Focus();
55701         if (tgs.length) {
55702             if (!this.getEditor().Selection.GetSelection()) {
55703                 this.focus.defer(100,this, [tag]); 
55704                 return;
55705             }
55706             
55707             
55708             var r = this.getEditor().EditorDocument.createRange();
55709             r.setStart(tgs[0],0);
55710             r.setEnd(tgs[0],0);
55711             this.getEditor().Selection.GetSelection().removeAllRanges();
55712             this.getEditor().Selection.GetSelection().addRange(r);
55713             this.getEditor().Focus();
55714         }
55715         
55716     },
55717     
55718     
55719     
55720     replaceTextarea : function()
55721     {
55722         if ( document.getElementById( this.getId() + '___Frame' ) ) {
55723             return ;
55724         }
55725         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
55726         //{
55727             // We must check the elements firstly using the Id and then the name.
55728         var oTextarea = document.getElementById( this.getId() );
55729         
55730         var colElementsByName = document.getElementsByName( this.getId() ) ;
55731          
55732         oTextarea.style.display = 'none' ;
55733
55734         if ( oTextarea.tabIndex ) {            
55735             this.TabIndex = oTextarea.tabIndex ;
55736         }
55737         
55738         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
55739         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
55740         this.frame = Roo.get(this.getId() + '___Frame')
55741     },
55742     
55743     _getConfigHtml : function()
55744     {
55745         var sConfig = '' ;
55746
55747         for ( var o in this.fckconfig ) {
55748             sConfig += sConfig.length > 0  ? '&amp;' : '';
55749             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
55750         }
55751
55752         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
55753     },
55754     
55755     
55756     _getIFrameHtml : function()
55757     {
55758         var sFile = 'fckeditor.html' ;
55759         /* no idea what this is about..
55760         try
55761         {
55762             if ( (/fcksource=true/i).test( window.top.location.search ) )
55763                 sFile = 'fckeditor.original.html' ;
55764         }
55765         catch (e) { 
55766         */
55767
55768         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
55769         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
55770         
55771         
55772         var html = '<iframe id="' + this.getId() +
55773             '___Frame" src="' + sLink +
55774             '" width="' + this.width +
55775             '" height="' + this.height + '"' +
55776             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
55777             ' frameborder="0" scrolling="no"></iframe>' ;
55778
55779         return html ;
55780     },
55781     
55782     _insertHtmlBefore : function( html, element )
55783     {
55784         if ( element.insertAdjacentHTML )       {
55785             // IE
55786             element.insertAdjacentHTML( 'beforeBegin', html ) ;
55787         } else { // Gecko
55788             var oRange = document.createRange() ;
55789             oRange.setStartBefore( element ) ;
55790             var oFragment = oRange.createContextualFragment( html );
55791             element.parentNode.insertBefore( oFragment, element ) ;
55792         }
55793     }
55794     
55795     
55796   
55797     
55798     
55799     
55800     
55801
55802 });
55803
55804 //Roo.reg('fckeditor', Roo.form.FCKeditor);
55805
55806 function FCKeditor_OnComplete(editorInstance){
55807     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
55808     f.fckEditor = editorInstance;
55809     //console.log("loaded");
55810     f.fireEvent('editorinit', f, editorInstance);
55811
55812   
55813
55814  
55815
55816
55817
55818
55819
55820
55821
55822
55823
55824
55825
55826
55827
55828
55829
55830 //<script type="text/javascript">
55831 /**
55832  * @class Roo.form.GridField
55833  * @extends Roo.form.Field
55834  * Embed a grid (or editable grid into a form)
55835  * STATUS ALPHA
55836  * 
55837  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
55838  * it needs 
55839  * xgrid.store = Roo.data.Store
55840  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
55841  * xgrid.store.reader = Roo.data.JsonReader 
55842  * 
55843  * 
55844  * @constructor
55845  * Creates a new GridField
55846  * @param {Object} config Configuration options
55847  */
55848 Roo.form.GridField = function(config){
55849     Roo.form.GridField.superclass.constructor.call(this, config);
55850      
55851 };
55852
55853 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
55854     /**
55855      * @cfg {Number} width  - used to restrict width of grid..
55856      */
55857     width : 100,
55858     /**
55859      * @cfg {Number} height - used to restrict height of grid..
55860      */
55861     height : 50,
55862      /**
55863      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
55864          * 
55865          *}
55866      */
55867     xgrid : false, 
55868     /**
55869      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
55870      * {tag: "input", type: "checkbox", autocomplete: "off"})
55871      */
55872    // defaultAutoCreate : { tag: 'div' },
55873     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
55874     /**
55875      * @cfg {String} addTitle Text to include for adding a title.
55876      */
55877     addTitle : false,
55878     //
55879     onResize : function(){
55880         Roo.form.Field.superclass.onResize.apply(this, arguments);
55881     },
55882
55883     initEvents : function(){
55884         // Roo.form.Checkbox.superclass.initEvents.call(this);
55885         // has no events...
55886        
55887     },
55888
55889
55890     getResizeEl : function(){
55891         return this.wrap;
55892     },
55893
55894     getPositionEl : function(){
55895         return this.wrap;
55896     },
55897
55898     // private
55899     onRender : function(ct, position){
55900         
55901         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
55902         var style = this.style;
55903         delete this.style;
55904         
55905         Roo.form.GridField.superclass.onRender.call(this, ct, position);
55906         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
55907         this.viewEl = this.wrap.createChild({ tag: 'div' });
55908         if (style) {
55909             this.viewEl.applyStyles(style);
55910         }
55911         if (this.width) {
55912             this.viewEl.setWidth(this.width);
55913         }
55914         if (this.height) {
55915             this.viewEl.setHeight(this.height);
55916         }
55917         //if(this.inputValue !== undefined){
55918         //this.setValue(this.value);
55919         
55920         
55921         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
55922         
55923         
55924         this.grid.render();
55925         this.grid.getDataSource().on('remove', this.refreshValue, this);
55926         this.grid.getDataSource().on('update', this.refreshValue, this);
55927         this.grid.on('afteredit', this.refreshValue, this);
55928  
55929     },
55930      
55931     
55932     /**
55933      * Sets the value of the item. 
55934      * @param {String} either an object  or a string..
55935      */
55936     setValue : function(v){
55937         //this.value = v;
55938         v = v || []; // empty set..
55939         // this does not seem smart - it really only affects memoryproxy grids..
55940         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
55941             var ds = this.grid.getDataSource();
55942             // assumes a json reader..
55943             var data = {}
55944             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
55945             ds.loadData( data);
55946         }
55947         // clear selection so it does not get stale.
55948         if (this.grid.sm) { 
55949             this.grid.sm.clearSelections();
55950         }
55951         
55952         Roo.form.GridField.superclass.setValue.call(this, v);
55953         this.refreshValue();
55954         // should load data in the grid really....
55955     },
55956     
55957     // private
55958     refreshValue: function() {
55959          var val = [];
55960         this.grid.getDataSource().each(function(r) {
55961             val.push(r.data);
55962         });
55963         this.el.dom.value = Roo.encode(val);
55964     }
55965     
55966      
55967     
55968     
55969 });/*
55970  * Based on:
55971  * Ext JS Library 1.1.1
55972  * Copyright(c) 2006-2007, Ext JS, LLC.
55973  *
55974  * Originally Released Under LGPL - original licence link has changed is not relivant.
55975  *
55976  * Fork - LGPL
55977  * <script type="text/javascript">
55978  */
55979 /**
55980  * @class Roo.form.DisplayField
55981  * @extends Roo.form.Field
55982  * A generic Field to display non-editable data.
55983  * @cfg {Boolean} closable (true|false) default false
55984  * @constructor
55985  * Creates a new Display Field item.
55986  * @param {Object} config Configuration options
55987  */
55988 Roo.form.DisplayField = function(config){
55989     Roo.form.DisplayField.superclass.constructor.call(this, config);
55990     
55991     this.addEvents({
55992         /**
55993          * @event close
55994          * Fires after the click the close btn
55995              * @param {Roo.form.DisplayField} this
55996              */
55997         close : true
55998     });
55999 };
56000
56001 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
56002     inputType:      'hidden',
56003     allowBlank:     true,
56004     readOnly:         true,
56005     
56006  
56007     /**
56008      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56009      */
56010     focusClass : undefined,
56011     /**
56012      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56013      */
56014     fieldClass: 'x-form-field',
56015     
56016      /**
56017      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
56018      */
56019     valueRenderer: undefined,
56020     
56021     width: 100,
56022     /**
56023      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56024      * {tag: "input", type: "checkbox", autocomplete: "off"})
56025      */
56026      
56027  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
56028  
56029     closable : false,
56030     
56031     onResize : function(){
56032         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
56033         
56034     },
56035
56036     initEvents : function(){
56037         // Roo.form.Checkbox.superclass.initEvents.call(this);
56038         // has no events...
56039         
56040         if(this.closable){
56041             this.closeEl.on('click', this.onClose, this);
56042         }
56043        
56044     },
56045
56046
56047     getResizeEl : function(){
56048         return this.wrap;
56049     },
56050
56051     getPositionEl : function(){
56052         return this.wrap;
56053     },
56054
56055     // private
56056     onRender : function(ct, position){
56057         
56058         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
56059         //if(this.inputValue !== undefined){
56060         this.wrap = this.el.wrap();
56061         
56062         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
56063         
56064         if(this.closable){
56065             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
56066         }
56067         
56068         if (this.bodyStyle) {
56069             this.viewEl.applyStyles(this.bodyStyle);
56070         }
56071         //this.viewEl.setStyle('padding', '2px');
56072         
56073         this.setValue(this.value);
56074         
56075     },
56076 /*
56077     // private
56078     initValue : Roo.emptyFn,
56079
56080   */
56081
56082         // private
56083     onClick : function(){
56084         
56085     },
56086
56087     /**
56088      * Sets the checked state of the checkbox.
56089      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
56090      */
56091     setValue : function(v){
56092         this.value = v;
56093         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
56094         // this might be called before we have a dom element..
56095         if (!this.viewEl) {
56096             return;
56097         }
56098         this.viewEl.dom.innerHTML = html;
56099         Roo.form.DisplayField.superclass.setValue.call(this, v);
56100
56101     },
56102     
56103     onClose : function(e)
56104     {
56105         e.preventDefault();
56106         
56107         this.fireEvent('close', this);
56108     }
56109 });/*
56110  * 
56111  * Licence- LGPL
56112  * 
56113  */
56114
56115 /**
56116  * @class Roo.form.DayPicker
56117  * @extends Roo.form.Field
56118  * A Day picker show [M] [T] [W] ....
56119  * @constructor
56120  * Creates a new Day Picker
56121  * @param {Object} config Configuration options
56122  */
56123 Roo.form.DayPicker= function(config){
56124     Roo.form.DayPicker.superclass.constructor.call(this, config);
56125      
56126 };
56127
56128 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
56129     /**
56130      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56131      */
56132     focusClass : undefined,
56133     /**
56134      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56135      */
56136     fieldClass: "x-form-field",
56137    
56138     /**
56139      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56140      * {tag: "input", type: "checkbox", autocomplete: "off"})
56141      */
56142     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
56143     
56144    
56145     actionMode : 'viewEl', 
56146     //
56147     // private
56148  
56149     inputType : 'hidden',
56150     
56151      
56152     inputElement: false, // real input element?
56153     basedOn: false, // ????
56154     
56155     isFormField: true, // not sure where this is needed!!!!
56156
56157     onResize : function(){
56158         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
56159         if(!this.boxLabel){
56160             this.el.alignTo(this.wrap, 'c-c');
56161         }
56162     },
56163
56164     initEvents : function(){
56165         Roo.form.Checkbox.superclass.initEvents.call(this);
56166         this.el.on("click", this.onClick,  this);
56167         this.el.on("change", this.onClick,  this);
56168     },
56169
56170
56171     getResizeEl : function(){
56172         return this.wrap;
56173     },
56174
56175     getPositionEl : function(){
56176         return this.wrap;
56177     },
56178
56179     
56180     // private
56181     onRender : function(ct, position){
56182         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
56183        
56184         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
56185         
56186         var r1 = '<table><tr>';
56187         var r2 = '<tr class="x-form-daypick-icons">';
56188         for (var i=0; i < 7; i++) {
56189             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
56190             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
56191         }
56192         
56193         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
56194         viewEl.select('img').on('click', this.onClick, this);
56195         this.viewEl = viewEl;   
56196         
56197         
56198         // this will not work on Chrome!!!
56199         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
56200         this.el.on('propertychange', this.setFromHidden,  this);  //ie
56201         
56202         
56203           
56204
56205     },
56206
56207     // private
56208     initValue : Roo.emptyFn,
56209
56210     /**
56211      * Returns the checked state of the checkbox.
56212      * @return {Boolean} True if checked, else false
56213      */
56214     getValue : function(){
56215         return this.el.dom.value;
56216         
56217     },
56218
56219         // private
56220     onClick : function(e){ 
56221         //this.setChecked(!this.checked);
56222         Roo.get(e.target).toggleClass('x-menu-item-checked');
56223         this.refreshValue();
56224         //if(this.el.dom.checked != this.checked){
56225         //    this.setValue(this.el.dom.checked);
56226        // }
56227     },
56228     
56229     // private
56230     refreshValue : function()
56231     {
56232         var val = '';
56233         this.viewEl.select('img',true).each(function(e,i,n)  {
56234             val += e.is(".x-menu-item-checked") ? String(n) : '';
56235         });
56236         this.setValue(val, true);
56237     },
56238
56239     /**
56240      * Sets the checked state of the checkbox.
56241      * On is always based on a string comparison between inputValue and the param.
56242      * @param {Boolean/String} value - the value to set 
56243      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
56244      */
56245     setValue : function(v,suppressEvent){
56246         if (!this.el.dom) {
56247             return;
56248         }
56249         var old = this.el.dom.value ;
56250         this.el.dom.value = v;
56251         if (suppressEvent) {
56252             return ;
56253         }
56254          
56255         // update display..
56256         this.viewEl.select('img',true).each(function(e,i,n)  {
56257             
56258             var on = e.is(".x-menu-item-checked");
56259             var newv = v.indexOf(String(n)) > -1;
56260             if (on != newv) {
56261                 e.toggleClass('x-menu-item-checked');
56262             }
56263             
56264         });
56265         
56266         
56267         this.fireEvent('change', this, v, old);
56268         
56269         
56270     },
56271    
56272     // handle setting of hidden value by some other method!!?!?
56273     setFromHidden: function()
56274     {
56275         if(!this.el){
56276             return;
56277         }
56278         //console.log("SET FROM HIDDEN");
56279         //alert('setFrom hidden');
56280         this.setValue(this.el.dom.value);
56281     },
56282     
56283     onDestroy : function()
56284     {
56285         if(this.viewEl){
56286             Roo.get(this.viewEl).remove();
56287         }
56288          
56289         Roo.form.DayPicker.superclass.onDestroy.call(this);
56290     }
56291
56292 });/*
56293  * RooJS Library 1.1.1
56294  * Copyright(c) 2008-2011  Alan Knowles
56295  *
56296  * License - LGPL
56297  */
56298  
56299
56300 /**
56301  * @class Roo.form.ComboCheck
56302  * @extends Roo.form.ComboBox
56303  * A combobox for multiple select items.
56304  *
56305  * FIXME - could do with a reset button..
56306  * 
56307  * @constructor
56308  * Create a new ComboCheck
56309  * @param {Object} config Configuration options
56310  */
56311 Roo.form.ComboCheck = function(config){
56312     Roo.form.ComboCheck.superclass.constructor.call(this, config);
56313     // should verify some data...
56314     // like
56315     // hiddenName = required..
56316     // displayField = required
56317     // valudField == required
56318     var req= [ 'hiddenName', 'displayField', 'valueField' ];
56319     var _t = this;
56320     Roo.each(req, function(e) {
56321         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
56322             throw "Roo.form.ComboCheck : missing value for: " + e;
56323         }
56324     });
56325     
56326     
56327 };
56328
56329 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
56330      
56331      
56332     editable : false,
56333      
56334     selectedClass: 'x-menu-item-checked', 
56335     
56336     // private
56337     onRender : function(ct, position){
56338         var _t = this;
56339         
56340         
56341         
56342         if(!this.tpl){
56343             var cls = 'x-combo-list';
56344
56345             
56346             this.tpl =  new Roo.Template({
56347                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
56348                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
56349                    '<span>{' + this.displayField + '}</span>' +
56350                     '</div>' 
56351                 
56352             });
56353         }
56354  
56355         
56356         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
56357         this.view.singleSelect = false;
56358         this.view.multiSelect = true;
56359         this.view.toggleSelect = true;
56360         this.pageTb.add(new Roo.Toolbar.Fill(), {
56361             
56362             text: 'Done',
56363             handler: function()
56364             {
56365                 _t.collapse();
56366             }
56367         });
56368     },
56369     
56370     onViewOver : function(e, t){
56371         // do nothing...
56372         return;
56373         
56374     },
56375     
56376     onViewClick : function(doFocus,index){
56377         return;
56378         
56379     },
56380     select: function () {
56381         //Roo.log("SELECT CALLED");
56382     },
56383      
56384     selectByValue : function(xv, scrollIntoView){
56385         var ar = this.getValueArray();
56386         var sels = [];
56387         
56388         Roo.each(ar, function(v) {
56389             if(v === undefined || v === null){
56390                 return;
56391             }
56392             var r = this.findRecord(this.valueField, v);
56393             if(r){
56394                 sels.push(this.store.indexOf(r))
56395                 
56396             }
56397         },this);
56398         this.view.select(sels);
56399         return false;
56400     },
56401     
56402     
56403     
56404     onSelect : function(record, index){
56405        // Roo.log("onselect Called");
56406        // this is only called by the clear button now..
56407         this.view.clearSelections();
56408         this.setValue('[]');
56409         if (this.value != this.valueBefore) {
56410             this.fireEvent('change', this, this.value, this.valueBefore);
56411             this.valueBefore = this.value;
56412         }
56413     },
56414     getValueArray : function()
56415     {
56416         var ar = [] ;
56417         
56418         try {
56419             //Roo.log(this.value);
56420             if (typeof(this.value) == 'undefined') {
56421                 return [];
56422             }
56423             var ar = Roo.decode(this.value);
56424             return  ar instanceof Array ? ar : []; //?? valid?
56425             
56426         } catch(e) {
56427             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
56428             return [];
56429         }
56430          
56431     },
56432     expand : function ()
56433     {
56434         
56435         Roo.form.ComboCheck.superclass.expand.call(this);
56436         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
56437         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
56438         
56439
56440     },
56441     
56442     collapse : function(){
56443         Roo.form.ComboCheck.superclass.collapse.call(this);
56444         var sl = this.view.getSelectedIndexes();
56445         var st = this.store;
56446         var nv = [];
56447         var tv = [];
56448         var r;
56449         Roo.each(sl, function(i) {
56450             r = st.getAt(i);
56451             nv.push(r.get(this.valueField));
56452         },this);
56453         this.setValue(Roo.encode(nv));
56454         if (this.value != this.valueBefore) {
56455
56456             this.fireEvent('change', this, this.value, this.valueBefore);
56457             this.valueBefore = this.value;
56458         }
56459         
56460     },
56461     
56462     setValue : function(v){
56463         // Roo.log(v);
56464         this.value = v;
56465         
56466         var vals = this.getValueArray();
56467         var tv = [];
56468         Roo.each(vals, function(k) {
56469             var r = this.findRecord(this.valueField, k);
56470             if(r){
56471                 tv.push(r.data[this.displayField]);
56472             }else if(this.valueNotFoundText !== undefined){
56473                 tv.push( this.valueNotFoundText );
56474             }
56475         },this);
56476        // Roo.log(tv);
56477         
56478         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
56479         this.hiddenField.value = v;
56480         this.value = v;
56481     }
56482     
56483 });/*
56484  * Based on:
56485  * Ext JS Library 1.1.1
56486  * Copyright(c) 2006-2007, Ext JS, LLC.
56487  *
56488  * Originally Released Under LGPL - original licence link has changed is not relivant.
56489  *
56490  * Fork - LGPL
56491  * <script type="text/javascript">
56492  */
56493  
56494 /**
56495  * @class Roo.form.Signature
56496  * @extends Roo.form.Field
56497  * Signature field.  
56498  * @constructor
56499  * 
56500  * @param {Object} config Configuration options
56501  */
56502
56503 Roo.form.Signature = function(config){
56504     Roo.form.Signature.superclass.constructor.call(this, config);
56505     
56506     this.addEvents({// not in used??
56507          /**
56508          * @event confirm
56509          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
56510              * @param {Roo.form.Signature} combo This combo box
56511              */
56512         'confirm' : true,
56513         /**
56514          * @event reset
56515          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
56516              * @param {Roo.form.ComboBox} combo This combo box
56517              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
56518              */
56519         'reset' : true
56520     });
56521 };
56522
56523 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
56524     /**
56525      * @cfg {Object} labels Label to use when rendering a form.
56526      * defaults to 
56527      * labels : { 
56528      *      clear : "Clear",
56529      *      confirm : "Confirm"
56530      *  }
56531      */
56532     labels : { 
56533         clear : "Clear",
56534         confirm : "Confirm"
56535     },
56536     /**
56537      * @cfg {Number} width The signature panel width (defaults to 300)
56538      */
56539     width: 300,
56540     /**
56541      * @cfg {Number} height The signature panel height (defaults to 100)
56542      */
56543     height : 100,
56544     /**
56545      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
56546      */
56547     allowBlank : false,
56548     
56549     //private
56550     // {Object} signPanel The signature SVG panel element (defaults to {})
56551     signPanel : {},
56552     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
56553     isMouseDown : false,
56554     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
56555     isConfirmed : false,
56556     // {String} signatureTmp SVG mapping string (defaults to empty string)
56557     signatureTmp : '',
56558     
56559     
56560     defaultAutoCreate : { // modified by initCompnoent..
56561         tag: "input",
56562         type:"hidden"
56563     },
56564
56565     // private
56566     onRender : function(ct, position){
56567         
56568         Roo.form.Signature.superclass.onRender.call(this, ct, position);
56569         
56570         this.wrap = this.el.wrap({
56571             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
56572         });
56573         
56574         this.createToolbar(this);
56575         this.signPanel = this.wrap.createChild({
56576                 tag: 'div',
56577                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
56578             }, this.el
56579         );
56580             
56581         this.svgID = Roo.id();
56582         this.svgEl = this.signPanel.createChild({
56583               xmlns : 'http://www.w3.org/2000/svg',
56584               tag : 'svg',
56585               id : this.svgID + "-svg",
56586               width: this.width,
56587               height: this.height,
56588               viewBox: '0 0 '+this.width+' '+this.height,
56589               cn : [
56590                 {
56591                     tag: "rect",
56592                     id: this.svgID + "-svg-r",
56593                     width: this.width,
56594                     height: this.height,
56595                     fill: "#ffa"
56596                 },
56597                 {
56598                     tag: "line",
56599                     id: this.svgID + "-svg-l",
56600                     x1: "0", // start
56601                     y1: (this.height*0.8), // start set the line in 80% of height
56602                     x2: this.width, // end
56603                     y2: (this.height*0.8), // end set the line in 80% of height
56604                     'stroke': "#666",
56605                     'stroke-width': "1",
56606                     'stroke-dasharray': "3",
56607                     'shape-rendering': "crispEdges",
56608                     'pointer-events': "none"
56609                 },
56610                 {
56611                     tag: "path",
56612                     id: this.svgID + "-svg-p",
56613                     'stroke': "navy",
56614                     'stroke-width': "3",
56615                     'fill': "none",
56616                     'pointer-events': 'none'
56617                 }
56618               ]
56619         });
56620         this.createSVG();
56621         this.svgBox = this.svgEl.dom.getScreenCTM();
56622     },
56623     createSVG : function(){ 
56624         var svg = this.signPanel;
56625         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
56626         var t = this;
56627
56628         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
56629         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
56630         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
56631         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
56632         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
56633         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
56634         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
56635         
56636     },
56637     isTouchEvent : function(e){
56638         return e.type.match(/^touch/);
56639     },
56640     getCoords : function (e) {
56641         var pt    = this.svgEl.dom.createSVGPoint();
56642         pt.x = e.clientX; 
56643         pt.y = e.clientY;
56644         if (this.isTouchEvent(e)) {
56645             pt.x =  e.targetTouches[0].clientX;
56646             pt.y = e.targetTouches[0].clientY;
56647         }
56648         var a = this.svgEl.dom.getScreenCTM();
56649         var b = a.inverse();
56650         var mx = pt.matrixTransform(b);
56651         return mx.x + ',' + mx.y;
56652     },
56653     //mouse event headler 
56654     down : function (e) {
56655         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
56656         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
56657         
56658         this.isMouseDown = true;
56659         
56660         e.preventDefault();
56661     },
56662     move : function (e) {
56663         if (this.isMouseDown) {
56664             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
56665             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
56666         }
56667         
56668         e.preventDefault();
56669     },
56670     up : function (e) {
56671         this.isMouseDown = false;
56672         var sp = this.signatureTmp.split(' ');
56673         
56674         if(sp.length > 1){
56675             if(!sp[sp.length-2].match(/^L/)){
56676                 sp.pop();
56677                 sp.pop();
56678                 sp.push("");
56679                 this.signatureTmp = sp.join(" ");
56680             }
56681         }
56682         if(this.getValue() != this.signatureTmp){
56683             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56684             this.isConfirmed = false;
56685         }
56686         e.preventDefault();
56687     },
56688     
56689     /**
56690      * Protected method that will not generally be called directly. It
56691      * is called when the editor creates its toolbar. Override this method if you need to
56692      * add custom toolbar buttons.
56693      * @param {HtmlEditor} editor
56694      */
56695     createToolbar : function(editor){
56696          function btn(id, toggle, handler){
56697             var xid = fid + '-'+ id ;
56698             return {
56699                 id : xid,
56700                 cmd : id,
56701                 cls : 'x-btn-icon x-edit-'+id,
56702                 enableToggle:toggle !== false,
56703                 scope: editor, // was editor...
56704                 handler:handler||editor.relayBtnCmd,
56705                 clickEvent:'mousedown',
56706                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
56707                 tabIndex:-1
56708             };
56709         }
56710         
56711         
56712         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
56713         this.tb = tb;
56714         this.tb.add(
56715            {
56716                 cls : ' x-signature-btn x-signature-'+id,
56717                 scope: editor, // was editor...
56718                 handler: this.reset,
56719                 clickEvent:'mousedown',
56720                 text: this.labels.clear
56721             },
56722             {
56723                  xtype : 'Fill',
56724                  xns: Roo.Toolbar
56725             }, 
56726             {
56727                 cls : '  x-signature-btn x-signature-'+id,
56728                 scope: editor, // was editor...
56729                 handler: this.confirmHandler,
56730                 clickEvent:'mousedown',
56731                 text: this.labels.confirm
56732             }
56733         );
56734     
56735     },
56736     //public
56737     /**
56738      * when user is clicked confirm then show this image.....
56739      * 
56740      * @return {String} Image Data URI
56741      */
56742     getImageDataURI : function(){
56743         var svg = this.svgEl.dom.parentNode.innerHTML;
56744         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
56745         return src; 
56746     },
56747     /**
56748      * 
56749      * @return {Boolean} this.isConfirmed
56750      */
56751     getConfirmed : function(){
56752         return this.isConfirmed;
56753     },
56754     /**
56755      * 
56756      * @return {Number} this.width
56757      */
56758     getWidth : function(){
56759         return this.width;
56760     },
56761     /**
56762      * 
56763      * @return {Number} this.height
56764      */
56765     getHeight : function(){
56766         return this.height;
56767     },
56768     // private
56769     getSignature : function(){
56770         return this.signatureTmp;
56771     },
56772     // private
56773     reset : function(){
56774         this.signatureTmp = '';
56775         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56776         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
56777         this.isConfirmed = false;
56778         Roo.form.Signature.superclass.reset.call(this);
56779     },
56780     setSignature : function(s){
56781         this.signatureTmp = s;
56782         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56783         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
56784         this.setValue(s);
56785         this.isConfirmed = false;
56786         Roo.form.Signature.superclass.reset.call(this);
56787     }, 
56788     test : function(){
56789 //        Roo.log(this.signPanel.dom.contentWindow.up())
56790     },
56791     //private
56792     setConfirmed : function(){
56793         
56794         
56795         
56796 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
56797     },
56798     // private
56799     confirmHandler : function(){
56800         if(!this.getSignature()){
56801             return;
56802         }
56803         
56804         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
56805         this.setValue(this.getSignature());
56806         this.isConfirmed = true;
56807         
56808         this.fireEvent('confirm', this);
56809     },
56810     // private
56811     // Subclasses should provide the validation implementation by overriding this
56812     validateValue : function(value){
56813         if(this.allowBlank){
56814             return true;
56815         }
56816         
56817         if(this.isConfirmed){
56818             return true;
56819         }
56820         return false;
56821     }
56822 });/*
56823  * Based on:
56824  * Ext JS Library 1.1.1
56825  * Copyright(c) 2006-2007, Ext JS, LLC.
56826  *
56827  * Originally Released Under LGPL - original licence link has changed is not relivant.
56828  *
56829  * Fork - LGPL
56830  * <script type="text/javascript">
56831  */
56832  
56833
56834 /**
56835  * @class Roo.form.ComboBox
56836  * @extends Roo.form.TriggerField
56837  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
56838  * @constructor
56839  * Create a new ComboBox.
56840  * @param {Object} config Configuration options
56841  */
56842 Roo.form.Select = function(config){
56843     Roo.form.Select.superclass.constructor.call(this, config);
56844      
56845 };
56846
56847 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
56848     /**
56849      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
56850      */
56851     /**
56852      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
56853      * rendering into an Roo.Editor, defaults to false)
56854      */
56855     /**
56856      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
56857      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
56858      */
56859     /**
56860      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
56861      */
56862     /**
56863      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
56864      * the dropdown list (defaults to undefined, with no header element)
56865      */
56866
56867      /**
56868      * @cfg {String/Roo.Template} tpl The template to use to render the output
56869      */
56870      
56871     // private
56872     defaultAutoCreate : {tag: "select"  },
56873     /**
56874      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
56875      */
56876     listWidth: undefined,
56877     /**
56878      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
56879      * mode = 'remote' or 'text' if mode = 'local')
56880      */
56881     displayField: undefined,
56882     /**
56883      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
56884      * mode = 'remote' or 'value' if mode = 'local'). 
56885      * Note: use of a valueField requires the user make a selection
56886      * in order for a value to be mapped.
56887      */
56888     valueField: undefined,
56889     
56890     
56891     /**
56892      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
56893      * field's data value (defaults to the underlying DOM element's name)
56894      */
56895     hiddenName: undefined,
56896     /**
56897      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
56898      */
56899     listClass: '',
56900     /**
56901      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
56902      */
56903     selectedClass: 'x-combo-selected',
56904     /**
56905      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
56906      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
56907      * which displays a downward arrow icon).
56908      */
56909     triggerClass : 'x-form-arrow-trigger',
56910     /**
56911      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
56912      */
56913     shadow:'sides',
56914     /**
56915      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
56916      * anchor positions (defaults to 'tl-bl')
56917      */
56918     listAlign: 'tl-bl?',
56919     /**
56920      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
56921      */
56922     maxHeight: 300,
56923     /**
56924      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
56925      * query specified by the allQuery config option (defaults to 'query')
56926      */
56927     triggerAction: 'query',
56928     /**
56929      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
56930      * (defaults to 4, does not apply if editable = false)
56931      */
56932     minChars : 4,
56933     /**
56934      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
56935      * delay (typeAheadDelay) if it matches a known value (defaults to false)
56936      */
56937     typeAhead: false,
56938     /**
56939      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
56940      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
56941      */
56942     queryDelay: 500,
56943     /**
56944      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
56945      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
56946      */
56947     pageSize: 0,
56948     /**
56949      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
56950      * when editable = true (defaults to false)
56951      */
56952     selectOnFocus:false,
56953     /**
56954      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
56955      */
56956     queryParam: 'query',
56957     /**
56958      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
56959      * when mode = 'remote' (defaults to 'Loading...')
56960      */
56961     loadingText: 'Loading...',
56962     /**
56963      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
56964      */
56965     resizable: false,
56966     /**
56967      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
56968      */
56969     handleHeight : 8,
56970     /**
56971      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
56972      * traditional select (defaults to true)
56973      */
56974     editable: true,
56975     /**
56976      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
56977      */
56978     allQuery: '',
56979     /**
56980      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
56981      */
56982     mode: 'remote',
56983     /**
56984      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
56985      * listWidth has a higher value)
56986      */
56987     minListWidth : 70,
56988     /**
56989      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
56990      * allow the user to set arbitrary text into the field (defaults to false)
56991      */
56992     forceSelection:false,
56993     /**
56994      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
56995      * if typeAhead = true (defaults to 250)
56996      */
56997     typeAheadDelay : 250,
56998     /**
56999      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
57000      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
57001      */
57002     valueNotFoundText : undefined,
57003     
57004     /**
57005      * @cfg {String} defaultValue The value displayed after loading the store.
57006      */
57007     defaultValue: '',
57008     
57009     /**
57010      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
57011      */
57012     blockFocus : false,
57013     
57014     /**
57015      * @cfg {Boolean} disableClear Disable showing of clear button.
57016      */
57017     disableClear : false,
57018     /**
57019      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
57020      */
57021     alwaysQuery : false,
57022     
57023     //private
57024     addicon : false,
57025     editicon: false,
57026     
57027     // element that contains real text value.. (when hidden is used..)
57028      
57029     // private
57030     onRender : function(ct, position){
57031         Roo.form.Field.prototype.onRender.call(this, ct, position);
57032         
57033         if(this.store){
57034             this.store.on('beforeload', this.onBeforeLoad, this);
57035             this.store.on('load', this.onLoad, this);
57036             this.store.on('loadexception', this.onLoadException, this);
57037             this.store.load({});
57038         }
57039         
57040         
57041         
57042     },
57043
57044     // private
57045     initEvents : function(){
57046         //Roo.form.ComboBox.superclass.initEvents.call(this);
57047  
57048     },
57049
57050     onDestroy : function(){
57051        
57052         if(this.store){
57053             this.store.un('beforeload', this.onBeforeLoad, this);
57054             this.store.un('load', this.onLoad, this);
57055             this.store.un('loadexception', this.onLoadException, this);
57056         }
57057         //Roo.form.ComboBox.superclass.onDestroy.call(this);
57058     },
57059
57060     // private
57061     fireKey : function(e){
57062         if(e.isNavKeyPress() && !this.list.isVisible()){
57063             this.fireEvent("specialkey", this, e);
57064         }
57065     },
57066
57067     // private
57068     onResize: function(w, h){
57069         
57070         return; 
57071     
57072         
57073     },
57074
57075     /**
57076      * Allow or prevent the user from directly editing the field text.  If false is passed,
57077      * the user will only be able to select from the items defined in the dropdown list.  This method
57078      * is the runtime equivalent of setting the 'editable' config option at config time.
57079      * @param {Boolean} value True to allow the user to directly edit the field text
57080      */
57081     setEditable : function(value){
57082          
57083     },
57084
57085     // private
57086     onBeforeLoad : function(){
57087         
57088         Roo.log("Select before load");
57089         return;
57090     
57091         this.innerList.update(this.loadingText ?
57092                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
57093         //this.restrictHeight();
57094         this.selectedIndex = -1;
57095     },
57096
57097     // private
57098     onLoad : function(){
57099
57100     
57101         var dom = this.el.dom;
57102         dom.innerHTML = '';
57103          var od = dom.ownerDocument;
57104          
57105         if (this.emptyText) {
57106             var op = od.createElement('option');
57107             op.setAttribute('value', '');
57108             op.innerHTML = String.format('{0}', this.emptyText);
57109             dom.appendChild(op);
57110         }
57111         if(this.store.getCount() > 0){
57112            
57113             var vf = this.valueField;
57114             var df = this.displayField;
57115             this.store.data.each(function(r) {
57116                 // which colmsn to use... testing - cdoe / title..
57117                 var op = od.createElement('option');
57118                 op.setAttribute('value', r.data[vf]);
57119                 op.innerHTML = String.format('{0}', r.data[df]);
57120                 dom.appendChild(op);
57121             });
57122             if (typeof(this.defaultValue != 'undefined')) {
57123                 this.setValue(this.defaultValue);
57124             }
57125             
57126              
57127         }else{
57128             //this.onEmptyResults();
57129         }
57130         //this.el.focus();
57131     },
57132     // private
57133     onLoadException : function()
57134     {
57135         dom.innerHTML = '';
57136             
57137         Roo.log("Select on load exception");
57138         return;
57139     
57140         this.collapse();
57141         Roo.log(this.store.reader.jsonData);
57142         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57143             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57144         }
57145         
57146         
57147     },
57148     // private
57149     onTypeAhead : function(){
57150          
57151     },
57152
57153     // private
57154     onSelect : function(record, index){
57155         Roo.log('on select?');
57156         return;
57157         if(this.fireEvent('beforeselect', this, record, index) !== false){
57158             this.setFromData(index > -1 ? record.data : false);
57159             this.collapse();
57160             this.fireEvent('select', this, record, index);
57161         }
57162     },
57163
57164     /**
57165      * Returns the currently selected field value or empty string if no value is set.
57166      * @return {String} value The selected value
57167      */
57168     getValue : function(){
57169         var dom = this.el.dom;
57170         this.value = dom.options[dom.selectedIndex].value;
57171         return this.value;
57172         
57173     },
57174
57175     /**
57176      * Clears any text/value currently set in the field
57177      */
57178     clearValue : function(){
57179         this.value = '';
57180         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
57181         
57182     },
57183
57184     /**
57185      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
57186      * will be displayed in the field.  If the value does not match the data value of an existing item,
57187      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
57188      * Otherwise the field will be blank (although the value will still be set).
57189      * @param {String} value The value to match
57190      */
57191     setValue : function(v){
57192         var d = this.el.dom;
57193         for (var i =0; i < d.options.length;i++) {
57194             if (v == d.options[i].value) {
57195                 d.selectedIndex = i;
57196                 this.value = v;
57197                 return;
57198             }
57199         }
57200         this.clearValue();
57201     },
57202     /**
57203      * @property {Object} the last set data for the element
57204      */
57205     
57206     lastData : false,
57207     /**
57208      * Sets the value of the field based on a object which is related to the record format for the store.
57209      * @param {Object} value the value to set as. or false on reset?
57210      */
57211     setFromData : function(o){
57212         Roo.log('setfrom data?');
57213          
57214         
57215         
57216     },
57217     // private
57218     reset : function(){
57219         this.clearValue();
57220     },
57221     // private
57222     findRecord : function(prop, value){
57223         
57224         return false;
57225     
57226         var record;
57227         if(this.store.getCount() > 0){
57228             this.store.each(function(r){
57229                 if(r.data[prop] == value){
57230                     record = r;
57231                     return false;
57232                 }
57233                 return true;
57234             });
57235         }
57236         return record;
57237     },
57238     
57239     getName: function()
57240     {
57241         // returns hidden if it's set..
57242         if (!this.rendered) {return ''};
57243         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
57244         
57245     },
57246      
57247
57248     
57249
57250     // private
57251     onEmptyResults : function(){
57252         Roo.log('empty results');
57253         //this.collapse();
57254     },
57255
57256     /**
57257      * Returns true if the dropdown list is expanded, else false.
57258      */
57259     isExpanded : function(){
57260         return false;
57261     },
57262
57263     /**
57264      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
57265      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57266      * @param {String} value The data value of the item to select
57267      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57268      * selected item if it is not currently in view (defaults to true)
57269      * @return {Boolean} True if the value matched an item in the list, else false
57270      */
57271     selectByValue : function(v, scrollIntoView){
57272         Roo.log('select By Value');
57273         return false;
57274     
57275         if(v !== undefined && v !== null){
57276             var r = this.findRecord(this.valueField || this.displayField, v);
57277             if(r){
57278                 this.select(this.store.indexOf(r), scrollIntoView);
57279                 return true;
57280             }
57281         }
57282         return false;
57283     },
57284
57285     /**
57286      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
57287      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57288      * @param {Number} index The zero-based index of the list item to select
57289      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57290      * selected item if it is not currently in view (defaults to true)
57291      */
57292     select : function(index, scrollIntoView){
57293         Roo.log('select ');
57294         return  ;
57295         
57296         this.selectedIndex = index;
57297         this.view.select(index);
57298         if(scrollIntoView !== false){
57299             var el = this.view.getNode(index);
57300             if(el){
57301                 this.innerList.scrollChildIntoView(el, false);
57302             }
57303         }
57304     },
57305
57306       
57307
57308     // private
57309     validateBlur : function(){
57310         
57311         return;
57312         
57313     },
57314
57315     // private
57316     initQuery : function(){
57317         this.doQuery(this.getRawValue());
57318     },
57319
57320     // private
57321     doForce : function(){
57322         if(this.el.dom.value.length > 0){
57323             this.el.dom.value =
57324                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
57325              
57326         }
57327     },
57328
57329     /**
57330      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
57331      * query allowing the query action to be canceled if needed.
57332      * @param {String} query The SQL query to execute
57333      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
57334      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
57335      * saved in the current store (defaults to false)
57336      */
57337     doQuery : function(q, forceAll){
57338         
57339         Roo.log('doQuery?');
57340         if(q === undefined || q === null){
57341             q = '';
57342         }
57343         var qe = {
57344             query: q,
57345             forceAll: forceAll,
57346             combo: this,
57347             cancel:false
57348         };
57349         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
57350             return false;
57351         }
57352         q = qe.query;
57353         forceAll = qe.forceAll;
57354         if(forceAll === true || (q.length >= this.minChars)){
57355             if(this.lastQuery != q || this.alwaysQuery){
57356                 this.lastQuery = q;
57357                 if(this.mode == 'local'){
57358                     this.selectedIndex = -1;
57359                     if(forceAll){
57360                         this.store.clearFilter();
57361                     }else{
57362                         this.store.filter(this.displayField, q);
57363                     }
57364                     this.onLoad();
57365                 }else{
57366                     this.store.baseParams[this.queryParam] = q;
57367                     this.store.load({
57368                         params: this.getParams(q)
57369                     });
57370                     this.expand();
57371                 }
57372             }else{
57373                 this.selectedIndex = -1;
57374                 this.onLoad();   
57375             }
57376         }
57377     },
57378
57379     // private
57380     getParams : function(q){
57381         var p = {};
57382         //p[this.queryParam] = q;
57383         if(this.pageSize){
57384             p.start = 0;
57385             p.limit = this.pageSize;
57386         }
57387         return p;
57388     },
57389
57390     /**
57391      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
57392      */
57393     collapse : function(){
57394         
57395     },
57396
57397     // private
57398     collapseIf : function(e){
57399         
57400     },
57401
57402     /**
57403      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
57404      */
57405     expand : function(){
57406         
57407     } ,
57408
57409     // private
57410      
57411
57412     /** 
57413     * @cfg {Boolean} grow 
57414     * @hide 
57415     */
57416     /** 
57417     * @cfg {Number} growMin 
57418     * @hide 
57419     */
57420     /** 
57421     * @cfg {Number} growMax 
57422     * @hide 
57423     */
57424     /**
57425      * @hide
57426      * @method autoSize
57427      */
57428     
57429     setWidth : function()
57430     {
57431         
57432     },
57433     getResizeEl : function(){
57434         return this.el;
57435     }
57436 });//<script type="text/javasscript">
57437  
57438
57439 /**
57440  * @class Roo.DDView
57441  * A DnD enabled version of Roo.View.
57442  * @param {Element/String} container The Element in which to create the View.
57443  * @param {String} tpl The template string used to create the markup for each element of the View
57444  * @param {Object} config The configuration properties. These include all the config options of
57445  * {@link Roo.View} plus some specific to this class.<br>
57446  * <p>
57447  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
57448  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
57449  * <p>
57450  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
57451 .x-view-drag-insert-above {
57452         border-top:1px dotted #3366cc;
57453 }
57454 .x-view-drag-insert-below {
57455         border-bottom:1px dotted #3366cc;
57456 }
57457 </code></pre>
57458  * 
57459  */
57460  
57461 Roo.DDView = function(container, tpl, config) {
57462     Roo.DDView.superclass.constructor.apply(this, arguments);
57463     this.getEl().setStyle("outline", "0px none");
57464     this.getEl().unselectable();
57465     if (this.dragGroup) {
57466         this.setDraggable(this.dragGroup.split(","));
57467     }
57468     if (this.dropGroup) {
57469         this.setDroppable(this.dropGroup.split(","));
57470     }
57471     if (this.deletable) {
57472         this.setDeletable();
57473     }
57474     this.isDirtyFlag = false;
57475         this.addEvents({
57476                 "drop" : true
57477         });
57478 };
57479
57480 Roo.extend(Roo.DDView, Roo.View, {
57481 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
57482 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
57483 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
57484 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
57485
57486         isFormField: true,
57487
57488         reset: Roo.emptyFn,
57489         
57490         clearInvalid: Roo.form.Field.prototype.clearInvalid,
57491
57492         validate: function() {
57493                 return true;
57494         },
57495         
57496         destroy: function() {
57497                 this.purgeListeners();
57498                 this.getEl.removeAllListeners();
57499                 this.getEl().remove();
57500                 if (this.dragZone) {
57501                         if (this.dragZone.destroy) {
57502                                 this.dragZone.destroy();
57503                         }
57504                 }
57505                 if (this.dropZone) {
57506                         if (this.dropZone.destroy) {
57507                                 this.dropZone.destroy();
57508                         }
57509                 }
57510         },
57511
57512 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
57513         getName: function() {
57514                 return this.name;
57515         },
57516
57517 /**     Loads the View from a JSON string representing the Records to put into the Store. */
57518         setValue: function(v) {
57519                 if (!this.store) {
57520                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
57521                 }
57522                 var data = {};
57523                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
57524                 this.store.proxy = new Roo.data.MemoryProxy(data);
57525                 this.store.load();
57526         },
57527
57528 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
57529         getValue: function() {
57530                 var result = '(';
57531                 this.store.each(function(rec) {
57532                         result += rec.id + ',';
57533                 });
57534                 return result.substr(0, result.length - 1) + ')';
57535         },
57536         
57537         getIds: function() {
57538                 var i = 0, result = new Array(this.store.getCount());
57539                 this.store.each(function(rec) {
57540                         result[i++] = rec.id;
57541                 });
57542                 return result;
57543         },
57544         
57545         isDirty: function() {
57546                 return this.isDirtyFlag;
57547         },
57548
57549 /**
57550  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
57551  *      whole Element becomes the target, and this causes the drop gesture to append.
57552  */
57553     getTargetFromEvent : function(e) {
57554                 var target = e.getTarget();
57555                 while ((target !== null) && (target.parentNode != this.el.dom)) {
57556                 target = target.parentNode;
57557                 }
57558                 if (!target) {
57559                         target = this.el.dom.lastChild || this.el.dom;
57560                 }
57561                 return target;
57562     },
57563
57564 /**
57565  *      Create the drag data which consists of an object which has the property "ddel" as
57566  *      the drag proxy element. 
57567  */
57568     getDragData : function(e) {
57569         var target = this.findItemFromChild(e.getTarget());
57570                 if(target) {
57571                         this.handleSelection(e);
57572                         var selNodes = this.getSelectedNodes();
57573             var dragData = {
57574                 source: this,
57575                 copy: this.copy || (this.allowCopy && e.ctrlKey),
57576                 nodes: selNodes,
57577                 records: []
57578                         };
57579                         var selectedIndices = this.getSelectedIndexes();
57580                         for (var i = 0; i < selectedIndices.length; i++) {
57581                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
57582                         }
57583                         if (selNodes.length == 1) {
57584                                 dragData.ddel = target.cloneNode(true); // the div element
57585                         } else {
57586                                 var div = document.createElement('div'); // create the multi element drag "ghost"
57587                                 div.className = 'multi-proxy';
57588                                 for (var i = 0, len = selNodes.length; i < len; i++) {
57589                                         div.appendChild(selNodes[i].cloneNode(true));
57590                                 }
57591                                 dragData.ddel = div;
57592                         }
57593             //console.log(dragData)
57594             //console.log(dragData.ddel.innerHTML)
57595                         return dragData;
57596                 }
57597         //console.log('nodragData')
57598                 return false;
57599     },
57600     
57601 /**     Specify to which ddGroup items in this DDView may be dragged. */
57602     setDraggable: function(ddGroup) {
57603         if (ddGroup instanceof Array) {
57604                 Roo.each(ddGroup, this.setDraggable, this);
57605                 return;
57606         }
57607         if (this.dragZone) {
57608                 this.dragZone.addToGroup(ddGroup);
57609         } else {
57610                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
57611                                 containerScroll: true,
57612                                 ddGroup: ddGroup 
57613
57614                         });
57615 //                      Draggability implies selection. DragZone's mousedown selects the element.
57616                         if (!this.multiSelect) { this.singleSelect = true; }
57617
57618 //                      Wire the DragZone's handlers up to methods in *this*
57619                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
57620                 }
57621     },
57622
57623 /**     Specify from which ddGroup this DDView accepts drops. */
57624     setDroppable: function(ddGroup) {
57625         if (ddGroup instanceof Array) {
57626                 Roo.each(ddGroup, this.setDroppable, this);
57627                 return;
57628         }
57629         if (this.dropZone) {
57630                 this.dropZone.addToGroup(ddGroup);
57631         } else {
57632                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
57633                                 containerScroll: true,
57634                                 ddGroup: ddGroup
57635                         });
57636
57637 //                      Wire the DropZone's handlers up to methods in *this*
57638                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
57639                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
57640                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
57641                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
57642                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
57643                 }
57644     },
57645
57646 /**     Decide whether to drop above or below a View node. */
57647     getDropPoint : function(e, n, dd){
57648         if (n == this.el.dom) { return "above"; }
57649                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
57650                 var c = t + (b - t) / 2;
57651                 var y = Roo.lib.Event.getPageY(e);
57652                 if(y <= c) {
57653                         return "above";
57654                 }else{
57655                         return "below";
57656                 }
57657     },
57658
57659     onNodeEnter : function(n, dd, e, data){
57660                 return false;
57661     },
57662     
57663     onNodeOver : function(n, dd, e, data){
57664                 var pt = this.getDropPoint(e, n, dd);
57665                 // set the insert point style on the target node
57666                 var dragElClass = this.dropNotAllowed;
57667                 if (pt) {
57668                         var targetElClass;
57669                         if (pt == "above"){
57670                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
57671                                 targetElClass = "x-view-drag-insert-above";
57672                         } else {
57673                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
57674                                 targetElClass = "x-view-drag-insert-below";
57675                         }
57676                         if (this.lastInsertClass != targetElClass){
57677                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
57678                                 this.lastInsertClass = targetElClass;
57679                         }
57680                 }
57681                 return dragElClass;
57682         },
57683
57684     onNodeOut : function(n, dd, e, data){
57685                 this.removeDropIndicators(n);
57686     },
57687
57688     onNodeDrop : function(n, dd, e, data){
57689         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
57690                 return false;
57691         }
57692         var pt = this.getDropPoint(e, n, dd);
57693                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
57694                 if (pt == "below") { insertAt++; }
57695                 for (var i = 0; i < data.records.length; i++) {
57696                         var r = data.records[i];
57697                         var dup = this.store.getById(r.id);
57698                         if (dup && (dd != this.dragZone)) {
57699                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
57700                         } else {
57701                                 if (data.copy) {
57702                                         this.store.insert(insertAt++, r.copy());
57703                                 } else {
57704                                         data.source.isDirtyFlag = true;
57705                                         r.store.remove(r);
57706                                         this.store.insert(insertAt++, r);
57707                                 }
57708                                 this.isDirtyFlag = true;
57709                         }
57710                 }
57711                 this.dragZone.cachedTarget = null;
57712                 return true;
57713     },
57714
57715     removeDropIndicators : function(n){
57716                 if(n){
57717                         Roo.fly(n).removeClass([
57718                                 "x-view-drag-insert-above",
57719                                 "x-view-drag-insert-below"]);
57720                         this.lastInsertClass = "_noclass";
57721                 }
57722     },
57723
57724 /**
57725  *      Utility method. Add a delete option to the DDView's context menu.
57726  *      @param {String} imageUrl The URL of the "delete" icon image.
57727  */
57728         setDeletable: function(imageUrl) {
57729                 if (!this.singleSelect && !this.multiSelect) {
57730                         this.singleSelect = true;
57731                 }
57732                 var c = this.getContextMenu();
57733                 this.contextMenu.on("itemclick", function(item) {
57734                         switch (item.id) {
57735                                 case "delete":
57736                                         this.remove(this.getSelectedIndexes());
57737                                         break;
57738                         }
57739                 }, this);
57740                 this.contextMenu.add({
57741                         icon: imageUrl,
57742                         id: "delete",
57743                         text: 'Delete'
57744                 });
57745         },
57746         
57747 /**     Return the context menu for this DDView. */
57748         getContextMenu: function() {
57749                 if (!this.contextMenu) {
57750 //                      Create the View's context menu
57751                         this.contextMenu = new Roo.menu.Menu({
57752                                 id: this.id + "-contextmenu"
57753                         });
57754                         this.el.on("contextmenu", this.showContextMenu, this);
57755                 }
57756                 return this.contextMenu;
57757         },
57758         
57759         disableContextMenu: function() {
57760                 if (this.contextMenu) {
57761                         this.el.un("contextmenu", this.showContextMenu, this);
57762                 }
57763         },
57764
57765         showContextMenu: function(e, item) {
57766         item = this.findItemFromChild(e.getTarget());
57767                 if (item) {
57768                         e.stopEvent();
57769                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
57770                         this.contextMenu.showAt(e.getXY());
57771             }
57772     },
57773
57774 /**
57775  *      Remove {@link Roo.data.Record}s at the specified indices.
57776  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
57777  */
57778     remove: function(selectedIndices) {
57779                 selectedIndices = [].concat(selectedIndices);
57780                 for (var i = 0; i < selectedIndices.length; i++) {
57781                         var rec = this.store.getAt(selectedIndices[i]);
57782                         this.store.remove(rec);
57783                 }
57784     },
57785
57786 /**
57787  *      Double click fires the event, but also, if this is draggable, and there is only one other
57788  *      related DropZone, it transfers the selected node.
57789  */
57790     onDblClick : function(e){
57791         var item = this.findItemFromChild(e.getTarget());
57792         if(item){
57793             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
57794                 return false;
57795             }
57796             if (this.dragGroup) {
57797                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
57798                     while (targets.indexOf(this.dropZone) > -1) {
57799                             targets.remove(this.dropZone);
57800                                 }
57801                     if (targets.length == 1) {
57802                                         this.dragZone.cachedTarget = null;
57803                         var el = Roo.get(targets[0].getEl());
57804                         var box = el.getBox(true);
57805                         targets[0].onNodeDrop(el.dom, {
57806                                 target: el.dom,
57807                                 xy: [box.x, box.y + box.height - 1]
57808                         }, null, this.getDragData(e));
57809                     }
57810                 }
57811         }
57812     },
57813     
57814     handleSelection: function(e) {
57815                 this.dragZone.cachedTarget = null;
57816         var item = this.findItemFromChild(e.getTarget());
57817         if (!item) {
57818                 this.clearSelections(true);
57819                 return;
57820         }
57821                 if (item && (this.multiSelect || this.singleSelect)){
57822                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
57823                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
57824                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
57825                                 this.unselect(item);
57826                         } else {
57827                                 this.select(item, this.multiSelect && e.ctrlKey);
57828                                 this.lastSelection = item;
57829                         }
57830                 }
57831     },
57832
57833     onItemClick : function(item, index, e){
57834                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
57835                         return false;
57836                 }
57837                 return true;
57838     },
57839
57840     unselect : function(nodeInfo, suppressEvent){
57841                 var node = this.getNode(nodeInfo);
57842                 if(node && this.isSelected(node)){
57843                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
57844                                 Roo.fly(node).removeClass(this.selectedClass);
57845                                 this.selections.remove(node);
57846                                 if(!suppressEvent){
57847                                         this.fireEvent("selectionchange", this, this.selections);
57848                                 }
57849                         }
57850                 }
57851     }
57852 });
57853 /*
57854  * Based on:
57855  * Ext JS Library 1.1.1
57856  * Copyright(c) 2006-2007, Ext JS, LLC.
57857  *
57858  * Originally Released Under LGPL - original licence link has changed is not relivant.
57859  *
57860  * Fork - LGPL
57861  * <script type="text/javascript">
57862  */
57863  
57864 /**
57865  * @class Roo.LayoutManager
57866  * @extends Roo.util.Observable
57867  * Base class for layout managers.
57868  */
57869 Roo.LayoutManager = function(container, config){
57870     Roo.LayoutManager.superclass.constructor.call(this);
57871     this.el = Roo.get(container);
57872     // ie scrollbar fix
57873     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
57874         document.body.scroll = "no";
57875     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
57876         this.el.position('relative');
57877     }
57878     this.id = this.el.id;
57879     this.el.addClass("x-layout-container");
57880     /** false to disable window resize monitoring @type Boolean */
57881     this.monitorWindowResize = true;
57882     this.regions = {};
57883     this.addEvents({
57884         /**
57885          * @event layout
57886          * Fires when a layout is performed. 
57887          * @param {Roo.LayoutManager} this
57888          */
57889         "layout" : true,
57890         /**
57891          * @event regionresized
57892          * Fires when the user resizes a region. 
57893          * @param {Roo.LayoutRegion} region The resized region
57894          * @param {Number} newSize The new size (width for east/west, height for north/south)
57895          */
57896         "regionresized" : true,
57897         /**
57898          * @event regioncollapsed
57899          * Fires when a region is collapsed. 
57900          * @param {Roo.LayoutRegion} region The collapsed region
57901          */
57902         "regioncollapsed" : true,
57903         /**
57904          * @event regionexpanded
57905          * Fires when a region is expanded.  
57906          * @param {Roo.LayoutRegion} region The expanded region
57907          */
57908         "regionexpanded" : true
57909     });
57910     this.updating = false;
57911     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57912 };
57913
57914 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
57915     /**
57916      * Returns true if this layout is currently being updated
57917      * @return {Boolean}
57918      */
57919     isUpdating : function(){
57920         return this.updating; 
57921     },
57922     
57923     /**
57924      * Suspend the LayoutManager from doing auto-layouts while
57925      * making multiple add or remove calls
57926      */
57927     beginUpdate : function(){
57928         this.updating = true;    
57929     },
57930     
57931     /**
57932      * Restore auto-layouts and optionally disable the manager from performing a layout
57933      * @param {Boolean} noLayout true to disable a layout update 
57934      */
57935     endUpdate : function(noLayout){
57936         this.updating = false;
57937         if(!noLayout){
57938             this.layout();
57939         }    
57940     },
57941     
57942     layout: function(){
57943         
57944     },
57945     
57946     onRegionResized : function(region, newSize){
57947         this.fireEvent("regionresized", region, newSize);
57948         this.layout();
57949     },
57950     
57951     onRegionCollapsed : function(region){
57952         this.fireEvent("regioncollapsed", region);
57953     },
57954     
57955     onRegionExpanded : function(region){
57956         this.fireEvent("regionexpanded", region);
57957     },
57958         
57959     /**
57960      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
57961      * performs box-model adjustments.
57962      * @return {Object} The size as an object {width: (the width), height: (the height)}
57963      */
57964     getViewSize : function(){
57965         var size;
57966         if(this.el.dom != document.body){
57967             size = this.el.getSize();
57968         }else{
57969             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
57970         }
57971         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
57972         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
57973         return size;
57974     },
57975     
57976     /**
57977      * Returns the Element this layout is bound to.
57978      * @return {Roo.Element}
57979      */
57980     getEl : function(){
57981         return this.el;
57982     },
57983     
57984     /**
57985      * Returns the specified region.
57986      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
57987      * @return {Roo.LayoutRegion}
57988      */
57989     getRegion : function(target){
57990         return this.regions[target.toLowerCase()];
57991     },
57992     
57993     onWindowResize : function(){
57994         if(this.monitorWindowResize){
57995             this.layout();
57996         }
57997     }
57998 });/*
57999  * Based on:
58000  * Ext JS Library 1.1.1
58001  * Copyright(c) 2006-2007, Ext JS, LLC.
58002  *
58003  * Originally Released Under LGPL - original licence link has changed is not relivant.
58004  *
58005  * Fork - LGPL
58006  * <script type="text/javascript">
58007  */
58008 /**
58009  * @class Roo.BorderLayout
58010  * @extends Roo.LayoutManager
58011  * @children Roo.ContentPanel
58012  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
58013  * please see: <br><br>
58014  * <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>
58015  * <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>
58016  * Example:
58017  <pre><code>
58018  var layout = new Roo.BorderLayout(document.body, {
58019     north: {
58020         initialSize: 25,
58021         titlebar: false
58022     },
58023     west: {
58024         split:true,
58025         initialSize: 200,
58026         minSize: 175,
58027         maxSize: 400,
58028         titlebar: true,
58029         collapsible: true
58030     },
58031     east: {
58032         split:true,
58033         initialSize: 202,
58034         minSize: 175,
58035         maxSize: 400,
58036         titlebar: true,
58037         collapsible: true
58038     },
58039     south: {
58040         split:true,
58041         initialSize: 100,
58042         minSize: 100,
58043         maxSize: 200,
58044         titlebar: true,
58045         collapsible: true
58046     },
58047     center: {
58048         titlebar: true,
58049         autoScroll:true,
58050         resizeTabs: true,
58051         minTabWidth: 50,
58052         preferredTabWidth: 150
58053     }
58054 });
58055
58056 // shorthand
58057 var CP = Roo.ContentPanel;
58058
58059 layout.beginUpdate();
58060 layout.add("north", new CP("north", "North"));
58061 layout.add("south", new CP("south", {title: "South", closable: true}));
58062 layout.add("west", new CP("west", {title: "West"}));
58063 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
58064 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
58065 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
58066 layout.getRegion("center").showPanel("center1");
58067 layout.endUpdate();
58068 </code></pre>
58069
58070 <b>The container the layout is rendered into can be either the body element or any other element.
58071 If it is not the body element, the container needs to either be an absolute positioned element,
58072 or you will need to add "position:relative" to the css of the container.  You will also need to specify
58073 the container size if it is not the body element.</b>
58074
58075 * @constructor
58076 * Create a new BorderLayout
58077 * @param {String/HTMLElement/Element} container The container this layout is bound to
58078 * @param {Object} config Configuration options
58079  */
58080 Roo.BorderLayout = function(container, config){
58081     config = config || {};
58082     Roo.BorderLayout.superclass.constructor.call(this, container, config);
58083     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
58084     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
58085         var target = this.factory.validRegions[i];
58086         if(config[target]){
58087             this.addRegion(target, config[target]);
58088         }
58089     }
58090 };
58091
58092 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
58093         
58094         /**
58095          * @cfg {Roo.LayoutRegion} east
58096          */
58097         /**
58098          * @cfg {Roo.LayoutRegion} west
58099          */
58100         /**
58101          * @cfg {Roo.LayoutRegion} north
58102          */
58103         /**
58104          * @cfg {Roo.LayoutRegion} south
58105          */
58106         /**
58107          * @cfg {Roo.LayoutRegion} center
58108          */
58109     /**
58110      * Creates and adds a new region if it doesn't already exist.
58111      * @param {String} target The target region key (north, south, east, west or center).
58112      * @param {Object} config The regions config object
58113      * @return {BorderLayoutRegion} The new region
58114      */
58115     addRegion : function(target, config){
58116         if(!this.regions[target]){
58117             var r = this.factory.create(target, this, config);
58118             this.bindRegion(target, r);
58119         }
58120         return this.regions[target];
58121     },
58122
58123     // private (kinda)
58124     bindRegion : function(name, r){
58125         this.regions[name] = r;
58126         r.on("visibilitychange", this.layout, this);
58127         r.on("paneladded", this.layout, this);
58128         r.on("panelremoved", this.layout, this);
58129         r.on("invalidated", this.layout, this);
58130         r.on("resized", this.onRegionResized, this);
58131         r.on("collapsed", this.onRegionCollapsed, this);
58132         r.on("expanded", this.onRegionExpanded, this);
58133     },
58134
58135     /**
58136      * Performs a layout update.
58137      */
58138     layout : function(){
58139         if(this.updating) {
58140             return;
58141         }
58142         var size = this.getViewSize();
58143         var w = size.width;
58144         var h = size.height;
58145         var centerW = w;
58146         var centerH = h;
58147         var centerY = 0;
58148         var centerX = 0;
58149         //var x = 0, y = 0;
58150
58151         var rs = this.regions;
58152         var north = rs["north"];
58153         var south = rs["south"]; 
58154         var west = rs["west"];
58155         var east = rs["east"];
58156         var center = rs["center"];
58157         //if(this.hideOnLayout){ // not supported anymore
58158             //c.el.setStyle("display", "none");
58159         //}
58160         if(north && north.isVisible()){
58161             var b = north.getBox();
58162             var m = north.getMargins();
58163             b.width = w - (m.left+m.right);
58164             b.x = m.left;
58165             b.y = m.top;
58166             centerY = b.height + b.y + m.bottom;
58167             centerH -= centerY;
58168             north.updateBox(this.safeBox(b));
58169         }
58170         if(south && south.isVisible()){
58171             var b = south.getBox();
58172             var m = south.getMargins();
58173             b.width = w - (m.left+m.right);
58174             b.x = m.left;
58175             var totalHeight = (b.height + m.top + m.bottom);
58176             b.y = h - totalHeight + m.top;
58177             centerH -= totalHeight;
58178             south.updateBox(this.safeBox(b));
58179         }
58180         if(west && west.isVisible()){
58181             var b = west.getBox();
58182             var m = west.getMargins();
58183             b.height = centerH - (m.top+m.bottom);
58184             b.x = m.left;
58185             b.y = centerY + m.top;
58186             var totalWidth = (b.width + m.left + m.right);
58187             centerX += totalWidth;
58188             centerW -= totalWidth;
58189             west.updateBox(this.safeBox(b));
58190         }
58191         if(east && east.isVisible()){
58192             var b = east.getBox();
58193             var m = east.getMargins();
58194             b.height = centerH - (m.top+m.bottom);
58195             var totalWidth = (b.width + m.left + m.right);
58196             b.x = w - totalWidth + m.left;
58197             b.y = centerY + m.top;
58198             centerW -= totalWidth;
58199             east.updateBox(this.safeBox(b));
58200         }
58201         if(center){
58202             var m = center.getMargins();
58203             var centerBox = {
58204                 x: centerX + m.left,
58205                 y: centerY + m.top,
58206                 width: centerW - (m.left+m.right),
58207                 height: centerH - (m.top+m.bottom)
58208             };
58209             //if(this.hideOnLayout){
58210                 //center.el.setStyle("display", "block");
58211             //}
58212             center.updateBox(this.safeBox(centerBox));
58213         }
58214         this.el.repaint();
58215         this.fireEvent("layout", this);
58216     },
58217
58218     // private
58219     safeBox : function(box){
58220         box.width = Math.max(0, box.width);
58221         box.height = Math.max(0, box.height);
58222         return box;
58223     },
58224
58225     /**
58226      * Adds a ContentPanel (or subclass) to this layout.
58227      * @param {String} target The target region key (north, south, east, west or center).
58228      * @param {Roo.ContentPanel} panel The panel to add
58229      * @return {Roo.ContentPanel} The added panel
58230      */
58231     add : function(target, panel){
58232          
58233         target = target.toLowerCase();
58234         return this.regions[target].add(panel);
58235     },
58236
58237     /**
58238      * Remove a ContentPanel (or subclass) to this layout.
58239      * @param {String} target The target region key (north, south, east, west or center).
58240      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
58241      * @return {Roo.ContentPanel} The removed panel
58242      */
58243     remove : function(target, panel){
58244         target = target.toLowerCase();
58245         return this.regions[target].remove(panel);
58246     },
58247
58248     /**
58249      * Searches all regions for a panel with the specified id
58250      * @param {String} panelId
58251      * @return {Roo.ContentPanel} The panel or null if it wasn't found
58252      */
58253     findPanel : function(panelId){
58254         var rs = this.regions;
58255         for(var target in rs){
58256             if(typeof rs[target] != "function"){
58257                 var p = rs[target].getPanel(panelId);
58258                 if(p){
58259                     return p;
58260                 }
58261             }
58262         }
58263         return null;
58264     },
58265
58266     /**
58267      * Searches all regions for a panel with the specified id and activates (shows) it.
58268      * @param {String/ContentPanel} panelId The panels id or the panel itself
58269      * @return {Roo.ContentPanel} The shown panel or null
58270      */
58271     showPanel : function(panelId) {
58272       var rs = this.regions;
58273       for(var target in rs){
58274          var r = rs[target];
58275          if(typeof r != "function"){
58276             if(r.hasPanel(panelId)){
58277                return r.showPanel(panelId);
58278             }
58279          }
58280       }
58281       return null;
58282    },
58283
58284    /**
58285      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
58286      * @param {Roo.state.Provider} provider (optional) An alternate state provider
58287      */
58288     restoreState : function(provider){
58289         if(!provider){
58290             provider = Roo.state.Manager;
58291         }
58292         var sm = new Roo.LayoutStateManager();
58293         sm.init(this, provider);
58294     },
58295
58296     /**
58297      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
58298      * object should contain properties for each region to add ContentPanels to, and each property's value should be
58299      * a valid ContentPanel config object.  Example:
58300      * <pre><code>
58301 // Create the main layout
58302 var layout = new Roo.BorderLayout('main-ct', {
58303     west: {
58304         split:true,
58305         minSize: 175,
58306         titlebar: true
58307     },
58308     center: {
58309         title:'Components'
58310     }
58311 }, 'main-ct');
58312
58313 // Create and add multiple ContentPanels at once via configs
58314 layout.batchAdd({
58315    west: {
58316        id: 'source-files',
58317        autoCreate:true,
58318        title:'Ext Source Files',
58319        autoScroll:true,
58320        fitToFrame:true
58321    },
58322    center : {
58323        el: cview,
58324        autoScroll:true,
58325        fitToFrame:true,
58326        toolbar: tb,
58327        resizeEl:'cbody'
58328    }
58329 });
58330 </code></pre>
58331      * @param {Object} regions An object containing ContentPanel configs by region name
58332      */
58333     batchAdd : function(regions){
58334         this.beginUpdate();
58335         for(var rname in regions){
58336             var lr = this.regions[rname];
58337             if(lr){
58338                 this.addTypedPanels(lr, regions[rname]);
58339             }
58340         }
58341         this.endUpdate();
58342     },
58343
58344     // private
58345     addTypedPanels : function(lr, ps){
58346         if(typeof ps == 'string'){
58347             lr.add(new Roo.ContentPanel(ps));
58348         }
58349         else if(ps instanceof Array){
58350             for(var i =0, len = ps.length; i < len; i++){
58351                 this.addTypedPanels(lr, ps[i]);
58352             }
58353         }
58354         else if(!ps.events){ // raw config?
58355             var el = ps.el;
58356             delete ps.el; // prevent conflict
58357             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
58358         }
58359         else {  // panel object assumed!
58360             lr.add(ps);
58361         }
58362     },
58363     /**
58364      * Adds a xtype elements to the layout.
58365      * <pre><code>
58366
58367 layout.addxtype({
58368        xtype : 'ContentPanel',
58369        region: 'west',
58370        items: [ .... ]
58371    }
58372 );
58373
58374 layout.addxtype({
58375         xtype : 'NestedLayoutPanel',
58376         region: 'west',
58377         layout: {
58378            center: { },
58379            west: { }   
58380         },
58381         items : [ ... list of content panels or nested layout panels.. ]
58382    }
58383 );
58384 </code></pre>
58385      * @param {Object} cfg Xtype definition of item to add.
58386      */
58387     addxtype : function(cfg)
58388     {
58389         // basically accepts a pannel...
58390         // can accept a layout region..!?!?
58391         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
58392         
58393         if (!cfg.xtype.match(/Panel$/)) {
58394             return false;
58395         }
58396         var ret = false;
58397         
58398         if (typeof(cfg.region) == 'undefined') {
58399             Roo.log("Failed to add Panel, region was not set");
58400             Roo.log(cfg);
58401             return false;
58402         }
58403         var region = cfg.region;
58404         delete cfg.region;
58405         
58406           
58407         var xitems = [];
58408         if (cfg.items) {
58409             xitems = cfg.items;
58410             delete cfg.items;
58411         }
58412         var nb = false;
58413         
58414         switch(cfg.xtype) 
58415         {
58416             case 'ContentPanel':  // ContentPanel (el, cfg)
58417             case 'ScrollPanel':  // ContentPanel (el, cfg)
58418             case 'ViewPanel': 
58419                 if(cfg.autoCreate) {
58420                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58421                 } else {
58422                     var el = this.el.createChild();
58423                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
58424                 }
58425                 
58426                 this.add(region, ret);
58427                 break;
58428             
58429             
58430             case 'TreePanel': // our new panel!
58431                 cfg.el = this.el.createChild();
58432                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58433                 this.add(region, ret);
58434                 break;
58435             
58436             case 'NestedLayoutPanel': 
58437                 // create a new Layout (which is  a Border Layout...
58438                 var el = this.el.createChild();
58439                 var clayout = cfg.layout;
58440                 delete cfg.layout;
58441                 clayout.items   = clayout.items  || [];
58442                 // replace this exitems with the clayout ones..
58443                 xitems = clayout.items;
58444                  
58445                 
58446                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
58447                     cfg.background = false;
58448                 }
58449                 var layout = new Roo.BorderLayout(el, clayout);
58450                 
58451                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
58452                 //console.log('adding nested layout panel '  + cfg.toSource());
58453                 this.add(region, ret);
58454                 nb = {}; /// find first...
58455                 break;
58456                 
58457             case 'GridPanel': 
58458             
58459                 // needs grid and region
58460                 
58461                 //var el = this.getRegion(region).el.createChild();
58462                 var el = this.el.createChild();
58463                 // create the grid first...
58464                 
58465                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
58466                 delete cfg.grid;
58467                 if (region == 'center' && this.active ) {
58468                     cfg.background = false;
58469                 }
58470                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
58471                 
58472                 this.add(region, ret);
58473                 if (cfg.background) {
58474                     ret.on('activate', function(gp) {
58475                         if (!gp.grid.rendered) {
58476                             gp.grid.render();
58477                         }
58478                     });
58479                 } else {
58480                     grid.render();
58481                 }
58482                 break;
58483            
58484            
58485            
58486                 
58487                 
58488                 
58489             default:
58490                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
58491                     
58492                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58493                     this.add(region, ret);
58494                 } else {
58495                 
58496                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
58497                     return null;
58498                 }
58499                 
58500              // GridPanel (grid, cfg)
58501             
58502         }
58503         this.beginUpdate();
58504         // add children..
58505         var region = '';
58506         var abn = {};
58507         Roo.each(xitems, function(i)  {
58508             region = nb && i.region ? i.region : false;
58509             
58510             var add = ret.addxtype(i);
58511            
58512             if (region) {
58513                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
58514                 if (!i.background) {
58515                     abn[region] = nb[region] ;
58516                 }
58517             }
58518             
58519         });
58520         this.endUpdate();
58521
58522         // make the last non-background panel active..
58523         //if (nb) { Roo.log(abn); }
58524         if (nb) {
58525             
58526             for(var r in abn) {
58527                 region = this.getRegion(r);
58528                 if (region) {
58529                     // tried using nb[r], but it does not work..
58530                      
58531                     region.showPanel(abn[r]);
58532                    
58533                 }
58534             }
58535         }
58536         return ret;
58537         
58538     }
58539 });
58540
58541 /**
58542  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
58543  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
58544  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
58545  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
58546  * <pre><code>
58547 // shorthand
58548 var CP = Roo.ContentPanel;
58549
58550 var layout = Roo.BorderLayout.create({
58551     north: {
58552         initialSize: 25,
58553         titlebar: false,
58554         panels: [new CP("north", "North")]
58555     },
58556     west: {
58557         split:true,
58558         initialSize: 200,
58559         minSize: 175,
58560         maxSize: 400,
58561         titlebar: true,
58562         collapsible: true,
58563         panels: [new CP("west", {title: "West"})]
58564     },
58565     east: {
58566         split:true,
58567         initialSize: 202,
58568         minSize: 175,
58569         maxSize: 400,
58570         titlebar: true,
58571         collapsible: true,
58572         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
58573     },
58574     south: {
58575         split:true,
58576         initialSize: 100,
58577         minSize: 100,
58578         maxSize: 200,
58579         titlebar: true,
58580         collapsible: true,
58581         panels: [new CP("south", {title: "South", closable: true})]
58582     },
58583     center: {
58584         titlebar: true,
58585         autoScroll:true,
58586         resizeTabs: true,
58587         minTabWidth: 50,
58588         preferredTabWidth: 150,
58589         panels: [
58590             new CP("center1", {title: "Close Me", closable: true}),
58591             new CP("center2", {title: "Center Panel", closable: false})
58592         ]
58593     }
58594 }, document.body);
58595
58596 layout.getRegion("center").showPanel("center1");
58597 </code></pre>
58598  * @param config
58599  * @param targetEl
58600  */
58601 Roo.BorderLayout.create = function(config, targetEl){
58602     var layout = new Roo.BorderLayout(targetEl || document.body, config);
58603     layout.beginUpdate();
58604     var regions = Roo.BorderLayout.RegionFactory.validRegions;
58605     for(var j = 0, jlen = regions.length; j < jlen; j++){
58606         var lr = regions[j];
58607         if(layout.regions[lr] && config[lr].panels){
58608             var r = layout.regions[lr];
58609             var ps = config[lr].panels;
58610             layout.addTypedPanels(r, ps);
58611         }
58612     }
58613     layout.endUpdate();
58614     return layout;
58615 };
58616
58617 // private
58618 Roo.BorderLayout.RegionFactory = {
58619     // private
58620     validRegions : ["north","south","east","west","center"],
58621
58622     // private
58623     create : function(target, mgr, config){
58624         target = target.toLowerCase();
58625         if(config.lightweight || config.basic){
58626             return new Roo.BasicLayoutRegion(mgr, config, target);
58627         }
58628         switch(target){
58629             case "north":
58630                 return new Roo.NorthLayoutRegion(mgr, config);
58631             case "south":
58632                 return new Roo.SouthLayoutRegion(mgr, config);
58633             case "east":
58634                 return new Roo.EastLayoutRegion(mgr, config);
58635             case "west":
58636                 return new Roo.WestLayoutRegion(mgr, config);
58637             case "center":
58638                 return new Roo.CenterLayoutRegion(mgr, config);
58639         }
58640         throw 'Layout region "'+target+'" not supported.';
58641     }
58642 };/*
58643  * Based on:
58644  * Ext JS Library 1.1.1
58645  * Copyright(c) 2006-2007, Ext JS, LLC.
58646  *
58647  * Originally Released Under LGPL - original licence link has changed is not relivant.
58648  *
58649  * Fork - LGPL
58650  * <script type="text/javascript">
58651  */
58652  
58653 /**
58654  * @class Roo.BasicLayoutRegion
58655  * @extends Roo.util.Observable
58656  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
58657  * and does not have a titlebar, tabs or any other features. All it does is size and position 
58658  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
58659  */
58660 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
58661     this.mgr = mgr;
58662     this.position  = pos;
58663     this.events = {
58664         /**
58665          * @scope Roo.BasicLayoutRegion
58666          */
58667         
58668         /**
58669          * @event beforeremove
58670          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
58671          * @param {Roo.LayoutRegion} this
58672          * @param {Roo.ContentPanel} panel The panel
58673          * @param {Object} e The cancel event object
58674          */
58675         "beforeremove" : true,
58676         /**
58677          * @event invalidated
58678          * Fires when the layout for this region is changed.
58679          * @param {Roo.LayoutRegion} this
58680          */
58681         "invalidated" : true,
58682         /**
58683          * @event visibilitychange
58684          * Fires when this region is shown or hidden 
58685          * @param {Roo.LayoutRegion} this
58686          * @param {Boolean} visibility true or false
58687          */
58688         "visibilitychange" : true,
58689         /**
58690          * @event paneladded
58691          * Fires when a panel is added. 
58692          * @param {Roo.LayoutRegion} this
58693          * @param {Roo.ContentPanel} panel The panel
58694          */
58695         "paneladded" : true,
58696         /**
58697          * @event panelremoved
58698          * Fires when a panel is removed. 
58699          * @param {Roo.LayoutRegion} this
58700          * @param {Roo.ContentPanel} panel The panel
58701          */
58702         "panelremoved" : true,
58703         /**
58704          * @event beforecollapse
58705          * Fires when this region before collapse.
58706          * @param {Roo.LayoutRegion} this
58707          */
58708         "beforecollapse" : true,
58709         /**
58710          * @event collapsed
58711          * Fires when this region is collapsed.
58712          * @param {Roo.LayoutRegion} this
58713          */
58714         "collapsed" : true,
58715         /**
58716          * @event expanded
58717          * Fires when this region is expanded.
58718          * @param {Roo.LayoutRegion} this
58719          */
58720         "expanded" : true,
58721         /**
58722          * @event slideshow
58723          * Fires when this region is slid into view.
58724          * @param {Roo.LayoutRegion} this
58725          */
58726         "slideshow" : true,
58727         /**
58728          * @event slidehide
58729          * Fires when this region slides out of view. 
58730          * @param {Roo.LayoutRegion} this
58731          */
58732         "slidehide" : true,
58733         /**
58734          * @event panelactivated
58735          * Fires when a panel is activated. 
58736          * @param {Roo.LayoutRegion} this
58737          * @param {Roo.ContentPanel} panel The activated panel
58738          */
58739         "panelactivated" : true,
58740         /**
58741          * @event resized
58742          * Fires when the user resizes this region. 
58743          * @param {Roo.LayoutRegion} this
58744          * @param {Number} newSize The new size (width for east/west, height for north/south)
58745          */
58746         "resized" : true
58747     };
58748     /** A collection of panels in this region. @type Roo.util.MixedCollection */
58749     this.panels = new Roo.util.MixedCollection();
58750     this.panels.getKey = this.getPanelId.createDelegate(this);
58751     this.box = null;
58752     this.activePanel = null;
58753     // ensure listeners are added...
58754     
58755     if (config.listeners || config.events) {
58756         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
58757             listeners : config.listeners || {},
58758             events : config.events || {}
58759         });
58760     }
58761     
58762     if(skipConfig !== true){
58763         this.applyConfig(config);
58764     }
58765 };
58766
58767 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
58768     getPanelId : function(p){
58769         return p.getId();
58770     },
58771     
58772     applyConfig : function(config){
58773         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
58774         this.config = config;
58775         
58776     },
58777     
58778     /**
58779      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
58780      * the width, for horizontal (north, south) the height.
58781      * @param {Number} newSize The new width or height
58782      */
58783     resizeTo : function(newSize){
58784         var el = this.el ? this.el :
58785                  (this.activePanel ? this.activePanel.getEl() : null);
58786         if(el){
58787             switch(this.position){
58788                 case "east":
58789                 case "west":
58790                     el.setWidth(newSize);
58791                     this.fireEvent("resized", this, newSize);
58792                 break;
58793                 case "north":
58794                 case "south":
58795                     el.setHeight(newSize);
58796                     this.fireEvent("resized", this, newSize);
58797                 break;                
58798             }
58799         }
58800     },
58801     
58802     getBox : function(){
58803         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
58804     },
58805     
58806     getMargins : function(){
58807         return this.margins;
58808     },
58809     
58810     updateBox : function(box){
58811         this.box = box;
58812         var el = this.activePanel.getEl();
58813         el.dom.style.left = box.x + "px";
58814         el.dom.style.top = box.y + "px";
58815         this.activePanel.setSize(box.width, box.height);
58816     },
58817     
58818     /**
58819      * Returns the container element for this region.
58820      * @return {Roo.Element}
58821      */
58822     getEl : function(){
58823         return this.activePanel;
58824     },
58825     
58826     /**
58827      * Returns true if this region is currently visible.
58828      * @return {Boolean}
58829      */
58830     isVisible : function(){
58831         return this.activePanel ? true : false;
58832     },
58833     
58834     setActivePanel : function(panel){
58835         panel = this.getPanel(panel);
58836         if(this.activePanel && this.activePanel != panel){
58837             this.activePanel.setActiveState(false);
58838             this.activePanel.getEl().setLeftTop(-10000,-10000);
58839         }
58840         this.activePanel = panel;
58841         panel.setActiveState(true);
58842         if(this.box){
58843             panel.setSize(this.box.width, this.box.height);
58844         }
58845         this.fireEvent("panelactivated", this, panel);
58846         this.fireEvent("invalidated");
58847     },
58848     
58849     /**
58850      * Show the specified panel.
58851      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
58852      * @return {Roo.ContentPanel} The shown panel or null
58853      */
58854     showPanel : function(panel){
58855         if(panel = this.getPanel(panel)){
58856             this.setActivePanel(panel);
58857         }
58858         return panel;
58859     },
58860     
58861     /**
58862      * Get the active panel for this region.
58863      * @return {Roo.ContentPanel} The active panel or null
58864      */
58865     getActivePanel : function(){
58866         return this.activePanel;
58867     },
58868     
58869     /**
58870      * Add the passed ContentPanel(s)
58871      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
58872      * @return {Roo.ContentPanel} The panel added (if only one was added)
58873      */
58874     add : function(panel){
58875         if(arguments.length > 1){
58876             for(var i = 0, len = arguments.length; i < len; i++) {
58877                 this.add(arguments[i]);
58878             }
58879             return null;
58880         }
58881         if(this.hasPanel(panel)){
58882             this.showPanel(panel);
58883             return panel;
58884         }
58885         var el = panel.getEl();
58886         if(el.dom.parentNode != this.mgr.el.dom){
58887             this.mgr.el.dom.appendChild(el.dom);
58888         }
58889         if(panel.setRegion){
58890             panel.setRegion(this);
58891         }
58892         this.panels.add(panel);
58893         el.setStyle("position", "absolute");
58894         if(!panel.background){
58895             this.setActivePanel(panel);
58896             if(this.config.initialSize && this.panels.getCount()==1){
58897                 this.resizeTo(this.config.initialSize);
58898             }
58899         }
58900         this.fireEvent("paneladded", this, panel);
58901         return panel;
58902     },
58903     
58904     /**
58905      * Returns true if the panel is in this region.
58906      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
58907      * @return {Boolean}
58908      */
58909     hasPanel : function(panel){
58910         if(typeof panel == "object"){ // must be panel obj
58911             panel = panel.getId();
58912         }
58913         return this.getPanel(panel) ? true : false;
58914     },
58915     
58916     /**
58917      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
58918      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
58919      * @param {Boolean} preservePanel Overrides the config preservePanel option
58920      * @return {Roo.ContentPanel} The panel that was removed
58921      */
58922     remove : function(panel, preservePanel){
58923         panel = this.getPanel(panel);
58924         if(!panel){
58925             return null;
58926         }
58927         var e = {};
58928         this.fireEvent("beforeremove", this, panel, e);
58929         if(e.cancel === true){
58930             return null;
58931         }
58932         var panelId = panel.getId();
58933         this.panels.removeKey(panelId);
58934         return panel;
58935     },
58936     
58937     /**
58938      * Returns the panel specified or null if it's not in this region.
58939      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
58940      * @return {Roo.ContentPanel}
58941      */
58942     getPanel : function(id){
58943         if(typeof id == "object"){ // must be panel obj
58944             return id;
58945         }
58946         return this.panels.get(id);
58947     },
58948     
58949     /**
58950      * Returns this regions position (north/south/east/west/center).
58951      * @return {String} 
58952      */
58953     getPosition: function(){
58954         return this.position;    
58955     }
58956 });/*
58957  * Based on:
58958  * Ext JS Library 1.1.1
58959  * Copyright(c) 2006-2007, Ext JS, LLC.
58960  *
58961  * Originally Released Under LGPL - original licence link has changed is not relivant.
58962  *
58963  * Fork - LGPL
58964  * <script type="text/javascript">
58965  */
58966  
58967 /**
58968  * @class Roo.LayoutRegion
58969  * @extends Roo.BasicLayoutRegion
58970  * This class represents a region in a layout manager.
58971  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
58972  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
58973  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
58974  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
58975  * @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})
58976  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
58977  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
58978  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
58979  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
58980  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
58981  * @cfg {String}    title           The title for the region (overrides panel titles)
58982  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
58983  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
58984  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
58985  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
58986  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
58987  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
58988  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
58989  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
58990  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
58991  * @cfg {Boolean}   showPin         True to show a pin button
58992  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
58993  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
58994  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
58995  * @cfg {Number}    width           For East/West panels
58996  * @cfg {Number}    height          For North/South panels
58997  * @cfg {Boolean}   split           To show the splitter
58998  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
58999  */
59000 Roo.LayoutRegion = function(mgr, config, pos){
59001     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
59002     var dh = Roo.DomHelper;
59003     /** This region's container element 
59004     * @type Roo.Element */
59005     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
59006     /** This region's title element 
59007     * @type Roo.Element */
59008
59009     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
59010         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
59011         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
59012     ]}, true);
59013     this.titleEl.enableDisplayMode();
59014     /** This region's title text element 
59015     * @type HTMLElement */
59016     this.titleTextEl = this.titleEl.dom.firstChild;
59017     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
59018     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
59019     this.closeBtn.enableDisplayMode();
59020     this.closeBtn.on("click", this.closeClicked, this);
59021     this.closeBtn.hide();
59022
59023     this.createBody(config);
59024     this.visible = true;
59025     this.collapsed = false;
59026
59027     if(config.hideWhenEmpty){
59028         this.hide();
59029         this.on("paneladded", this.validateVisibility, this);
59030         this.on("panelremoved", this.validateVisibility, this);
59031     }
59032     this.applyConfig(config);
59033 };
59034
59035 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
59036
59037     createBody : function(){
59038         /** This region's body element 
59039         * @type Roo.Element */
59040         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
59041     },
59042
59043     applyConfig : function(c){
59044         if(c.collapsible && this.position != "center" && !this.collapsedEl){
59045             var dh = Roo.DomHelper;
59046             if(c.titlebar !== false){
59047                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
59048                 this.collapseBtn.on("click", this.collapse, this);
59049                 this.collapseBtn.enableDisplayMode();
59050
59051                 if(c.showPin === true || this.showPin){
59052                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
59053                     this.stickBtn.enableDisplayMode();
59054                     this.stickBtn.on("click", this.expand, this);
59055                     this.stickBtn.hide();
59056                 }
59057             }
59058             /** This region's collapsed element
59059             * @type Roo.Element */
59060             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
59061                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
59062             ]}, true);
59063             if(c.floatable !== false){
59064                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
59065                this.collapsedEl.on("click", this.collapseClick, this);
59066             }
59067
59068             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
59069                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
59070                    id: "message", unselectable: "on", style:{"float":"left"}});
59071                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
59072              }
59073             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
59074             this.expandBtn.on("click", this.expand, this);
59075         }
59076         if(this.collapseBtn){
59077             this.collapseBtn.setVisible(c.collapsible == true);
59078         }
59079         this.cmargins = c.cmargins || this.cmargins ||
59080                          (this.position == "west" || this.position == "east" ?
59081                              {top: 0, left: 2, right:2, bottom: 0} :
59082                              {top: 2, left: 0, right:0, bottom: 2});
59083         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59084         this.bottomTabs = c.tabPosition != "top";
59085         this.autoScroll = c.autoScroll || false;
59086         if(this.autoScroll){
59087             this.bodyEl.setStyle("overflow", "auto");
59088         }else{
59089             this.bodyEl.setStyle("overflow", "hidden");
59090         }
59091         //if(c.titlebar !== false){
59092             if((!c.titlebar && !c.title) || c.titlebar === false){
59093                 this.titleEl.hide();
59094             }else{
59095                 this.titleEl.show();
59096                 if(c.title){
59097                     this.titleTextEl.innerHTML = c.title;
59098                 }
59099             }
59100         //}
59101         this.duration = c.duration || .30;
59102         this.slideDuration = c.slideDuration || .45;
59103         this.config = c;
59104         if(c.collapsed){
59105             this.collapse(true);
59106         }
59107         if(c.hidden){
59108             this.hide();
59109         }
59110     },
59111     /**
59112      * Returns true if this region is currently visible.
59113      * @return {Boolean}
59114      */
59115     isVisible : function(){
59116         return this.visible;
59117     },
59118
59119     /**
59120      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
59121      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
59122      */
59123     setCollapsedTitle : function(title){
59124         title = title || "&#160;";
59125         if(this.collapsedTitleTextEl){
59126             this.collapsedTitleTextEl.innerHTML = title;
59127         }
59128     },
59129
59130     getBox : function(){
59131         var b;
59132         if(!this.collapsed){
59133             b = this.el.getBox(false, true);
59134         }else{
59135             b = this.collapsedEl.getBox(false, true);
59136         }
59137         return b;
59138     },
59139
59140     getMargins : function(){
59141         return this.collapsed ? this.cmargins : this.margins;
59142     },
59143
59144     highlight : function(){
59145         this.el.addClass("x-layout-panel-dragover");
59146     },
59147
59148     unhighlight : function(){
59149         this.el.removeClass("x-layout-panel-dragover");
59150     },
59151
59152     updateBox : function(box){
59153         this.box = box;
59154         if(!this.collapsed){
59155             this.el.dom.style.left = box.x + "px";
59156             this.el.dom.style.top = box.y + "px";
59157             this.updateBody(box.width, box.height);
59158         }else{
59159             this.collapsedEl.dom.style.left = box.x + "px";
59160             this.collapsedEl.dom.style.top = box.y + "px";
59161             this.collapsedEl.setSize(box.width, box.height);
59162         }
59163         if(this.tabs){
59164             this.tabs.autoSizeTabs();
59165         }
59166     },
59167
59168     updateBody : function(w, h){
59169         if(w !== null){
59170             this.el.setWidth(w);
59171             w -= this.el.getBorderWidth("rl");
59172             if(this.config.adjustments){
59173                 w += this.config.adjustments[0];
59174             }
59175         }
59176         if(h !== null){
59177             this.el.setHeight(h);
59178             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
59179             h -= this.el.getBorderWidth("tb");
59180             if(this.config.adjustments){
59181                 h += this.config.adjustments[1];
59182             }
59183             this.bodyEl.setHeight(h);
59184             if(this.tabs){
59185                 h = this.tabs.syncHeight(h);
59186             }
59187         }
59188         if(this.panelSize){
59189             w = w !== null ? w : this.panelSize.width;
59190             h = h !== null ? h : this.panelSize.height;
59191         }
59192         if(this.activePanel){
59193             var el = this.activePanel.getEl();
59194             w = w !== null ? w : el.getWidth();
59195             h = h !== null ? h : el.getHeight();
59196             this.panelSize = {width: w, height: h};
59197             this.activePanel.setSize(w, h);
59198         }
59199         if(Roo.isIE && this.tabs){
59200             this.tabs.el.repaint();
59201         }
59202     },
59203
59204     /**
59205      * Returns the container element for this region.
59206      * @return {Roo.Element}
59207      */
59208     getEl : function(){
59209         return this.el;
59210     },
59211
59212     /**
59213      * Hides this region.
59214      */
59215     hide : function(){
59216         if(!this.collapsed){
59217             this.el.dom.style.left = "-2000px";
59218             this.el.hide();
59219         }else{
59220             this.collapsedEl.dom.style.left = "-2000px";
59221             this.collapsedEl.hide();
59222         }
59223         this.visible = false;
59224         this.fireEvent("visibilitychange", this, false);
59225     },
59226
59227     /**
59228      * Shows this region if it was previously hidden.
59229      */
59230     show : function(){
59231         if(!this.collapsed){
59232             this.el.show();
59233         }else{
59234             this.collapsedEl.show();
59235         }
59236         this.visible = true;
59237         this.fireEvent("visibilitychange", this, true);
59238     },
59239
59240     closeClicked : function(){
59241         if(this.activePanel){
59242             this.remove(this.activePanel);
59243         }
59244     },
59245
59246     collapseClick : function(e){
59247         if(this.isSlid){
59248            e.stopPropagation();
59249            this.slideIn();
59250         }else{
59251            e.stopPropagation();
59252            this.slideOut();
59253         }
59254     },
59255
59256     /**
59257      * Collapses this region.
59258      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
59259      */
59260     collapse : function(skipAnim, skipCheck){
59261         if(this.collapsed) {
59262             return;
59263         }
59264         
59265         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
59266             
59267             this.collapsed = true;
59268             if(this.split){
59269                 this.split.el.hide();
59270             }
59271             if(this.config.animate && skipAnim !== true){
59272                 this.fireEvent("invalidated", this);
59273                 this.animateCollapse();
59274             }else{
59275                 this.el.setLocation(-20000,-20000);
59276                 this.el.hide();
59277                 this.collapsedEl.show();
59278                 this.fireEvent("collapsed", this);
59279                 this.fireEvent("invalidated", this);
59280             }
59281         }
59282         
59283     },
59284
59285     animateCollapse : function(){
59286         // overridden
59287     },
59288
59289     /**
59290      * Expands this region if it was previously collapsed.
59291      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
59292      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
59293      */
59294     expand : function(e, skipAnim){
59295         if(e) {
59296             e.stopPropagation();
59297         }
59298         if(!this.collapsed || this.el.hasActiveFx()) {
59299             return;
59300         }
59301         if(this.isSlid){
59302             this.afterSlideIn();
59303             skipAnim = true;
59304         }
59305         this.collapsed = false;
59306         if(this.config.animate && skipAnim !== true){
59307             this.animateExpand();
59308         }else{
59309             this.el.show();
59310             if(this.split){
59311                 this.split.el.show();
59312             }
59313             this.collapsedEl.setLocation(-2000,-2000);
59314             this.collapsedEl.hide();
59315             this.fireEvent("invalidated", this);
59316             this.fireEvent("expanded", this);
59317         }
59318     },
59319
59320     animateExpand : function(){
59321         // overridden
59322     },
59323
59324     initTabs : function()
59325     {
59326         this.bodyEl.setStyle("overflow", "hidden");
59327         var ts = new Roo.TabPanel(
59328                 this.bodyEl.dom,
59329                 {
59330                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
59331                     disableTooltips: this.config.disableTabTips,
59332                     toolbar : this.config.toolbar
59333                 }
59334         );
59335         if(this.config.hideTabs){
59336             ts.stripWrap.setDisplayed(false);
59337         }
59338         this.tabs = ts;
59339         ts.resizeTabs = this.config.resizeTabs === true;
59340         ts.minTabWidth = this.config.minTabWidth || 40;
59341         ts.maxTabWidth = this.config.maxTabWidth || 250;
59342         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
59343         ts.monitorResize = false;
59344         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59345         ts.bodyEl.addClass('x-layout-tabs-body');
59346         this.panels.each(this.initPanelAsTab, this);
59347     },
59348
59349     initPanelAsTab : function(panel){
59350         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
59351                     this.config.closeOnTab && panel.isClosable());
59352         if(panel.tabTip !== undefined){
59353             ti.setTooltip(panel.tabTip);
59354         }
59355         ti.on("activate", function(){
59356               this.setActivePanel(panel);
59357         }, this);
59358         if(this.config.closeOnTab){
59359             ti.on("beforeclose", function(t, e){
59360                 e.cancel = true;
59361                 this.remove(panel);
59362             }, this);
59363         }
59364         return ti;
59365     },
59366
59367     updatePanelTitle : function(panel, title){
59368         if(this.activePanel == panel){
59369             this.updateTitle(title);
59370         }
59371         if(this.tabs){
59372             var ti = this.tabs.getTab(panel.getEl().id);
59373             ti.setText(title);
59374             if(panel.tabTip !== undefined){
59375                 ti.setTooltip(panel.tabTip);
59376             }
59377         }
59378     },
59379
59380     updateTitle : function(title){
59381         if(this.titleTextEl && !this.config.title){
59382             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
59383         }
59384     },
59385
59386     setActivePanel : function(panel){
59387         panel = this.getPanel(panel);
59388         if(this.activePanel && this.activePanel != panel){
59389             this.activePanel.setActiveState(false);
59390         }
59391         this.activePanel = panel;
59392         panel.setActiveState(true);
59393         if(this.panelSize){
59394             panel.setSize(this.panelSize.width, this.panelSize.height);
59395         }
59396         if(this.closeBtn){
59397             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
59398         }
59399         this.updateTitle(panel.getTitle());
59400         if(this.tabs){
59401             this.fireEvent("invalidated", this);
59402         }
59403         this.fireEvent("panelactivated", this, panel);
59404     },
59405
59406     /**
59407      * Shows the specified panel.
59408      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
59409      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
59410      */
59411     showPanel : function(panel)
59412     {
59413         panel = this.getPanel(panel);
59414         if(panel){
59415             if(this.tabs){
59416                 var tab = this.tabs.getTab(panel.getEl().id);
59417                 if(tab.isHidden()){
59418                     this.tabs.unhideTab(tab.id);
59419                 }
59420                 tab.activate();
59421             }else{
59422                 this.setActivePanel(panel);
59423             }
59424         }
59425         return panel;
59426     },
59427
59428     /**
59429      * Get the active panel for this region.
59430      * @return {Roo.ContentPanel} The active panel or null
59431      */
59432     getActivePanel : function(){
59433         return this.activePanel;
59434     },
59435
59436     validateVisibility : function(){
59437         if(this.panels.getCount() < 1){
59438             this.updateTitle("&#160;");
59439             this.closeBtn.hide();
59440             this.hide();
59441         }else{
59442             if(!this.isVisible()){
59443                 this.show();
59444             }
59445         }
59446     },
59447
59448     /**
59449      * Adds the passed ContentPanel(s) to this region.
59450      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59451      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
59452      */
59453     add : function(panel){
59454         if(arguments.length > 1){
59455             for(var i = 0, len = arguments.length; i < len; i++) {
59456                 this.add(arguments[i]);
59457             }
59458             return null;
59459         }
59460         if(this.hasPanel(panel)){
59461             this.showPanel(panel);
59462             return panel;
59463         }
59464         panel.setRegion(this);
59465         this.panels.add(panel);
59466         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
59467             this.bodyEl.dom.appendChild(panel.getEl().dom);
59468             if(panel.background !== true){
59469                 this.setActivePanel(panel);
59470             }
59471             this.fireEvent("paneladded", this, panel);
59472             return panel;
59473         }
59474         if(!this.tabs){
59475             this.initTabs();
59476         }else{
59477             this.initPanelAsTab(panel);
59478         }
59479         if(panel.background !== true){
59480             this.tabs.activate(panel.getEl().id);
59481         }
59482         this.fireEvent("paneladded", this, panel);
59483         return panel;
59484     },
59485
59486     /**
59487      * Hides the tab for the specified panel.
59488      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59489      */
59490     hidePanel : function(panel){
59491         if(this.tabs && (panel = this.getPanel(panel))){
59492             this.tabs.hideTab(panel.getEl().id);
59493         }
59494     },
59495
59496     /**
59497      * Unhides the tab for a previously hidden panel.
59498      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59499      */
59500     unhidePanel : function(panel){
59501         if(this.tabs && (panel = this.getPanel(panel))){
59502             this.tabs.unhideTab(panel.getEl().id);
59503         }
59504     },
59505
59506     clearPanels : function(){
59507         while(this.panels.getCount() > 0){
59508              this.remove(this.panels.first());
59509         }
59510     },
59511
59512     /**
59513      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59514      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59515      * @param {Boolean} preservePanel Overrides the config preservePanel option
59516      * @return {Roo.ContentPanel} The panel that was removed
59517      */
59518     remove : function(panel, preservePanel){
59519         panel = this.getPanel(panel);
59520         if(!panel){
59521             return null;
59522         }
59523         var e = {};
59524         this.fireEvent("beforeremove", this, panel, e);
59525         if(e.cancel === true){
59526             return null;
59527         }
59528         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
59529         var panelId = panel.getId();
59530         this.panels.removeKey(panelId);
59531         if(preservePanel){
59532             document.body.appendChild(panel.getEl().dom);
59533         }
59534         if(this.tabs){
59535             this.tabs.removeTab(panel.getEl().id);
59536         }else if (!preservePanel){
59537             this.bodyEl.dom.removeChild(panel.getEl().dom);
59538         }
59539         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
59540             var p = this.panels.first();
59541             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
59542             tempEl.appendChild(p.getEl().dom);
59543             this.bodyEl.update("");
59544             this.bodyEl.dom.appendChild(p.getEl().dom);
59545             tempEl = null;
59546             this.updateTitle(p.getTitle());
59547             this.tabs = null;
59548             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59549             this.setActivePanel(p);
59550         }
59551         panel.setRegion(null);
59552         if(this.activePanel == panel){
59553             this.activePanel = null;
59554         }
59555         if(this.config.autoDestroy !== false && preservePanel !== true){
59556             try{panel.destroy();}catch(e){}
59557         }
59558         this.fireEvent("panelremoved", this, panel);
59559         return panel;
59560     },
59561
59562     /**
59563      * Returns the TabPanel component used by this region
59564      * @return {Roo.TabPanel}
59565      */
59566     getTabs : function(){
59567         return this.tabs;
59568     },
59569
59570     createTool : function(parentEl, className){
59571         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
59572             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
59573         btn.addClassOnOver("x-layout-tools-button-over");
59574         return btn;
59575     }
59576 });/*
59577  * Based on:
59578  * Ext JS Library 1.1.1
59579  * Copyright(c) 2006-2007, Ext JS, LLC.
59580  *
59581  * Originally Released Under LGPL - original licence link has changed is not relivant.
59582  *
59583  * Fork - LGPL
59584  * <script type="text/javascript">
59585  */
59586  
59587
59588
59589 /**
59590  * @class Roo.SplitLayoutRegion
59591  * @extends Roo.LayoutRegion
59592  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
59593  */
59594 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
59595     this.cursor = cursor;
59596     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
59597 };
59598
59599 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
59600     splitTip : "Drag to resize.",
59601     collapsibleSplitTip : "Drag to resize. Double click to hide.",
59602     useSplitTips : false,
59603
59604     applyConfig : function(config){
59605         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
59606         if(config.split){
59607             if(!this.split){
59608                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
59609                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
59610                 /** The SplitBar for this region 
59611                 * @type Roo.SplitBar */
59612                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
59613                 this.split.on("moved", this.onSplitMove, this);
59614                 this.split.useShim = config.useShim === true;
59615                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
59616                 if(this.useSplitTips){
59617                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
59618                 }
59619                 if(config.collapsible){
59620                     this.split.el.on("dblclick", this.collapse,  this);
59621                 }
59622             }
59623             if(typeof config.minSize != "undefined"){
59624                 this.split.minSize = config.minSize;
59625             }
59626             if(typeof config.maxSize != "undefined"){
59627                 this.split.maxSize = config.maxSize;
59628             }
59629             if(config.hideWhenEmpty || config.hidden || config.collapsed){
59630                 this.hideSplitter();
59631             }
59632         }
59633     },
59634
59635     getHMaxSize : function(){
59636          var cmax = this.config.maxSize || 10000;
59637          var center = this.mgr.getRegion("center");
59638          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
59639     },
59640
59641     getVMaxSize : function(){
59642          var cmax = this.config.maxSize || 10000;
59643          var center = this.mgr.getRegion("center");
59644          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
59645     },
59646
59647     onSplitMove : function(split, newSize){
59648         this.fireEvent("resized", this, newSize);
59649     },
59650     
59651     /** 
59652      * Returns the {@link Roo.SplitBar} for this region.
59653      * @return {Roo.SplitBar}
59654      */
59655     getSplitBar : function(){
59656         return this.split;
59657     },
59658     
59659     hide : function(){
59660         this.hideSplitter();
59661         Roo.SplitLayoutRegion.superclass.hide.call(this);
59662     },
59663
59664     hideSplitter : function(){
59665         if(this.split){
59666             this.split.el.setLocation(-2000,-2000);
59667             this.split.el.hide();
59668         }
59669     },
59670
59671     show : function(){
59672         if(this.split){
59673             this.split.el.show();
59674         }
59675         Roo.SplitLayoutRegion.superclass.show.call(this);
59676     },
59677     
59678     beforeSlide: function(){
59679         if(Roo.isGecko){// firefox overflow auto bug workaround
59680             this.bodyEl.clip();
59681             if(this.tabs) {
59682                 this.tabs.bodyEl.clip();
59683             }
59684             if(this.activePanel){
59685                 this.activePanel.getEl().clip();
59686                 
59687                 if(this.activePanel.beforeSlide){
59688                     this.activePanel.beforeSlide();
59689                 }
59690             }
59691         }
59692     },
59693     
59694     afterSlide : function(){
59695         if(Roo.isGecko){// firefox overflow auto bug workaround
59696             this.bodyEl.unclip();
59697             if(this.tabs) {
59698                 this.tabs.bodyEl.unclip();
59699             }
59700             if(this.activePanel){
59701                 this.activePanel.getEl().unclip();
59702                 if(this.activePanel.afterSlide){
59703                     this.activePanel.afterSlide();
59704                 }
59705             }
59706         }
59707     },
59708
59709     initAutoHide : function(){
59710         if(this.autoHide !== false){
59711             if(!this.autoHideHd){
59712                 var st = new Roo.util.DelayedTask(this.slideIn, this);
59713                 this.autoHideHd = {
59714                     "mouseout": function(e){
59715                         if(!e.within(this.el, true)){
59716                             st.delay(500);
59717                         }
59718                     },
59719                     "mouseover" : function(e){
59720                         st.cancel();
59721                     },
59722                     scope : this
59723                 };
59724             }
59725             this.el.on(this.autoHideHd);
59726         }
59727     },
59728
59729     clearAutoHide : function(){
59730         if(this.autoHide !== false){
59731             this.el.un("mouseout", this.autoHideHd.mouseout);
59732             this.el.un("mouseover", this.autoHideHd.mouseover);
59733         }
59734     },
59735
59736     clearMonitor : function(){
59737         Roo.get(document).un("click", this.slideInIf, this);
59738     },
59739
59740     // these names are backwards but not changed for compat
59741     slideOut : function(){
59742         if(this.isSlid || this.el.hasActiveFx()){
59743             return;
59744         }
59745         this.isSlid = true;
59746         if(this.collapseBtn){
59747             this.collapseBtn.hide();
59748         }
59749         this.closeBtnState = this.closeBtn.getStyle('display');
59750         this.closeBtn.hide();
59751         if(this.stickBtn){
59752             this.stickBtn.show();
59753         }
59754         this.el.show();
59755         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
59756         this.beforeSlide();
59757         this.el.setStyle("z-index", 10001);
59758         this.el.slideIn(this.getSlideAnchor(), {
59759             callback: function(){
59760                 this.afterSlide();
59761                 this.initAutoHide();
59762                 Roo.get(document).on("click", this.slideInIf, this);
59763                 this.fireEvent("slideshow", this);
59764             },
59765             scope: this,
59766             block: true
59767         });
59768     },
59769
59770     afterSlideIn : function(){
59771         this.clearAutoHide();
59772         this.isSlid = false;
59773         this.clearMonitor();
59774         this.el.setStyle("z-index", "");
59775         if(this.collapseBtn){
59776             this.collapseBtn.show();
59777         }
59778         this.closeBtn.setStyle('display', this.closeBtnState);
59779         if(this.stickBtn){
59780             this.stickBtn.hide();
59781         }
59782         this.fireEvent("slidehide", this);
59783     },
59784
59785     slideIn : function(cb){
59786         if(!this.isSlid || this.el.hasActiveFx()){
59787             Roo.callback(cb);
59788             return;
59789         }
59790         this.isSlid = false;
59791         this.beforeSlide();
59792         this.el.slideOut(this.getSlideAnchor(), {
59793             callback: function(){
59794                 this.el.setLeftTop(-10000, -10000);
59795                 this.afterSlide();
59796                 this.afterSlideIn();
59797                 Roo.callback(cb);
59798             },
59799             scope: this,
59800             block: true
59801         });
59802     },
59803     
59804     slideInIf : function(e){
59805         if(!e.within(this.el)){
59806             this.slideIn();
59807         }
59808     },
59809
59810     animateCollapse : function(){
59811         this.beforeSlide();
59812         this.el.setStyle("z-index", 20000);
59813         var anchor = this.getSlideAnchor();
59814         this.el.slideOut(anchor, {
59815             callback : function(){
59816                 this.el.setStyle("z-index", "");
59817                 this.collapsedEl.slideIn(anchor, {duration:.3});
59818                 this.afterSlide();
59819                 this.el.setLocation(-10000,-10000);
59820                 this.el.hide();
59821                 this.fireEvent("collapsed", this);
59822             },
59823             scope: this,
59824             block: true
59825         });
59826     },
59827
59828     animateExpand : function(){
59829         this.beforeSlide();
59830         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
59831         this.el.setStyle("z-index", 20000);
59832         this.collapsedEl.hide({
59833             duration:.1
59834         });
59835         this.el.slideIn(this.getSlideAnchor(), {
59836             callback : function(){
59837                 this.el.setStyle("z-index", "");
59838                 this.afterSlide();
59839                 if(this.split){
59840                     this.split.el.show();
59841                 }
59842                 this.fireEvent("invalidated", this);
59843                 this.fireEvent("expanded", this);
59844             },
59845             scope: this,
59846             block: true
59847         });
59848     },
59849
59850     anchors : {
59851         "west" : "left",
59852         "east" : "right",
59853         "north" : "top",
59854         "south" : "bottom"
59855     },
59856
59857     sanchors : {
59858         "west" : "l",
59859         "east" : "r",
59860         "north" : "t",
59861         "south" : "b"
59862     },
59863
59864     canchors : {
59865         "west" : "tl-tr",
59866         "east" : "tr-tl",
59867         "north" : "tl-bl",
59868         "south" : "bl-tl"
59869     },
59870
59871     getAnchor : function(){
59872         return this.anchors[this.position];
59873     },
59874
59875     getCollapseAnchor : function(){
59876         return this.canchors[this.position];
59877     },
59878
59879     getSlideAnchor : function(){
59880         return this.sanchors[this.position];
59881     },
59882
59883     getAlignAdj : function(){
59884         var cm = this.cmargins;
59885         switch(this.position){
59886             case "west":
59887                 return [0, 0];
59888             break;
59889             case "east":
59890                 return [0, 0];
59891             break;
59892             case "north":
59893                 return [0, 0];
59894             break;
59895             case "south":
59896                 return [0, 0];
59897             break;
59898         }
59899     },
59900
59901     getExpandAdj : function(){
59902         var c = this.collapsedEl, cm = this.cmargins;
59903         switch(this.position){
59904             case "west":
59905                 return [-(cm.right+c.getWidth()+cm.left), 0];
59906             break;
59907             case "east":
59908                 return [cm.right+c.getWidth()+cm.left, 0];
59909             break;
59910             case "north":
59911                 return [0, -(cm.top+cm.bottom+c.getHeight())];
59912             break;
59913             case "south":
59914                 return [0, cm.top+cm.bottom+c.getHeight()];
59915             break;
59916         }
59917     }
59918 });/*
59919  * Based on:
59920  * Ext JS Library 1.1.1
59921  * Copyright(c) 2006-2007, Ext JS, LLC.
59922  *
59923  * Originally Released Under LGPL - original licence link has changed is not relivant.
59924  *
59925  * Fork - LGPL
59926  * <script type="text/javascript">
59927  */
59928 /*
59929  * These classes are private internal classes
59930  */
59931 Roo.CenterLayoutRegion = function(mgr, config){
59932     Roo.LayoutRegion.call(this, mgr, config, "center");
59933     this.visible = true;
59934     this.minWidth = config.minWidth || 20;
59935     this.minHeight = config.minHeight || 20;
59936 };
59937
59938 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
59939     hide : function(){
59940         // center panel can't be hidden
59941     },
59942     
59943     show : function(){
59944         // center panel can't be hidden
59945     },
59946     
59947     getMinWidth: function(){
59948         return this.minWidth;
59949     },
59950     
59951     getMinHeight: function(){
59952         return this.minHeight;
59953     }
59954 });
59955
59956
59957 Roo.NorthLayoutRegion = function(mgr, config){
59958     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
59959     if(this.split){
59960         this.split.placement = Roo.SplitBar.TOP;
59961         this.split.orientation = Roo.SplitBar.VERTICAL;
59962         this.split.el.addClass("x-layout-split-v");
59963     }
59964     var size = config.initialSize || config.height;
59965     if(typeof size != "undefined"){
59966         this.el.setHeight(size);
59967     }
59968 };
59969 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
59970     orientation: Roo.SplitBar.VERTICAL,
59971     getBox : function(){
59972         if(this.collapsed){
59973             return this.collapsedEl.getBox();
59974         }
59975         var box = this.el.getBox();
59976         if(this.split){
59977             box.height += this.split.el.getHeight();
59978         }
59979         return box;
59980     },
59981     
59982     updateBox : function(box){
59983         if(this.split && !this.collapsed){
59984             box.height -= this.split.el.getHeight();
59985             this.split.el.setLeft(box.x);
59986             this.split.el.setTop(box.y+box.height);
59987             this.split.el.setWidth(box.width);
59988         }
59989         if(this.collapsed){
59990             this.updateBody(box.width, null);
59991         }
59992         Roo.LayoutRegion.prototype.updateBox.call(this, box);
59993     }
59994 });
59995
59996 Roo.SouthLayoutRegion = function(mgr, config){
59997     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
59998     if(this.split){
59999         this.split.placement = Roo.SplitBar.BOTTOM;
60000         this.split.orientation = Roo.SplitBar.VERTICAL;
60001         this.split.el.addClass("x-layout-split-v");
60002     }
60003     var size = config.initialSize || config.height;
60004     if(typeof size != "undefined"){
60005         this.el.setHeight(size);
60006     }
60007 };
60008 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
60009     orientation: Roo.SplitBar.VERTICAL,
60010     getBox : function(){
60011         if(this.collapsed){
60012             return this.collapsedEl.getBox();
60013         }
60014         var box = this.el.getBox();
60015         if(this.split){
60016             var sh = this.split.el.getHeight();
60017             box.height += sh;
60018             box.y -= sh;
60019         }
60020         return box;
60021     },
60022     
60023     updateBox : function(box){
60024         if(this.split && !this.collapsed){
60025             var sh = this.split.el.getHeight();
60026             box.height -= sh;
60027             box.y += sh;
60028             this.split.el.setLeft(box.x);
60029             this.split.el.setTop(box.y-sh);
60030             this.split.el.setWidth(box.width);
60031         }
60032         if(this.collapsed){
60033             this.updateBody(box.width, null);
60034         }
60035         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60036     }
60037 });
60038
60039 Roo.EastLayoutRegion = function(mgr, config){
60040     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
60041     if(this.split){
60042         this.split.placement = Roo.SplitBar.RIGHT;
60043         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60044         this.split.el.addClass("x-layout-split-h");
60045     }
60046     var size = config.initialSize || config.width;
60047     if(typeof size != "undefined"){
60048         this.el.setWidth(size);
60049     }
60050 };
60051 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
60052     orientation: Roo.SplitBar.HORIZONTAL,
60053     getBox : function(){
60054         if(this.collapsed){
60055             return this.collapsedEl.getBox();
60056         }
60057         var box = this.el.getBox();
60058         if(this.split){
60059             var sw = this.split.el.getWidth();
60060             box.width += sw;
60061             box.x -= sw;
60062         }
60063         return box;
60064     },
60065
60066     updateBox : function(box){
60067         if(this.split && !this.collapsed){
60068             var sw = this.split.el.getWidth();
60069             box.width -= sw;
60070             this.split.el.setLeft(box.x);
60071             this.split.el.setTop(box.y);
60072             this.split.el.setHeight(box.height);
60073             box.x += sw;
60074         }
60075         if(this.collapsed){
60076             this.updateBody(null, box.height);
60077         }
60078         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60079     }
60080 });
60081
60082 Roo.WestLayoutRegion = function(mgr, config){
60083     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
60084     if(this.split){
60085         this.split.placement = Roo.SplitBar.LEFT;
60086         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60087         this.split.el.addClass("x-layout-split-h");
60088     }
60089     var size = config.initialSize || config.width;
60090     if(typeof size != "undefined"){
60091         this.el.setWidth(size);
60092     }
60093 };
60094 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
60095     orientation: Roo.SplitBar.HORIZONTAL,
60096     getBox : function(){
60097         if(this.collapsed){
60098             return this.collapsedEl.getBox();
60099         }
60100         var box = this.el.getBox();
60101         if(this.split){
60102             box.width += this.split.el.getWidth();
60103         }
60104         return box;
60105     },
60106     
60107     updateBox : function(box){
60108         if(this.split && !this.collapsed){
60109             var sw = this.split.el.getWidth();
60110             box.width -= sw;
60111             this.split.el.setLeft(box.x+box.width);
60112             this.split.el.setTop(box.y);
60113             this.split.el.setHeight(box.height);
60114         }
60115         if(this.collapsed){
60116             this.updateBody(null, box.height);
60117         }
60118         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60119     }
60120 });
60121 /*
60122  * Based on:
60123  * Ext JS Library 1.1.1
60124  * Copyright(c) 2006-2007, Ext JS, LLC.
60125  *
60126  * Originally Released Under LGPL - original licence link has changed is not relivant.
60127  *
60128  * Fork - LGPL
60129  * <script type="text/javascript">
60130  */
60131  
60132  
60133 /*
60134  * Private internal class for reading and applying state
60135  */
60136 Roo.LayoutStateManager = function(layout){
60137      // default empty state
60138      this.state = {
60139         north: {},
60140         south: {},
60141         east: {},
60142         west: {}       
60143     };
60144 };
60145
60146 Roo.LayoutStateManager.prototype = {
60147     init : function(layout, provider){
60148         this.provider = provider;
60149         var state = provider.get(layout.id+"-layout-state");
60150         if(state){
60151             var wasUpdating = layout.isUpdating();
60152             if(!wasUpdating){
60153                 layout.beginUpdate();
60154             }
60155             for(var key in state){
60156                 if(typeof state[key] != "function"){
60157                     var rstate = state[key];
60158                     var r = layout.getRegion(key);
60159                     if(r && rstate){
60160                         if(rstate.size){
60161                             r.resizeTo(rstate.size);
60162                         }
60163                         if(rstate.collapsed == true){
60164                             r.collapse(true);
60165                         }else{
60166                             r.expand(null, true);
60167                         }
60168                     }
60169                 }
60170             }
60171             if(!wasUpdating){
60172                 layout.endUpdate();
60173             }
60174             this.state = state; 
60175         }
60176         this.layout = layout;
60177         layout.on("regionresized", this.onRegionResized, this);
60178         layout.on("regioncollapsed", this.onRegionCollapsed, this);
60179         layout.on("regionexpanded", this.onRegionExpanded, this);
60180     },
60181     
60182     storeState : function(){
60183         this.provider.set(this.layout.id+"-layout-state", this.state);
60184     },
60185     
60186     onRegionResized : function(region, newSize){
60187         this.state[region.getPosition()].size = newSize;
60188         this.storeState();
60189     },
60190     
60191     onRegionCollapsed : function(region){
60192         this.state[region.getPosition()].collapsed = true;
60193         this.storeState();
60194     },
60195     
60196     onRegionExpanded : function(region){
60197         this.state[region.getPosition()].collapsed = false;
60198         this.storeState();
60199     }
60200 };/*
60201  * Based on:
60202  * Ext JS Library 1.1.1
60203  * Copyright(c) 2006-2007, Ext JS, LLC.
60204  *
60205  * Originally Released Under LGPL - original licence link has changed is not relivant.
60206  *
60207  * Fork - LGPL
60208  * <script type="text/javascript">
60209  */
60210 /**
60211  * @class Roo.ContentPanel
60212  * @extends Roo.util.Observable
60213  * @children Roo.form.Form Roo.JsonView Roo.View
60214  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60215  * A basic ContentPanel element.
60216  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
60217  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
60218  * @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
60219  * @cfg {Boolean}   closable      True if the panel can be closed/removed
60220  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
60221  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
60222  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
60223  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
60224  * @cfg {String} title          The title for this panel
60225  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
60226  * @cfg {String} url            Calls {@link #setUrl} with this value
60227  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
60228  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
60229  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
60230  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
60231  * @cfg {String}    style  Extra style to add to the content panel
60232  * @cfg {Roo.menu.Menu} menu  popup menu
60233
60234  * @constructor
60235  * Create a new ContentPanel.
60236  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
60237  * @param {String/Object} config A string to set only the title or a config object
60238  * @param {String} content (optional) Set the HTML content for this panel
60239  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
60240  */
60241 Roo.ContentPanel = function(el, config, content){
60242     
60243      
60244     /*
60245     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
60246         config = el;
60247         el = Roo.id();
60248     }
60249     if (config && config.parentLayout) { 
60250         el = config.parentLayout.el.createChild(); 
60251     }
60252     */
60253     if(el.autoCreate){ // xtype is available if this is called from factory
60254         config = el;
60255         el = Roo.id();
60256     }
60257     this.el = Roo.get(el);
60258     if(!this.el && config && config.autoCreate){
60259         if(typeof config.autoCreate == "object"){
60260             if(!config.autoCreate.id){
60261                 config.autoCreate.id = config.id||el;
60262             }
60263             this.el = Roo.DomHelper.append(document.body,
60264                         config.autoCreate, true);
60265         }else{
60266             this.el = Roo.DomHelper.append(document.body,
60267                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
60268         }
60269     }
60270     
60271     
60272     this.closable = false;
60273     this.loaded = false;
60274     this.active = false;
60275     if(typeof config == "string"){
60276         this.title = config;
60277     }else{
60278         Roo.apply(this, config);
60279     }
60280     
60281     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
60282         this.wrapEl = this.el.wrap();
60283         this.toolbar.container = this.el.insertSibling(false, 'before');
60284         this.toolbar = new Roo.Toolbar(this.toolbar);
60285     }
60286     
60287     // xtype created footer. - not sure if will work as we normally have to render first..
60288     if (this.footer && !this.footer.el && this.footer.xtype) {
60289         if (!this.wrapEl) {
60290             this.wrapEl = this.el.wrap();
60291         }
60292     
60293         this.footer.container = this.wrapEl.createChild();
60294          
60295         this.footer = Roo.factory(this.footer, Roo);
60296         
60297     }
60298     
60299     if(this.resizeEl){
60300         this.resizeEl = Roo.get(this.resizeEl, true);
60301     }else{
60302         this.resizeEl = this.el;
60303     }
60304     // handle view.xtype
60305     
60306  
60307     
60308     
60309     this.addEvents({
60310         /**
60311          * @event activate
60312          * Fires when this panel is activated. 
60313          * @param {Roo.ContentPanel} this
60314          */
60315         "activate" : true,
60316         /**
60317          * @event deactivate
60318          * Fires when this panel is activated. 
60319          * @param {Roo.ContentPanel} this
60320          */
60321         "deactivate" : true,
60322
60323         /**
60324          * @event resize
60325          * Fires when this panel is resized if fitToFrame is true.
60326          * @param {Roo.ContentPanel} this
60327          * @param {Number} width The width after any component adjustments
60328          * @param {Number} height The height after any component adjustments
60329          */
60330         "resize" : true,
60331         
60332          /**
60333          * @event render
60334          * Fires when this tab is created
60335          * @param {Roo.ContentPanel} this
60336          */
60337         "render" : true
60338          
60339         
60340     });
60341     
60342
60343     
60344     
60345     if(this.autoScroll){
60346         this.resizeEl.setStyle("overflow", "auto");
60347     } else {
60348         // fix randome scrolling
60349         this.el.on('scroll', function() {
60350             Roo.log('fix random scolling');
60351             this.scrollTo('top',0); 
60352         });
60353     }
60354     content = content || this.content;
60355     if(content){
60356         this.setContent(content);
60357     }
60358     if(config && config.url){
60359         this.setUrl(this.url, this.params, this.loadOnce);
60360     }
60361     
60362     
60363     
60364     Roo.ContentPanel.superclass.constructor.call(this);
60365     
60366     if (this.view && typeof(this.view.xtype) != 'undefined') {
60367         this.view.el = this.el.appendChild(document.createElement("div"));
60368         this.view = Roo.factory(this.view); 
60369         this.view.render  &&  this.view.render(false, '');  
60370     }
60371     
60372     
60373     this.fireEvent('render', this);
60374 };
60375
60376 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
60377     tabTip:'',
60378     setRegion : function(region){
60379         this.region = region;
60380         if(region){
60381            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
60382         }else{
60383            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
60384         } 
60385     },
60386     
60387     /**
60388      * Returns the toolbar for this Panel if one was configured. 
60389      * @return {Roo.Toolbar} 
60390      */
60391     getToolbar : function(){
60392         return this.toolbar;
60393     },
60394     
60395     setActiveState : function(active){
60396         this.active = active;
60397         if(!active){
60398             this.fireEvent("deactivate", this);
60399         }else{
60400             this.fireEvent("activate", this);
60401         }
60402     },
60403     /**
60404      * Updates this panel's element
60405      * @param {String} content The new content
60406      * @param {Boolean} loadScripts (optional) true to look for and process scripts
60407     */
60408     setContent : function(content, loadScripts){
60409         this.el.update(content, loadScripts);
60410     },
60411
60412     ignoreResize : function(w, h){
60413         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
60414             return true;
60415         }else{
60416             this.lastSize = {width: w, height: h};
60417             return false;
60418         }
60419     },
60420     /**
60421      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
60422      * @return {Roo.UpdateManager} The UpdateManager
60423      */
60424     getUpdateManager : function(){
60425         return this.el.getUpdateManager();
60426     },
60427      /**
60428      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
60429      * @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:
60430 <pre><code>
60431 panel.load({
60432     url: "your-url.php",
60433     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
60434     callback: yourFunction,
60435     scope: yourObject, //(optional scope)
60436     discardUrl: false,
60437     nocache: false,
60438     text: "Loading...",
60439     timeout: 30,
60440     scripts: false
60441 });
60442 </code></pre>
60443      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
60444      * 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.
60445      * @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}
60446      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
60447      * @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.
60448      * @return {Roo.ContentPanel} this
60449      */
60450     load : function(){
60451         var um = this.el.getUpdateManager();
60452         um.update.apply(um, arguments);
60453         return this;
60454     },
60455
60456
60457     /**
60458      * 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.
60459      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
60460      * @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)
60461      * @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)
60462      * @return {Roo.UpdateManager} The UpdateManager
60463      */
60464     setUrl : function(url, params, loadOnce){
60465         if(this.refreshDelegate){
60466             this.removeListener("activate", this.refreshDelegate);
60467         }
60468         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
60469         this.on("activate", this.refreshDelegate);
60470         return this.el.getUpdateManager();
60471     },
60472     
60473     _handleRefresh : function(url, params, loadOnce){
60474         if(!loadOnce || !this.loaded){
60475             var updater = this.el.getUpdateManager();
60476             updater.update(url, params, this._setLoaded.createDelegate(this));
60477         }
60478     },
60479     
60480     _setLoaded : function(){
60481         this.loaded = true;
60482     }, 
60483     
60484     /**
60485      * Returns this panel's id
60486      * @return {String} 
60487      */
60488     getId : function(){
60489         return this.el.id;
60490     },
60491     
60492     /** 
60493      * Returns this panel's element - used by regiosn to add.
60494      * @return {Roo.Element} 
60495      */
60496     getEl : function(){
60497         return this.wrapEl || this.el;
60498     },
60499     
60500     adjustForComponents : function(width, height)
60501     {
60502         //Roo.log('adjustForComponents ');
60503         if(this.resizeEl != this.el){
60504             width -= this.el.getFrameWidth('lr');
60505             height -= this.el.getFrameWidth('tb');
60506         }
60507         if(this.toolbar){
60508             var te = this.toolbar.getEl();
60509             height -= te.getHeight();
60510             te.setWidth(width);
60511         }
60512         if(this.footer){
60513             var te = this.footer.getEl();
60514             //Roo.log("footer:" + te.getHeight());
60515             
60516             height -= te.getHeight();
60517             te.setWidth(width);
60518         }
60519         
60520         
60521         if(this.adjustments){
60522             width += this.adjustments[0];
60523             height += this.adjustments[1];
60524         }
60525         return {"width": width, "height": height};
60526     },
60527     
60528     setSize : function(width, height){
60529         if(this.fitToFrame && !this.ignoreResize(width, height)){
60530             if(this.fitContainer && this.resizeEl != this.el){
60531                 this.el.setSize(width, height);
60532             }
60533             var size = this.adjustForComponents(width, height);
60534             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
60535             this.fireEvent('resize', this, size.width, size.height);
60536         }
60537     },
60538     
60539     /**
60540      * Returns this panel's title
60541      * @return {String} 
60542      */
60543     getTitle : function(){
60544         return this.title;
60545     },
60546     
60547     /**
60548      * Set this panel's title
60549      * @param {String} title
60550      */
60551     setTitle : function(title){
60552         this.title = title;
60553         if(this.region){
60554             this.region.updatePanelTitle(this, title);
60555         }
60556     },
60557     
60558     /**
60559      * Returns true is this panel was configured to be closable
60560      * @return {Boolean} 
60561      */
60562     isClosable : function(){
60563         return this.closable;
60564     },
60565     
60566     beforeSlide : function(){
60567         this.el.clip();
60568         this.resizeEl.clip();
60569     },
60570     
60571     afterSlide : function(){
60572         this.el.unclip();
60573         this.resizeEl.unclip();
60574     },
60575     
60576     /**
60577      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
60578      *   Will fail silently if the {@link #setUrl} method has not been called.
60579      *   This does not activate the panel, just updates its content.
60580      */
60581     refresh : function(){
60582         if(this.refreshDelegate){
60583            this.loaded = false;
60584            this.refreshDelegate();
60585         }
60586     },
60587     
60588     /**
60589      * Destroys this panel
60590      */
60591     destroy : function(){
60592         this.el.removeAllListeners();
60593         var tempEl = document.createElement("span");
60594         tempEl.appendChild(this.el.dom);
60595         tempEl.innerHTML = "";
60596         this.el.remove();
60597         this.el = null;
60598     },
60599     
60600     /**
60601      * form - if the content panel contains a form - this is a reference to it.
60602      * @type {Roo.form.Form}
60603      */
60604     form : false,
60605     /**
60606      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
60607      *    This contains a reference to it.
60608      * @type {Roo.View}
60609      */
60610     view : false,
60611     
60612       /**
60613      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
60614      * <pre><code>
60615
60616 layout.addxtype({
60617        xtype : 'Form',
60618        items: [ .... ]
60619    }
60620 );
60621
60622 </code></pre>
60623      * @param {Object} cfg Xtype definition of item to add.
60624      */
60625     
60626     addxtype : function(cfg) {
60627         // add form..
60628         if (cfg.xtype.match(/^Form$/)) {
60629             
60630             var el;
60631             //if (this.footer) {
60632             //    el = this.footer.container.insertSibling(false, 'before');
60633             //} else {
60634                 el = this.el.createChild();
60635             //}
60636
60637             this.form = new  Roo.form.Form(cfg);
60638             
60639             
60640             if ( this.form.allItems.length) {
60641                 this.form.render(el.dom);
60642             }
60643             return this.form;
60644         }
60645         // should only have one of theses..
60646         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
60647             // views.. should not be just added - used named prop 'view''
60648             
60649             cfg.el = this.el.appendChild(document.createElement("div"));
60650             // factory?
60651             
60652             var ret = new Roo.factory(cfg);
60653              
60654              ret.render && ret.render(false, ''); // render blank..
60655             this.view = ret;
60656             return ret;
60657         }
60658         return false;
60659     }
60660 });
60661
60662
60663
60664
60665
60666
60667
60668
60669
60670
60671
60672
60673 /**
60674  * @class Roo.GridPanel
60675  * @extends Roo.ContentPanel
60676  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60677  * @constructor
60678  * Create a new GridPanel.
60679  * @cfg {Roo.grid.Grid} grid The grid for this panel
60680  */
60681 Roo.GridPanel = function(grid, config){
60682     
60683     // universal ctor...
60684     if (typeof(grid.grid) != 'undefined') {
60685         config = grid;
60686         grid = config.grid;
60687     }
60688     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
60689         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
60690         
60691     this.wrapper.dom.appendChild(grid.getGridEl().dom);
60692     
60693     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
60694     
60695     if(this.toolbar){
60696         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
60697     }
60698     // xtype created footer. - not sure if will work as we normally have to render first..
60699     if (this.footer && !this.footer.el && this.footer.xtype) {
60700         
60701         this.footer.container = this.grid.getView().getFooterPanel(true);
60702         this.footer.dataSource = this.grid.dataSource;
60703         this.footer = Roo.factory(this.footer, Roo);
60704         
60705     }
60706     
60707     grid.monitorWindowResize = false; // turn off autosizing
60708     grid.autoHeight = false;
60709     grid.autoWidth = false;
60710     this.grid = grid;
60711     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
60712 };
60713
60714 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
60715     getId : function(){
60716         return this.grid.id;
60717     },
60718     
60719     /**
60720      * Returns the grid for this panel
60721      * @return {Roo.grid.Grid} 
60722      */
60723     getGrid : function(){
60724         return this.grid;    
60725     },
60726     
60727     setSize : function(width, height){
60728         if(!this.ignoreResize(width, height)){
60729             var grid = this.grid;
60730             var size = this.adjustForComponents(width, height);
60731             grid.getGridEl().setSize(size.width, size.height);
60732             grid.autoSize();
60733         }
60734     },
60735     
60736     beforeSlide : function(){
60737         this.grid.getView().scroller.clip();
60738     },
60739     
60740     afterSlide : function(){
60741         this.grid.getView().scroller.unclip();
60742     },
60743     
60744     destroy : function(){
60745         this.grid.destroy();
60746         delete this.grid;
60747         Roo.GridPanel.superclass.destroy.call(this); 
60748     }
60749 });
60750
60751
60752 /**
60753  * @class Roo.NestedLayoutPanel
60754  * @extends Roo.ContentPanel
60755  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60756  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
60757  *
60758  * 
60759  * @constructor
60760  * Create a new NestedLayoutPanel.
60761  * 
60762  * 
60763  * @param {Roo.BorderLayout} layout [required] The layout for this panel
60764  * @param {String/Object} config A string to set only the title or a config object
60765  */
60766 Roo.NestedLayoutPanel = function(layout, config)
60767 {
60768     // construct with only one argument..
60769     /* FIXME - implement nicer consturctors
60770     if (layout.layout) {
60771         config = layout;
60772         layout = config.layout;
60773         delete config.layout;
60774     }
60775     if (layout.xtype && !layout.getEl) {
60776         // then layout needs constructing..
60777         layout = Roo.factory(layout, Roo);
60778     }
60779     */
60780     
60781     
60782     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
60783     
60784     layout.monitorWindowResize = false; // turn off autosizing
60785     this.layout = layout;
60786     this.layout.getEl().addClass("x-layout-nested-layout");
60787     
60788     
60789     
60790     
60791 };
60792
60793 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
60794
60795     layout : false,
60796
60797     setSize : function(width, height){
60798         if(!this.ignoreResize(width, height)){
60799             var size = this.adjustForComponents(width, height);
60800             var el = this.layout.getEl();
60801             el.setSize(size.width, size.height);
60802             var touch = el.dom.offsetWidth;
60803             this.layout.layout();
60804             // ie requires a double layout on the first pass
60805             if(Roo.isIE && !this.initialized){
60806                 this.initialized = true;
60807                 this.layout.layout();
60808             }
60809         }
60810     },
60811     
60812     // activate all subpanels if not currently active..
60813     
60814     setActiveState : function(active){
60815         this.active = active;
60816         if(!active){
60817             this.fireEvent("deactivate", this);
60818             return;
60819         }
60820         
60821         this.fireEvent("activate", this);
60822         // not sure if this should happen before or after..
60823         if (!this.layout) {
60824             return; // should not happen..
60825         }
60826         var reg = false;
60827         for (var r in this.layout.regions) {
60828             reg = this.layout.getRegion(r);
60829             if (reg.getActivePanel()) {
60830                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
60831                 reg.setActivePanel(reg.getActivePanel());
60832                 continue;
60833             }
60834             if (!reg.panels.length) {
60835                 continue;
60836             }
60837             reg.showPanel(reg.getPanel(0));
60838         }
60839         
60840         
60841         
60842         
60843     },
60844     
60845     /**
60846      * Returns the nested BorderLayout for this panel
60847      * @return {Roo.BorderLayout}
60848      */
60849     getLayout : function(){
60850         return this.layout;
60851     },
60852     
60853      /**
60854      * Adds a xtype elements to the layout of the nested panel
60855      * <pre><code>
60856
60857 panel.addxtype({
60858        xtype : 'ContentPanel',
60859        region: 'west',
60860        items: [ .... ]
60861    }
60862 );
60863
60864 panel.addxtype({
60865         xtype : 'NestedLayoutPanel',
60866         region: 'west',
60867         layout: {
60868            center: { },
60869            west: { }   
60870         },
60871         items : [ ... list of content panels or nested layout panels.. ]
60872    }
60873 );
60874 </code></pre>
60875      * @param {Object} cfg Xtype definition of item to add.
60876      */
60877     addxtype : function(cfg) {
60878         return this.layout.addxtype(cfg);
60879     
60880     }
60881 });
60882
60883 Roo.ScrollPanel = function(el, config, content){
60884     config = config || {};
60885     config.fitToFrame = true;
60886     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
60887     
60888     this.el.dom.style.overflow = "hidden";
60889     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
60890     this.el.removeClass("x-layout-inactive-content");
60891     this.el.on("mousewheel", this.onWheel, this);
60892
60893     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
60894     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
60895     up.unselectable(); down.unselectable();
60896     up.on("click", this.scrollUp, this);
60897     down.on("click", this.scrollDown, this);
60898     up.addClassOnOver("x-scroller-btn-over");
60899     down.addClassOnOver("x-scroller-btn-over");
60900     up.addClassOnClick("x-scroller-btn-click");
60901     down.addClassOnClick("x-scroller-btn-click");
60902     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
60903
60904     this.resizeEl = this.el;
60905     this.el = wrap; this.up = up; this.down = down;
60906 };
60907
60908 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
60909     increment : 100,
60910     wheelIncrement : 5,
60911     scrollUp : function(){
60912         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
60913     },
60914
60915     scrollDown : function(){
60916         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
60917     },
60918
60919     afterScroll : function(){
60920         var el = this.resizeEl;
60921         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
60922         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
60923         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
60924     },
60925
60926     setSize : function(){
60927         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
60928         this.afterScroll();
60929     },
60930
60931     onWheel : function(e){
60932         var d = e.getWheelDelta();
60933         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
60934         this.afterScroll();
60935         e.stopEvent();
60936     },
60937
60938     setContent : function(content, loadScripts){
60939         this.resizeEl.update(content, loadScripts);
60940     }
60941
60942 });
60943
60944
60945
60946 /**
60947  * @class Roo.TreePanel
60948  * @extends Roo.ContentPanel
60949  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60950  * Treepanel component
60951  * 
60952  * @constructor
60953  * Create a new TreePanel. - defaults to fit/scoll contents.
60954  * @param {String/Object} config A string to set only the panel's title, or a config object
60955  */
60956 Roo.TreePanel = function(config){
60957     var el = config.el;
60958     var tree = config.tree;
60959     delete config.tree; 
60960     delete config.el; // hopefull!
60961     
60962     // wrapper for IE7 strict & safari scroll issue
60963     
60964     var treeEl = el.createChild();
60965     config.resizeEl = treeEl;
60966     
60967     
60968     
60969     Roo.TreePanel.superclass.constructor.call(this, el, config);
60970  
60971  
60972     this.tree = new Roo.tree.TreePanel(treeEl , tree);
60973     //console.log(tree);
60974     this.on('activate', function()
60975     {
60976         if (this.tree.rendered) {
60977             return;
60978         }
60979         //console.log('render tree');
60980         this.tree.render();
60981     });
60982     // this should not be needed.. - it's actually the 'el' that resizes?
60983     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
60984     
60985     //this.on('resize',  function (cp, w, h) {
60986     //        this.tree.innerCt.setWidth(w);
60987     //        this.tree.innerCt.setHeight(h);
60988     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
60989     //});
60990
60991         
60992     
60993 };
60994
60995 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
60996     fitToFrame : true,
60997     autoScroll : true,
60998     /*
60999      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
61000      */
61001     tree : false
61002
61003 });
61004 /*
61005  * Based on:
61006  * Ext JS Library 1.1.1
61007  * Copyright(c) 2006-2007, Ext JS, LLC.
61008  *
61009  * Originally Released Under LGPL - original licence link has changed is not relivant.
61010  *
61011  * Fork - LGPL
61012  * <script type="text/javascript">
61013  */
61014  
61015
61016 /**
61017  * @class Roo.ReaderLayout
61018  * @extends Roo.BorderLayout
61019  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
61020  * center region containing two nested regions (a top one for a list view and one for item preview below),
61021  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
61022  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
61023  * expedites the setup of the overall layout and regions for this common application style.
61024  * Example:
61025  <pre><code>
61026 var reader = new Roo.ReaderLayout();
61027 var CP = Roo.ContentPanel;  // shortcut for adding
61028
61029 reader.beginUpdate();
61030 reader.add("north", new CP("north", "North"));
61031 reader.add("west", new CP("west", {title: "West"}));
61032 reader.add("east", new CP("east", {title: "East"}));
61033
61034 reader.regions.listView.add(new CP("listView", "List"));
61035 reader.regions.preview.add(new CP("preview", "Preview"));
61036 reader.endUpdate();
61037 </code></pre>
61038 * @constructor
61039 * Create a new ReaderLayout
61040 * @param {Object} config Configuration options
61041 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
61042 * document.body if omitted)
61043 */
61044 Roo.ReaderLayout = function(config, renderTo){
61045     var c = config || {size:{}};
61046     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
61047         north: c.north !== false ? Roo.apply({
61048             split:false,
61049             initialSize: 32,
61050             titlebar: false
61051         }, c.north) : false,
61052         west: c.west !== false ? Roo.apply({
61053             split:true,
61054             initialSize: 200,
61055             minSize: 175,
61056             maxSize: 400,
61057             titlebar: true,
61058             collapsible: true,
61059             animate: true,
61060             margins:{left:5,right:0,bottom:5,top:5},
61061             cmargins:{left:5,right:5,bottom:5,top:5}
61062         }, c.west) : false,
61063         east: c.east !== false ? Roo.apply({
61064             split:true,
61065             initialSize: 200,
61066             minSize: 175,
61067             maxSize: 400,
61068             titlebar: true,
61069             collapsible: true,
61070             animate: true,
61071             margins:{left:0,right:5,bottom:5,top:5},
61072             cmargins:{left:5,right:5,bottom:5,top:5}
61073         }, c.east) : false,
61074         center: Roo.apply({
61075             tabPosition: 'top',
61076             autoScroll:false,
61077             closeOnTab: true,
61078             titlebar:false,
61079             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
61080         }, c.center)
61081     });
61082
61083     this.el.addClass('x-reader');
61084
61085     this.beginUpdate();
61086
61087     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
61088         south: c.preview !== false ? Roo.apply({
61089             split:true,
61090             initialSize: 200,
61091             minSize: 100,
61092             autoScroll:true,
61093             collapsible:true,
61094             titlebar: true,
61095             cmargins:{top:5,left:0, right:0, bottom:0}
61096         }, c.preview) : false,
61097         center: Roo.apply({
61098             autoScroll:false,
61099             titlebar:false,
61100             minHeight:200
61101         }, c.listView)
61102     });
61103     this.add('center', new Roo.NestedLayoutPanel(inner,
61104             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
61105
61106     this.endUpdate();
61107
61108     this.regions.preview = inner.getRegion('south');
61109     this.regions.listView = inner.getRegion('center');
61110 };
61111
61112 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
61113  * Based on:
61114  * Ext JS Library 1.1.1
61115  * Copyright(c) 2006-2007, Ext JS, LLC.
61116  *
61117  * Originally Released Under LGPL - original licence link has changed is not relivant.
61118  *
61119  * Fork - LGPL
61120  * <script type="text/javascript">
61121  */
61122  
61123 /**
61124  * @class Roo.grid.Grid
61125  * @extends Roo.util.Observable
61126  * This class represents the primary interface of a component based grid control.
61127  * <br><br>Usage:<pre><code>
61128  var grid = new Roo.grid.Grid("my-container-id", {
61129      ds: myDataStore,
61130      cm: myColModel,
61131      selModel: mySelectionModel,
61132      autoSizeColumns: true,
61133      monitorWindowResize: false,
61134      trackMouseOver: true
61135  });
61136  // set any options
61137  grid.render();
61138  * </code></pre>
61139  * <b>Common Problems:</b><br/>
61140  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
61141  * element will correct this<br/>
61142  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
61143  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
61144  * are unpredictable.<br/>
61145  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
61146  * grid to calculate dimensions/offsets.<br/>
61147   * @constructor
61148  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61149  * The container MUST have some type of size defined for the grid to fill. The container will be
61150  * automatically set to position relative if it isn't already.
61151  * @param {Object} config A config object that sets properties on this grid.
61152  */
61153 Roo.grid.Grid = function(container, config){
61154         // initialize the container
61155         this.container = Roo.get(container);
61156         this.container.update("");
61157         this.container.setStyle("overflow", "hidden");
61158     this.container.addClass('x-grid-container');
61159
61160     this.id = this.container.id;
61161
61162     Roo.apply(this, config);
61163     // check and correct shorthanded configs
61164     if(this.ds){
61165         this.dataSource = this.ds;
61166         delete this.ds;
61167     }
61168     if(this.cm){
61169         this.colModel = this.cm;
61170         delete this.cm;
61171     }
61172     if(this.sm){
61173         this.selModel = this.sm;
61174         delete this.sm;
61175     }
61176
61177     if (this.selModel) {
61178         this.selModel = Roo.factory(this.selModel, Roo.grid);
61179         this.sm = this.selModel;
61180         this.sm.xmodule = this.xmodule || false;
61181     }
61182     if (typeof(this.colModel.config) == 'undefined') {
61183         this.colModel = new Roo.grid.ColumnModel(this.colModel);
61184         this.cm = this.colModel;
61185         this.cm.xmodule = this.xmodule || false;
61186     }
61187     if (this.dataSource) {
61188         this.dataSource= Roo.factory(this.dataSource, Roo.data);
61189         this.ds = this.dataSource;
61190         this.ds.xmodule = this.xmodule || false;
61191          
61192     }
61193     
61194     
61195     
61196     if(this.width){
61197         this.container.setWidth(this.width);
61198     }
61199
61200     if(this.height){
61201         this.container.setHeight(this.height);
61202     }
61203     /** @private */
61204         this.addEvents({
61205         // raw events
61206         /**
61207          * @event click
61208          * The raw click event for the entire grid.
61209          * @param {Roo.EventObject} e
61210          */
61211         "click" : true,
61212         /**
61213          * @event dblclick
61214          * The raw dblclick event for the entire grid.
61215          * @param {Roo.EventObject} e
61216          */
61217         "dblclick" : true,
61218         /**
61219          * @event contextmenu
61220          * The raw contextmenu event for the entire grid.
61221          * @param {Roo.EventObject} e
61222          */
61223         "contextmenu" : true,
61224         /**
61225          * @event mousedown
61226          * The raw mousedown event for the entire grid.
61227          * @param {Roo.EventObject} e
61228          */
61229         "mousedown" : true,
61230         /**
61231          * @event mouseup
61232          * The raw mouseup event for the entire grid.
61233          * @param {Roo.EventObject} e
61234          */
61235         "mouseup" : true,
61236         /**
61237          * @event mouseover
61238          * The raw mouseover event for the entire grid.
61239          * @param {Roo.EventObject} e
61240          */
61241         "mouseover" : true,
61242         /**
61243          * @event mouseout
61244          * The raw mouseout event for the entire grid.
61245          * @param {Roo.EventObject} e
61246          */
61247         "mouseout" : true,
61248         /**
61249          * @event keypress
61250          * The raw keypress event for the entire grid.
61251          * @param {Roo.EventObject} e
61252          */
61253         "keypress" : true,
61254         /**
61255          * @event keydown
61256          * The raw keydown event for the entire grid.
61257          * @param {Roo.EventObject} e
61258          */
61259         "keydown" : true,
61260
61261         // custom events
61262
61263         /**
61264          * @event cellclick
61265          * Fires when a cell is clicked
61266          * @param {Grid} this
61267          * @param {Number} rowIndex
61268          * @param {Number} columnIndex
61269          * @param {Roo.EventObject} e
61270          */
61271         "cellclick" : true,
61272         /**
61273          * @event celldblclick
61274          * Fires when a cell is double clicked
61275          * @param {Grid} this
61276          * @param {Number} rowIndex
61277          * @param {Number} columnIndex
61278          * @param {Roo.EventObject} e
61279          */
61280         "celldblclick" : true,
61281         /**
61282          * @event rowclick
61283          * Fires when a row is clicked
61284          * @param {Grid} this
61285          * @param {Number} rowIndex
61286          * @param {Roo.EventObject} e
61287          */
61288         "rowclick" : true,
61289         /**
61290          * @event rowdblclick
61291          * Fires when a row is double clicked
61292          * @param {Grid} this
61293          * @param {Number} rowIndex
61294          * @param {Roo.EventObject} e
61295          */
61296         "rowdblclick" : true,
61297         /**
61298          * @event headerclick
61299          * Fires when a header is clicked
61300          * @param {Grid} this
61301          * @param {Number} columnIndex
61302          * @param {Roo.EventObject} e
61303          */
61304         "headerclick" : true,
61305         /**
61306          * @event headerdblclick
61307          * Fires when a header cell is double clicked
61308          * @param {Grid} this
61309          * @param {Number} columnIndex
61310          * @param {Roo.EventObject} e
61311          */
61312         "headerdblclick" : true,
61313         /**
61314          * @event rowcontextmenu
61315          * Fires when a row is right clicked
61316          * @param {Grid} this
61317          * @param {Number} rowIndex
61318          * @param {Roo.EventObject} e
61319          */
61320         "rowcontextmenu" : true,
61321         /**
61322          * @event cellcontextmenu
61323          * Fires when a cell is right clicked
61324          * @param {Grid} this
61325          * @param {Number} rowIndex
61326          * @param {Number} cellIndex
61327          * @param {Roo.EventObject} e
61328          */
61329          "cellcontextmenu" : true,
61330         /**
61331          * @event headercontextmenu
61332          * Fires when a header is right clicked
61333          * @param {Grid} this
61334          * @param {Number} columnIndex
61335          * @param {Roo.EventObject} e
61336          */
61337         "headercontextmenu" : true,
61338         /**
61339          * @event bodyscroll
61340          * Fires when the body element is scrolled
61341          * @param {Number} scrollLeft
61342          * @param {Number} scrollTop
61343          */
61344         "bodyscroll" : true,
61345         /**
61346          * @event columnresize
61347          * Fires when the user resizes a column
61348          * @param {Number} columnIndex
61349          * @param {Number} newSize
61350          */
61351         "columnresize" : true,
61352         /**
61353          * @event columnmove
61354          * Fires when the user moves a column
61355          * @param {Number} oldIndex
61356          * @param {Number} newIndex
61357          */
61358         "columnmove" : true,
61359         /**
61360          * @event startdrag
61361          * Fires when row(s) start being dragged
61362          * @param {Grid} this
61363          * @param {Roo.GridDD} dd The drag drop object
61364          * @param {event} e The raw browser event
61365          */
61366         "startdrag" : true,
61367         /**
61368          * @event enddrag
61369          * Fires when a drag operation is complete
61370          * @param {Grid} this
61371          * @param {Roo.GridDD} dd The drag drop object
61372          * @param {event} e The raw browser event
61373          */
61374         "enddrag" : true,
61375         /**
61376          * @event dragdrop
61377          * Fires when dragged row(s) are dropped on a valid DD target
61378          * @param {Grid} this
61379          * @param {Roo.GridDD} dd The drag drop object
61380          * @param {String} targetId The target drag drop object
61381          * @param {event} e The raw browser event
61382          */
61383         "dragdrop" : true,
61384         /**
61385          * @event dragover
61386          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61387          * @param {Grid} this
61388          * @param {Roo.GridDD} dd The drag drop object
61389          * @param {String} targetId The target drag drop object
61390          * @param {event} e The raw browser event
61391          */
61392         "dragover" : true,
61393         /**
61394          * @event dragenter
61395          *  Fires when the dragged row(s) first cross another DD target while being dragged
61396          * @param {Grid} this
61397          * @param {Roo.GridDD} dd The drag drop object
61398          * @param {String} targetId The target drag drop object
61399          * @param {event} e The raw browser event
61400          */
61401         "dragenter" : true,
61402         /**
61403          * @event dragout
61404          * Fires when the dragged row(s) leave another DD target while being dragged
61405          * @param {Grid} this
61406          * @param {Roo.GridDD} dd The drag drop object
61407          * @param {String} targetId The target drag drop object
61408          * @param {event} e The raw browser event
61409          */
61410         "dragout" : true,
61411         /**
61412          * @event rowclass
61413          * Fires when a row is rendered, so you can change add a style to it.
61414          * @param {GridView} gridview   The grid view
61415          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61416          */
61417         'rowclass' : true,
61418
61419         /**
61420          * @event render
61421          * Fires when the grid is rendered
61422          * @param {Grid} grid
61423          */
61424         'render' : true
61425     });
61426
61427     Roo.grid.Grid.superclass.constructor.call(this);
61428 };
61429 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
61430     
61431     /**
61432          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
61433          */
61434         /**
61435          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
61436          */
61437         /**
61438          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
61439          */
61440         /**
61441          * @cfg {Roo.data.Store} ds The data store for the grid
61442          */
61443         /**
61444          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
61445          */
61446         /**
61447      * @cfg {String} ddGroup - drag drop group.
61448      */
61449       /**
61450      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
61451      */
61452
61453     /**
61454      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
61455      */
61456     minColumnWidth : 25,
61457
61458     /**
61459      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
61460      * <b>on initial render.</b> It is more efficient to explicitly size the columns
61461      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
61462      */
61463     autoSizeColumns : false,
61464
61465     /**
61466      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
61467      */
61468     autoSizeHeaders : true,
61469
61470     /**
61471      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
61472      */
61473     monitorWindowResize : true,
61474
61475     /**
61476      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
61477      * rows measured to get a columns size. Default is 0 (all rows).
61478      */
61479     maxRowsToMeasure : 0,
61480
61481     /**
61482      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
61483      */
61484     trackMouseOver : true,
61485
61486     /**
61487     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
61488     */
61489       /**
61490     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
61491     */
61492     
61493     /**
61494     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
61495     */
61496     enableDragDrop : false,
61497     
61498     /**
61499     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
61500     */
61501     enableColumnMove : true,
61502     
61503     /**
61504     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
61505     */
61506     enableColumnHide : true,
61507     
61508     /**
61509     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
61510     */
61511     enableRowHeightSync : false,
61512     
61513     /**
61514     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
61515     */
61516     stripeRows : true,
61517     
61518     /**
61519     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
61520     */
61521     autoHeight : false,
61522
61523     /**
61524      * @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.
61525      */
61526     autoExpandColumn : false,
61527
61528     /**
61529     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
61530     * Default is 50.
61531     */
61532     autoExpandMin : 50,
61533
61534     /**
61535     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
61536     */
61537     autoExpandMax : 1000,
61538
61539     /**
61540     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
61541     */
61542     view : null,
61543
61544     /**
61545     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
61546     */
61547     loadMask : false,
61548     /**
61549     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
61550     */
61551     dropTarget: false,
61552      /**
61553     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
61554     */ 
61555     sortColMenu : false,
61556     
61557     // private
61558     rendered : false,
61559
61560     /**
61561     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
61562     * of a fixed width. Default is false.
61563     */
61564     /**
61565     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
61566     */
61567     
61568     
61569     /**
61570     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
61571     * %0 is replaced with the number of selected rows.
61572     */
61573     ddText : "{0} selected row{1}",
61574     
61575     
61576     /**
61577      * Called once after all setup has been completed and the grid is ready to be rendered.
61578      * @return {Roo.grid.Grid} this
61579      */
61580     render : function()
61581     {
61582         var c = this.container;
61583         // try to detect autoHeight/width mode
61584         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
61585             this.autoHeight = true;
61586         }
61587         var view = this.getView();
61588         view.init(this);
61589
61590         c.on("click", this.onClick, this);
61591         c.on("dblclick", this.onDblClick, this);
61592         c.on("contextmenu", this.onContextMenu, this);
61593         c.on("keydown", this.onKeyDown, this);
61594         if (Roo.isTouch) {
61595             c.on("touchstart", this.onTouchStart, this);
61596         }
61597
61598         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
61599
61600         this.getSelectionModel().init(this);
61601
61602         view.render();
61603
61604         if(this.loadMask){
61605             this.loadMask = new Roo.LoadMask(this.container,
61606                     Roo.apply({store:this.dataSource}, this.loadMask));
61607         }
61608         
61609         
61610         if (this.toolbar && this.toolbar.xtype) {
61611             this.toolbar.container = this.getView().getHeaderPanel(true);
61612             this.toolbar = new Roo.Toolbar(this.toolbar);
61613         }
61614         if (this.footer && this.footer.xtype) {
61615             this.footer.dataSource = this.getDataSource();
61616             this.footer.container = this.getView().getFooterPanel(true);
61617             this.footer = Roo.factory(this.footer, Roo);
61618         }
61619         if (this.dropTarget && this.dropTarget.xtype) {
61620             delete this.dropTarget.xtype;
61621             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
61622         }
61623         
61624         
61625         this.rendered = true;
61626         this.fireEvent('render', this);
61627         return this;
61628     },
61629
61630     /**
61631      * Reconfigures the grid to use a different Store and Column Model.
61632      * The View will be bound to the new objects and refreshed.
61633      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
61634      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
61635      */
61636     reconfigure : function(dataSource, colModel){
61637         if(this.loadMask){
61638             this.loadMask.destroy();
61639             this.loadMask = new Roo.LoadMask(this.container,
61640                     Roo.apply({store:dataSource}, this.loadMask));
61641         }
61642         this.view.bind(dataSource, colModel);
61643         this.dataSource = dataSource;
61644         this.colModel = colModel;
61645         this.view.refresh(true);
61646     },
61647     /**
61648      * addColumns
61649      * Add's a column, default at the end..
61650      
61651      * @param {int} position to add (default end)
61652      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
61653      */
61654     addColumns : function(pos, ar)
61655     {
61656         
61657         for (var i =0;i< ar.length;i++) {
61658             var cfg = ar[i];
61659             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
61660             this.cm.lookup[cfg.id] = cfg;
61661         }
61662         
61663         
61664         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
61665             pos = this.cm.config.length; //this.cm.config.push(cfg);
61666         } 
61667         pos = Math.max(0,pos);
61668         ar.unshift(0);
61669         ar.unshift(pos);
61670         this.cm.config.splice.apply(this.cm.config, ar);
61671         
61672         
61673         
61674         this.view.generateRules(this.cm);
61675         this.view.refresh(true);
61676         
61677     },
61678     
61679     
61680     
61681     
61682     // private
61683     onKeyDown : function(e){
61684         this.fireEvent("keydown", e);
61685     },
61686
61687     /**
61688      * Destroy this grid.
61689      * @param {Boolean} removeEl True to remove the element
61690      */
61691     destroy : function(removeEl, keepListeners){
61692         if(this.loadMask){
61693             this.loadMask.destroy();
61694         }
61695         var c = this.container;
61696         c.removeAllListeners();
61697         this.view.destroy();
61698         this.colModel.purgeListeners();
61699         if(!keepListeners){
61700             this.purgeListeners();
61701         }
61702         c.update("");
61703         if(removeEl === true){
61704             c.remove();
61705         }
61706     },
61707
61708     // private
61709     processEvent : function(name, e){
61710         // does this fire select???
61711         //Roo.log('grid:processEvent '  + name);
61712         
61713         if (name != 'touchstart' ) {
61714             this.fireEvent(name, e);    
61715         }
61716         
61717         var t = e.getTarget();
61718         var v = this.view;
61719         var header = v.findHeaderIndex(t);
61720         if(header !== false){
61721             var ename = name == 'touchstart' ? 'click' : name;
61722              
61723             this.fireEvent("header" + ename, this, header, e);
61724         }else{
61725             var row = v.findRowIndex(t);
61726             var cell = v.findCellIndex(t);
61727             if (name == 'touchstart') {
61728                 // first touch is always a click.
61729                 // hopefull this happens after selection is updated.?
61730                 name = false;
61731                 
61732                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
61733                     var cs = this.selModel.getSelectedCell();
61734                     if (row == cs[0] && cell == cs[1]){
61735                         name = 'dblclick';
61736                     }
61737                 }
61738                 if (typeof(this.selModel.getSelections) != 'undefined') {
61739                     var cs = this.selModel.getSelections();
61740                     var ds = this.dataSource;
61741                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
61742                         name = 'dblclick';
61743                     }
61744                 }
61745                 if (!name) {
61746                     return;
61747                 }
61748             }
61749             
61750             
61751             if(row !== false){
61752                 this.fireEvent("row" + name, this, row, e);
61753                 if(cell !== false){
61754                     this.fireEvent("cell" + name, this, row, cell, e);
61755                 }
61756             }
61757         }
61758     },
61759
61760     // private
61761     onClick : function(e){
61762         this.processEvent("click", e);
61763     },
61764    // private
61765     onTouchStart : function(e){
61766         this.processEvent("touchstart", e);
61767     },
61768
61769     // private
61770     onContextMenu : function(e, t){
61771         this.processEvent("contextmenu", e);
61772     },
61773
61774     // private
61775     onDblClick : function(e){
61776         this.processEvent("dblclick", e);
61777     },
61778
61779     // private
61780     walkCells : function(row, col, step, fn, scope){
61781         var cm = this.colModel, clen = cm.getColumnCount();
61782         var ds = this.dataSource, rlen = ds.getCount(), first = true;
61783         if(step < 0){
61784             if(col < 0){
61785                 row--;
61786                 first = false;
61787             }
61788             while(row >= 0){
61789                 if(!first){
61790                     col = clen-1;
61791                 }
61792                 first = false;
61793                 while(col >= 0){
61794                     if(fn.call(scope || this, row, col, cm) === true){
61795                         return [row, col];
61796                     }
61797                     col--;
61798                 }
61799                 row--;
61800             }
61801         } else {
61802             if(col >= clen){
61803                 row++;
61804                 first = false;
61805             }
61806             while(row < rlen){
61807                 if(!first){
61808                     col = 0;
61809                 }
61810                 first = false;
61811                 while(col < clen){
61812                     if(fn.call(scope || this, row, col, cm) === true){
61813                         return [row, col];
61814                     }
61815                     col++;
61816                 }
61817                 row++;
61818             }
61819         }
61820         return null;
61821     },
61822
61823     // private
61824     getSelections : function(){
61825         return this.selModel.getSelections();
61826     },
61827
61828     /**
61829      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
61830      * but if manual update is required this method will initiate it.
61831      */
61832     autoSize : function(){
61833         if(this.rendered){
61834             this.view.layout();
61835             if(this.view.adjustForScroll){
61836                 this.view.adjustForScroll();
61837             }
61838         }
61839     },
61840
61841     /**
61842      * Returns the grid's underlying element.
61843      * @return {Element} The element
61844      */
61845     getGridEl : function(){
61846         return this.container;
61847     },
61848
61849     // private for compatibility, overridden by editor grid
61850     stopEditing : function(){},
61851
61852     /**
61853      * Returns the grid's SelectionModel.
61854      * @return {SelectionModel}
61855      */
61856     getSelectionModel : function(){
61857         if(!this.selModel){
61858             this.selModel = new Roo.grid.RowSelectionModel();
61859         }
61860         return this.selModel;
61861     },
61862
61863     /**
61864      * Returns the grid's DataSource.
61865      * @return {DataSource}
61866      */
61867     getDataSource : function(){
61868         return this.dataSource;
61869     },
61870
61871     /**
61872      * Returns the grid's ColumnModel.
61873      * @return {ColumnModel}
61874      */
61875     getColumnModel : function(){
61876         return this.colModel;
61877     },
61878
61879     /**
61880      * Returns the grid's GridView object.
61881      * @return {GridView}
61882      */
61883     getView : function(){
61884         if(!this.view){
61885             this.view = new Roo.grid.GridView(this.viewConfig);
61886             this.relayEvents(this.view, [
61887                 "beforerowremoved", "beforerowsinserted",
61888                 "beforerefresh", "rowremoved",
61889                 "rowsinserted", "rowupdated" ,"refresh"
61890             ]);
61891         }
61892         return this.view;
61893     },
61894     /**
61895      * Called to get grid's drag proxy text, by default returns this.ddText.
61896      * Override this to put something different in the dragged text.
61897      * @return {String}
61898      */
61899     getDragDropText : function(){
61900         var count = this.selModel.getCount();
61901         return String.format(this.ddText, count, count == 1 ? '' : 's');
61902     }
61903 });
61904 /*
61905  * Based on:
61906  * Ext JS Library 1.1.1
61907  * Copyright(c) 2006-2007, Ext JS, LLC.
61908  *
61909  * Originally Released Under LGPL - original licence link has changed is not relivant.
61910  *
61911  * Fork - LGPL
61912  * <script type="text/javascript">
61913  */
61914  /**
61915  * @class Roo.grid.AbstractGridView
61916  * @extends Roo.util.Observable
61917  * @abstract
61918  * Abstract base class for grid Views
61919  * @constructor
61920  */
61921 Roo.grid.AbstractGridView = function(){
61922         this.grid = null;
61923         
61924         this.events = {
61925             "beforerowremoved" : true,
61926             "beforerowsinserted" : true,
61927             "beforerefresh" : true,
61928             "rowremoved" : true,
61929             "rowsinserted" : true,
61930             "rowupdated" : true,
61931             "refresh" : true
61932         };
61933     Roo.grid.AbstractGridView.superclass.constructor.call(this);
61934 };
61935
61936 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
61937     rowClass : "x-grid-row",
61938     cellClass : "x-grid-cell",
61939     tdClass : "x-grid-td",
61940     hdClass : "x-grid-hd",
61941     splitClass : "x-grid-hd-split",
61942     
61943     init: function(grid){
61944         this.grid = grid;
61945                 var cid = this.grid.getGridEl().id;
61946         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
61947         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
61948         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
61949         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
61950         },
61951         
61952     getColumnRenderers : function(){
61953         var renderers = [];
61954         var cm = this.grid.colModel;
61955         var colCount = cm.getColumnCount();
61956         for(var i = 0; i < colCount; i++){
61957             renderers[i] = cm.getRenderer(i);
61958         }
61959         return renderers;
61960     },
61961     
61962     getColumnIds : function(){
61963         var ids = [];
61964         var cm = this.grid.colModel;
61965         var colCount = cm.getColumnCount();
61966         for(var i = 0; i < colCount; i++){
61967             ids[i] = cm.getColumnId(i);
61968         }
61969         return ids;
61970     },
61971     
61972     getDataIndexes : function(){
61973         if(!this.indexMap){
61974             this.indexMap = this.buildIndexMap();
61975         }
61976         return this.indexMap.colToData;
61977     },
61978     
61979     getColumnIndexByDataIndex : function(dataIndex){
61980         if(!this.indexMap){
61981             this.indexMap = this.buildIndexMap();
61982         }
61983         return this.indexMap.dataToCol[dataIndex];
61984     },
61985     
61986     /**
61987      * Set a css style for a column dynamically. 
61988      * @param {Number} colIndex The index of the column
61989      * @param {String} name The css property name
61990      * @param {String} value The css value
61991      */
61992     setCSSStyle : function(colIndex, name, value){
61993         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
61994         Roo.util.CSS.updateRule(selector, name, value);
61995     },
61996     
61997     generateRules : function(cm){
61998         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
61999         Roo.util.CSS.removeStyleSheet(rulesId);
62000         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62001             var cid = cm.getColumnId(i);
62002             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
62003                          this.tdSelector, cid, " {\n}\n",
62004                          this.hdSelector, cid, " {\n}\n",
62005                          this.splitSelector, cid, " {\n}\n");
62006         }
62007         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62008     }
62009 });/*
62010  * Based on:
62011  * Ext JS Library 1.1.1
62012  * Copyright(c) 2006-2007, Ext JS, LLC.
62013  *
62014  * Originally Released Under LGPL - original licence link has changed is not relivant.
62015  *
62016  * Fork - LGPL
62017  * <script type="text/javascript">
62018  */
62019
62020 // private
62021 // This is a support class used internally by the Grid components
62022 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
62023     this.grid = grid;
62024     this.view = grid.getView();
62025     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62026     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
62027     if(hd2){
62028         this.setHandleElId(Roo.id(hd));
62029         this.setOuterHandleElId(Roo.id(hd2));
62030     }
62031     this.scroll = false;
62032 };
62033 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
62034     maxDragWidth: 120,
62035     getDragData : function(e){
62036         var t = Roo.lib.Event.getTarget(e);
62037         var h = this.view.findHeaderCell(t);
62038         if(h){
62039             return {ddel: h.firstChild, header:h};
62040         }
62041         return false;
62042     },
62043
62044     onInitDrag : function(e){
62045         this.view.headersDisabled = true;
62046         var clone = this.dragData.ddel.cloneNode(true);
62047         clone.id = Roo.id();
62048         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
62049         this.proxy.update(clone);
62050         return true;
62051     },
62052
62053     afterValidDrop : function(){
62054         var v = this.view;
62055         setTimeout(function(){
62056             v.headersDisabled = false;
62057         }, 50);
62058     },
62059
62060     afterInvalidDrop : function(){
62061         var v = this.view;
62062         setTimeout(function(){
62063             v.headersDisabled = false;
62064         }, 50);
62065     }
62066 });
62067 /*
62068  * Based on:
62069  * Ext JS Library 1.1.1
62070  * Copyright(c) 2006-2007, Ext JS, LLC.
62071  *
62072  * Originally Released Under LGPL - original licence link has changed is not relivant.
62073  *
62074  * Fork - LGPL
62075  * <script type="text/javascript">
62076  */
62077 // private
62078 // This is a support class used internally by the Grid components
62079 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
62080     this.grid = grid;
62081     this.view = grid.getView();
62082     // split the proxies so they don't interfere with mouse events
62083     this.proxyTop = Roo.DomHelper.append(document.body, {
62084         cls:"col-move-top", html:"&#160;"
62085     }, true);
62086     this.proxyBottom = Roo.DomHelper.append(document.body, {
62087         cls:"col-move-bottom", html:"&#160;"
62088     }, true);
62089     this.proxyTop.hide = this.proxyBottom.hide = function(){
62090         this.setLeftTop(-100,-100);
62091         this.setStyle("visibility", "hidden");
62092     };
62093     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62094     // temporarily disabled
62095     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
62096     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
62097 };
62098 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
62099     proxyOffsets : [-4, -9],
62100     fly: Roo.Element.fly,
62101
62102     getTargetFromEvent : function(e){
62103         var t = Roo.lib.Event.getTarget(e);
62104         var cindex = this.view.findCellIndex(t);
62105         if(cindex !== false){
62106             return this.view.getHeaderCell(cindex);
62107         }
62108         return null;
62109     },
62110
62111     nextVisible : function(h){
62112         var v = this.view, cm = this.grid.colModel;
62113         h = h.nextSibling;
62114         while(h){
62115             if(!cm.isHidden(v.getCellIndex(h))){
62116                 return h;
62117             }
62118             h = h.nextSibling;
62119         }
62120         return null;
62121     },
62122
62123     prevVisible : function(h){
62124         var v = this.view, cm = this.grid.colModel;
62125         h = h.prevSibling;
62126         while(h){
62127             if(!cm.isHidden(v.getCellIndex(h))){
62128                 return h;
62129             }
62130             h = h.prevSibling;
62131         }
62132         return null;
62133     },
62134
62135     positionIndicator : function(h, n, e){
62136         var x = Roo.lib.Event.getPageX(e);
62137         var r = Roo.lib.Dom.getRegion(n.firstChild);
62138         var px, pt, py = r.top + this.proxyOffsets[1];
62139         if((r.right - x) <= (r.right-r.left)/2){
62140             px = r.right+this.view.borderWidth;
62141             pt = "after";
62142         }else{
62143             px = r.left;
62144             pt = "before";
62145         }
62146         var oldIndex = this.view.getCellIndex(h);
62147         var newIndex = this.view.getCellIndex(n);
62148
62149         if(this.grid.colModel.isFixed(newIndex)){
62150             return false;
62151         }
62152
62153         var locked = this.grid.colModel.isLocked(newIndex);
62154
62155         if(pt == "after"){
62156             newIndex++;
62157         }
62158         if(oldIndex < newIndex){
62159             newIndex--;
62160         }
62161         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
62162             return false;
62163         }
62164         px +=  this.proxyOffsets[0];
62165         this.proxyTop.setLeftTop(px, py);
62166         this.proxyTop.show();
62167         if(!this.bottomOffset){
62168             this.bottomOffset = this.view.mainHd.getHeight();
62169         }
62170         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
62171         this.proxyBottom.show();
62172         return pt;
62173     },
62174
62175     onNodeEnter : function(n, dd, e, data){
62176         if(data.header != n){
62177             this.positionIndicator(data.header, n, e);
62178         }
62179     },
62180
62181     onNodeOver : function(n, dd, e, data){
62182         var result = false;
62183         if(data.header != n){
62184             result = this.positionIndicator(data.header, n, e);
62185         }
62186         if(!result){
62187             this.proxyTop.hide();
62188             this.proxyBottom.hide();
62189         }
62190         return result ? this.dropAllowed : this.dropNotAllowed;
62191     },
62192
62193     onNodeOut : function(n, dd, e, data){
62194         this.proxyTop.hide();
62195         this.proxyBottom.hide();
62196     },
62197
62198     onNodeDrop : function(n, dd, e, data){
62199         var h = data.header;
62200         if(h != n){
62201             var cm = this.grid.colModel;
62202             var x = Roo.lib.Event.getPageX(e);
62203             var r = Roo.lib.Dom.getRegion(n.firstChild);
62204             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
62205             var oldIndex = this.view.getCellIndex(h);
62206             var newIndex = this.view.getCellIndex(n);
62207             var locked = cm.isLocked(newIndex);
62208             if(pt == "after"){
62209                 newIndex++;
62210             }
62211             if(oldIndex < newIndex){
62212                 newIndex--;
62213             }
62214             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
62215                 return false;
62216             }
62217             cm.setLocked(oldIndex, locked, true);
62218             cm.moveColumn(oldIndex, newIndex);
62219             this.grid.fireEvent("columnmove", oldIndex, newIndex);
62220             return true;
62221         }
62222         return false;
62223     }
62224 });
62225 /*
62226  * Based on:
62227  * Ext JS Library 1.1.1
62228  * Copyright(c) 2006-2007, Ext JS, LLC.
62229  *
62230  * Originally Released Under LGPL - original licence link has changed is not relivant.
62231  *
62232  * Fork - LGPL
62233  * <script type="text/javascript">
62234  */
62235   
62236 /**
62237  * @class Roo.grid.GridView
62238  * @extends Roo.util.Observable
62239  *
62240  * @constructor
62241  * @param {Object} config
62242  */
62243 Roo.grid.GridView = function(config){
62244     Roo.grid.GridView.superclass.constructor.call(this);
62245     this.el = null;
62246
62247     Roo.apply(this, config);
62248 };
62249
62250 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
62251
62252     unselectable :  'unselectable="on"',
62253     unselectableCls :  'x-unselectable',
62254     
62255     
62256     rowClass : "x-grid-row",
62257
62258     cellClass : "x-grid-col",
62259
62260     tdClass : "x-grid-td",
62261
62262     hdClass : "x-grid-hd",
62263
62264     splitClass : "x-grid-split",
62265
62266     sortClasses : ["sort-asc", "sort-desc"],
62267
62268     enableMoveAnim : false,
62269
62270     hlColor: "C3DAF9",
62271
62272     dh : Roo.DomHelper,
62273
62274     fly : Roo.Element.fly,
62275
62276     css : Roo.util.CSS,
62277
62278     borderWidth: 1,
62279
62280     splitOffset: 3,
62281
62282     scrollIncrement : 22,
62283
62284     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
62285
62286     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
62287
62288     bind : function(ds, cm){
62289         if(this.ds){
62290             this.ds.un("load", this.onLoad, this);
62291             this.ds.un("datachanged", this.onDataChange, this);
62292             this.ds.un("add", this.onAdd, this);
62293             this.ds.un("remove", this.onRemove, this);
62294             this.ds.un("update", this.onUpdate, this);
62295             this.ds.un("clear", this.onClear, this);
62296         }
62297         if(ds){
62298             ds.on("load", this.onLoad, this);
62299             ds.on("datachanged", this.onDataChange, this);
62300             ds.on("add", this.onAdd, this);
62301             ds.on("remove", this.onRemove, this);
62302             ds.on("update", this.onUpdate, this);
62303             ds.on("clear", this.onClear, this);
62304         }
62305         this.ds = ds;
62306
62307         if(this.cm){
62308             this.cm.un("widthchange", this.onColWidthChange, this);
62309             this.cm.un("headerchange", this.onHeaderChange, this);
62310             this.cm.un("hiddenchange", this.onHiddenChange, this);
62311             this.cm.un("columnmoved", this.onColumnMove, this);
62312             this.cm.un("columnlockchange", this.onColumnLock, this);
62313         }
62314         if(cm){
62315             this.generateRules(cm);
62316             cm.on("widthchange", this.onColWidthChange, this);
62317             cm.on("headerchange", this.onHeaderChange, this);
62318             cm.on("hiddenchange", this.onHiddenChange, this);
62319             cm.on("columnmoved", this.onColumnMove, this);
62320             cm.on("columnlockchange", this.onColumnLock, this);
62321         }
62322         this.cm = cm;
62323     },
62324
62325     init: function(grid){
62326         Roo.grid.GridView.superclass.init.call(this, grid);
62327
62328         this.bind(grid.dataSource, grid.colModel);
62329
62330         grid.on("headerclick", this.handleHeaderClick, this);
62331
62332         if(grid.trackMouseOver){
62333             grid.on("mouseover", this.onRowOver, this);
62334             grid.on("mouseout", this.onRowOut, this);
62335         }
62336         grid.cancelTextSelection = function(){};
62337         this.gridId = grid.id;
62338
62339         var tpls = this.templates || {};
62340
62341         if(!tpls.master){
62342             tpls.master = new Roo.Template(
62343                '<div class="x-grid" hidefocus="true">',
62344                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
62345                   '<div class="x-grid-topbar"></div>',
62346                   '<div class="x-grid-scroller"><div></div></div>',
62347                   '<div class="x-grid-locked">',
62348                       '<div class="x-grid-header">{lockedHeader}</div>',
62349                       '<div class="x-grid-body">{lockedBody}</div>',
62350                   "</div>",
62351                   '<div class="x-grid-viewport">',
62352                       '<div class="x-grid-header">{header}</div>',
62353                       '<div class="x-grid-body">{body}</div>',
62354                   "</div>",
62355                   '<div class="x-grid-bottombar"></div>',
62356                  
62357                   '<div class="x-grid-resize-proxy">&#160;</div>',
62358                "</div>"
62359             );
62360             tpls.master.disableformats = true;
62361         }
62362
62363         if(!tpls.header){
62364             tpls.header = new Roo.Template(
62365                '<table border="0" cellspacing="0" cellpadding="0">',
62366                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
62367                "</table>{splits}"
62368             );
62369             tpls.header.disableformats = true;
62370         }
62371         tpls.header.compile();
62372
62373         if(!tpls.hcell){
62374             tpls.hcell = new Roo.Template(
62375                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
62376                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
62377                 "</div></td>"
62378              );
62379              tpls.hcell.disableFormats = true;
62380         }
62381         tpls.hcell.compile();
62382
62383         if(!tpls.hsplit){
62384             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
62385                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
62386             tpls.hsplit.disableFormats = true;
62387         }
62388         tpls.hsplit.compile();
62389
62390         if(!tpls.body){
62391             tpls.body = new Roo.Template(
62392                '<table border="0" cellspacing="0" cellpadding="0">',
62393                "<tbody>{rows}</tbody>",
62394                "</table>"
62395             );
62396             tpls.body.disableFormats = true;
62397         }
62398         tpls.body.compile();
62399
62400         if(!tpls.row){
62401             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
62402             tpls.row.disableFormats = true;
62403         }
62404         tpls.row.compile();
62405
62406         if(!tpls.cell){
62407             tpls.cell = new Roo.Template(
62408                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
62409                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
62410                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
62411                 "</td>"
62412             );
62413             tpls.cell.disableFormats = true;
62414         }
62415         tpls.cell.compile();
62416
62417         this.templates = tpls;
62418     },
62419
62420     // remap these for backwards compat
62421     onColWidthChange : function(){
62422         this.updateColumns.apply(this, arguments);
62423     },
62424     onHeaderChange : function(){
62425         this.updateHeaders.apply(this, arguments);
62426     }, 
62427     onHiddenChange : function(){
62428         this.handleHiddenChange.apply(this, arguments);
62429     },
62430     onColumnMove : function(){
62431         this.handleColumnMove.apply(this, arguments);
62432     },
62433     onColumnLock : function(){
62434         this.handleLockChange.apply(this, arguments);
62435     },
62436
62437     onDataChange : function(){
62438         this.refresh();
62439         this.updateHeaderSortState();
62440     },
62441
62442     onClear : function(){
62443         this.refresh();
62444     },
62445
62446     onUpdate : function(ds, record){
62447         this.refreshRow(record);
62448     },
62449
62450     refreshRow : function(record){
62451         var ds = this.ds, index;
62452         if(typeof record == 'number'){
62453             index = record;
62454             record = ds.getAt(index);
62455         }else{
62456             index = ds.indexOf(record);
62457         }
62458         this.insertRows(ds, index, index, true);
62459         this.onRemove(ds, record, index+1, true);
62460         this.syncRowHeights(index, index);
62461         this.layout();
62462         this.fireEvent("rowupdated", this, index, record);
62463     },
62464
62465     onAdd : function(ds, records, index){
62466         this.insertRows(ds, index, index + (records.length-1));
62467     },
62468
62469     onRemove : function(ds, record, index, isUpdate){
62470         if(isUpdate !== true){
62471             this.fireEvent("beforerowremoved", this, index, record);
62472         }
62473         var bt = this.getBodyTable(), lt = this.getLockedTable();
62474         if(bt.rows[index]){
62475             bt.firstChild.removeChild(bt.rows[index]);
62476         }
62477         if(lt.rows[index]){
62478             lt.firstChild.removeChild(lt.rows[index]);
62479         }
62480         if(isUpdate !== true){
62481             this.stripeRows(index);
62482             this.syncRowHeights(index, index);
62483             this.layout();
62484             this.fireEvent("rowremoved", this, index, record);
62485         }
62486     },
62487
62488     onLoad : function(){
62489         this.scrollToTop();
62490     },
62491
62492     /**
62493      * Scrolls the grid to the top
62494      */
62495     scrollToTop : function(){
62496         if(this.scroller){
62497             this.scroller.dom.scrollTop = 0;
62498             this.syncScroll();
62499         }
62500     },
62501
62502     /**
62503      * Gets a panel in the header of the grid that can be used for toolbars etc.
62504      * After modifying the contents of this panel a call to grid.autoSize() may be
62505      * required to register any changes in size.
62506      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
62507      * @return Roo.Element
62508      */
62509     getHeaderPanel : function(doShow){
62510         if(doShow){
62511             this.headerPanel.show();
62512         }
62513         return this.headerPanel;
62514     },
62515
62516     /**
62517      * Gets a panel in the footer of the grid that can be used for toolbars etc.
62518      * After modifying the contents of this panel a call to grid.autoSize() may be
62519      * required to register any changes in size.
62520      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
62521      * @return Roo.Element
62522      */
62523     getFooterPanel : function(doShow){
62524         if(doShow){
62525             this.footerPanel.show();
62526         }
62527         return this.footerPanel;
62528     },
62529
62530     initElements : function(){
62531         var E = Roo.Element;
62532         var el = this.grid.getGridEl().dom.firstChild;
62533         var cs = el.childNodes;
62534
62535         this.el = new E(el);
62536         
62537          this.focusEl = new E(el.firstChild);
62538         this.focusEl.swallowEvent("click", true);
62539         
62540         this.headerPanel = new E(cs[1]);
62541         this.headerPanel.enableDisplayMode("block");
62542
62543         this.scroller = new E(cs[2]);
62544         this.scrollSizer = new E(this.scroller.dom.firstChild);
62545
62546         this.lockedWrap = new E(cs[3]);
62547         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
62548         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
62549
62550         this.mainWrap = new E(cs[4]);
62551         this.mainHd = new E(this.mainWrap.dom.firstChild);
62552         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
62553
62554         this.footerPanel = new E(cs[5]);
62555         this.footerPanel.enableDisplayMode("block");
62556
62557         this.resizeProxy = new E(cs[6]);
62558
62559         this.headerSelector = String.format(
62560            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
62561            this.lockedHd.id, this.mainHd.id
62562         );
62563
62564         this.splitterSelector = String.format(
62565            '#{0} div.x-grid-split, #{1} div.x-grid-split',
62566            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
62567         );
62568     },
62569     idToCssName : function(s)
62570     {
62571         return s.replace(/[^a-z0-9]+/ig, '-');
62572     },
62573
62574     getHeaderCell : function(index){
62575         return Roo.DomQuery.select(this.headerSelector)[index];
62576     },
62577
62578     getHeaderCellMeasure : function(index){
62579         return this.getHeaderCell(index).firstChild;
62580     },
62581
62582     getHeaderCellText : function(index){
62583         return this.getHeaderCell(index).firstChild.firstChild;
62584     },
62585
62586     getLockedTable : function(){
62587         return this.lockedBody.dom.firstChild;
62588     },
62589
62590     getBodyTable : function(){
62591         return this.mainBody.dom.firstChild;
62592     },
62593
62594     getLockedRow : function(index){
62595         return this.getLockedTable().rows[index];
62596     },
62597
62598     getRow : function(index){
62599         return this.getBodyTable().rows[index];
62600     },
62601
62602     getRowComposite : function(index){
62603         if(!this.rowEl){
62604             this.rowEl = new Roo.CompositeElementLite();
62605         }
62606         var els = [], lrow, mrow;
62607         if(lrow = this.getLockedRow(index)){
62608             els.push(lrow);
62609         }
62610         if(mrow = this.getRow(index)){
62611             els.push(mrow);
62612         }
62613         this.rowEl.elements = els;
62614         return this.rowEl;
62615     },
62616     /**
62617      * Gets the 'td' of the cell
62618      * 
62619      * @param {Integer} rowIndex row to select
62620      * @param {Integer} colIndex column to select
62621      * 
62622      * @return {Object} 
62623      */
62624     getCell : function(rowIndex, colIndex){
62625         var locked = this.cm.getLockedCount();
62626         var source;
62627         if(colIndex < locked){
62628             source = this.lockedBody.dom.firstChild;
62629         }else{
62630             source = this.mainBody.dom.firstChild;
62631             colIndex -= locked;
62632         }
62633         return source.rows[rowIndex].childNodes[colIndex];
62634     },
62635
62636     getCellText : function(rowIndex, colIndex){
62637         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
62638     },
62639
62640     getCellBox : function(cell){
62641         var b = this.fly(cell).getBox();
62642         if(Roo.isOpera){ // opera fails to report the Y
62643             b.y = cell.offsetTop + this.mainBody.getY();
62644         }
62645         return b;
62646     },
62647
62648     getCellIndex : function(cell){
62649         var id = String(cell.className).match(this.cellRE);
62650         if(id){
62651             return parseInt(id[1], 10);
62652         }
62653         return 0;
62654     },
62655
62656     findHeaderIndex : function(n){
62657         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62658         return r ? this.getCellIndex(r) : false;
62659     },
62660
62661     findHeaderCell : function(n){
62662         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62663         return r ? r : false;
62664     },
62665
62666     findRowIndex : function(n){
62667         if(!n){
62668             return false;
62669         }
62670         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
62671         return r ? r.rowIndex : false;
62672     },
62673
62674     findCellIndex : function(node){
62675         var stop = this.el.dom;
62676         while(node && node != stop){
62677             if(this.findRE.test(node.className)){
62678                 return this.getCellIndex(node);
62679             }
62680             node = node.parentNode;
62681         }
62682         return false;
62683     },
62684
62685     getColumnId : function(index){
62686         return this.cm.getColumnId(index);
62687     },
62688
62689     getSplitters : function()
62690     {
62691         if(this.splitterSelector){
62692            return Roo.DomQuery.select(this.splitterSelector);
62693         }else{
62694             return null;
62695       }
62696     },
62697
62698     getSplitter : function(index){
62699         return this.getSplitters()[index];
62700     },
62701
62702     onRowOver : function(e, t){
62703         var row;
62704         if((row = this.findRowIndex(t)) !== false){
62705             this.getRowComposite(row).addClass("x-grid-row-over");
62706         }
62707     },
62708
62709     onRowOut : function(e, t){
62710         var row;
62711         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
62712             this.getRowComposite(row).removeClass("x-grid-row-over");
62713         }
62714     },
62715
62716     renderHeaders : function(){
62717         var cm = this.cm;
62718         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
62719         var cb = [], lb = [], sb = [], lsb = [], p = {};
62720         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62721             p.cellId = "x-grid-hd-0-" + i;
62722             p.splitId = "x-grid-csplit-0-" + i;
62723             p.id = cm.getColumnId(i);
62724             p.value = cm.getColumnHeader(i) || "";
62725             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
62726             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
62727             if(!cm.isLocked(i)){
62728                 cb[cb.length] = ct.apply(p);
62729                 sb[sb.length] = st.apply(p);
62730             }else{
62731                 lb[lb.length] = ct.apply(p);
62732                 lsb[lsb.length] = st.apply(p);
62733             }
62734         }
62735         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
62736                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
62737     },
62738
62739     updateHeaders : function(){
62740         var html = this.renderHeaders();
62741         this.lockedHd.update(html[0]);
62742         this.mainHd.update(html[1]);
62743     },
62744
62745     /**
62746      * Focuses the specified row.
62747      * @param {Number} row The row index
62748      */
62749     focusRow : function(row)
62750     {
62751         //Roo.log('GridView.focusRow');
62752         var x = this.scroller.dom.scrollLeft;
62753         this.focusCell(row, 0, false);
62754         this.scroller.dom.scrollLeft = x;
62755     },
62756
62757     /**
62758      * Focuses the specified cell.
62759      * @param {Number} row The row index
62760      * @param {Number} col The column index
62761      * @param {Boolean} hscroll false to disable horizontal scrolling
62762      */
62763     focusCell : function(row, col, hscroll)
62764     {
62765         //Roo.log('GridView.focusCell');
62766         var el = this.ensureVisible(row, col, hscroll);
62767         this.focusEl.alignTo(el, "tl-tl");
62768         if(Roo.isGecko){
62769             this.focusEl.focus();
62770         }else{
62771             this.focusEl.focus.defer(1, this.focusEl);
62772         }
62773     },
62774
62775     /**
62776      * Scrolls the specified cell into view
62777      * @param {Number} row The row index
62778      * @param {Number} col The column index
62779      * @param {Boolean} hscroll false to disable horizontal scrolling
62780      */
62781     ensureVisible : function(row, col, hscroll)
62782     {
62783         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
62784         //return null; //disable for testing.
62785         if(typeof row != "number"){
62786             row = row.rowIndex;
62787         }
62788         if(row < 0 && row >= this.ds.getCount()){
62789             return  null;
62790         }
62791         col = (col !== undefined ? col : 0);
62792         var cm = this.grid.colModel;
62793         while(cm.isHidden(col)){
62794             col++;
62795         }
62796
62797         var el = this.getCell(row, col);
62798         if(!el){
62799             return null;
62800         }
62801         var c = this.scroller.dom;
62802
62803         var ctop = parseInt(el.offsetTop, 10);
62804         var cleft = parseInt(el.offsetLeft, 10);
62805         var cbot = ctop + el.offsetHeight;
62806         var cright = cleft + el.offsetWidth;
62807         
62808         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
62809         var stop = parseInt(c.scrollTop, 10);
62810         var sleft = parseInt(c.scrollLeft, 10);
62811         var sbot = stop + ch;
62812         var sright = sleft + c.clientWidth;
62813         /*
62814         Roo.log('GridView.ensureVisible:' +
62815                 ' ctop:' + ctop +
62816                 ' c.clientHeight:' + c.clientHeight +
62817                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
62818                 ' stop:' + stop +
62819                 ' cbot:' + cbot +
62820                 ' sbot:' + sbot +
62821                 ' ch:' + ch  
62822                 );
62823         */
62824         if(ctop < stop){
62825             c.scrollTop = ctop;
62826             //Roo.log("set scrolltop to ctop DISABLE?");
62827         }else if(cbot > sbot){
62828             //Roo.log("set scrolltop to cbot-ch");
62829             c.scrollTop = cbot-ch;
62830         }
62831         
62832         if(hscroll !== false){
62833             if(cleft < sleft){
62834                 c.scrollLeft = cleft;
62835             }else if(cright > sright){
62836                 c.scrollLeft = cright-c.clientWidth;
62837             }
62838         }
62839          
62840         return el;
62841     },
62842
62843     updateColumns : function(){
62844         this.grid.stopEditing();
62845         var cm = this.grid.colModel, colIds = this.getColumnIds();
62846         //var totalWidth = cm.getTotalWidth();
62847         var pos = 0;
62848         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62849             //if(cm.isHidden(i)) continue;
62850             var w = cm.getColumnWidth(i);
62851             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
62852             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
62853         }
62854         this.updateSplitters();
62855     },
62856
62857     generateRules : function(cm){
62858         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
62859         Roo.util.CSS.removeStyleSheet(rulesId);
62860         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62861             var cid = cm.getColumnId(i);
62862             var align = '';
62863             if(cm.config[i].align){
62864                 align = 'text-align:'+cm.config[i].align+';';
62865             }
62866             var hidden = '';
62867             if(cm.isHidden(i)){
62868                 hidden = 'display:none;';
62869             }
62870             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
62871             ruleBuf.push(
62872                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
62873                     this.hdSelector, cid, " {\n", align, width, "}\n",
62874                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
62875                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
62876         }
62877         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62878     },
62879
62880     updateSplitters : function(){
62881         var cm = this.cm, s = this.getSplitters();
62882         if(s){ // splitters not created yet
62883             var pos = 0, locked = true;
62884             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62885                 if(cm.isHidden(i)) {
62886                     continue;
62887                 }
62888                 var w = cm.getColumnWidth(i); // make sure it's a number
62889                 if(!cm.isLocked(i) && locked){
62890                     pos = 0;
62891                     locked = false;
62892                 }
62893                 pos += w;
62894                 s[i].style.left = (pos-this.splitOffset) + "px";
62895             }
62896         }
62897     },
62898
62899     handleHiddenChange : function(colModel, colIndex, hidden){
62900         if(hidden){
62901             this.hideColumn(colIndex);
62902         }else{
62903             this.unhideColumn(colIndex);
62904         }
62905     },
62906
62907     hideColumn : function(colIndex){
62908         var cid = this.getColumnId(colIndex);
62909         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
62910         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
62911         if(Roo.isSafari){
62912             this.updateHeaders();
62913         }
62914         this.updateSplitters();
62915         this.layout();
62916     },
62917
62918     unhideColumn : function(colIndex){
62919         var cid = this.getColumnId(colIndex);
62920         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
62921         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
62922
62923         if(Roo.isSafari){
62924             this.updateHeaders();
62925         }
62926         this.updateSplitters();
62927         this.layout();
62928     },
62929
62930     insertRows : function(dm, firstRow, lastRow, isUpdate){
62931         if(firstRow == 0 && lastRow == dm.getCount()-1){
62932             this.refresh();
62933         }else{
62934             if(!isUpdate){
62935                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
62936             }
62937             var s = this.getScrollState();
62938             var markup = this.renderRows(firstRow, lastRow);
62939             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
62940             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
62941             this.restoreScroll(s);
62942             if(!isUpdate){
62943                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
62944                 this.syncRowHeights(firstRow, lastRow);
62945                 this.stripeRows(firstRow);
62946                 this.layout();
62947             }
62948         }
62949     },
62950
62951     bufferRows : function(markup, target, index){
62952         var before = null, trows = target.rows, tbody = target.tBodies[0];
62953         if(index < trows.length){
62954             before = trows[index];
62955         }
62956         var b = document.createElement("div");
62957         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
62958         var rows = b.firstChild.rows;
62959         for(var i = 0, len = rows.length; i < len; i++){
62960             if(before){
62961                 tbody.insertBefore(rows[0], before);
62962             }else{
62963                 tbody.appendChild(rows[0]);
62964             }
62965         }
62966         b.innerHTML = "";
62967         b = null;
62968     },
62969
62970     deleteRows : function(dm, firstRow, lastRow){
62971         if(dm.getRowCount()<1){
62972             this.fireEvent("beforerefresh", this);
62973             this.mainBody.update("");
62974             this.lockedBody.update("");
62975             this.fireEvent("refresh", this);
62976         }else{
62977             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
62978             var bt = this.getBodyTable();
62979             var tbody = bt.firstChild;
62980             var rows = bt.rows;
62981             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
62982                 tbody.removeChild(rows[firstRow]);
62983             }
62984             this.stripeRows(firstRow);
62985             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
62986         }
62987     },
62988
62989     updateRows : function(dataSource, firstRow, lastRow){
62990         var s = this.getScrollState();
62991         this.refresh();
62992         this.restoreScroll(s);
62993     },
62994
62995     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
62996         if(!noRefresh){
62997            this.refresh();
62998         }
62999         this.updateHeaderSortState();
63000     },
63001
63002     getScrollState : function(){
63003         
63004         var sb = this.scroller.dom;
63005         return {left: sb.scrollLeft, top: sb.scrollTop};
63006     },
63007
63008     stripeRows : function(startRow){
63009         if(!this.grid.stripeRows || this.ds.getCount() < 1){
63010             return;
63011         }
63012         startRow = startRow || 0;
63013         var rows = this.getBodyTable().rows;
63014         var lrows = this.getLockedTable().rows;
63015         var cls = ' x-grid-row-alt ';
63016         for(var i = startRow, len = rows.length; i < len; i++){
63017             var row = rows[i], lrow = lrows[i];
63018             var isAlt = ((i+1) % 2 == 0);
63019             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
63020             if(isAlt == hasAlt){
63021                 continue;
63022             }
63023             if(isAlt){
63024                 row.className += " x-grid-row-alt";
63025             }else{
63026                 row.className = row.className.replace("x-grid-row-alt", "");
63027             }
63028             if(lrow){
63029                 lrow.className = row.className;
63030             }
63031         }
63032     },
63033
63034     restoreScroll : function(state){
63035         //Roo.log('GridView.restoreScroll');
63036         var sb = this.scroller.dom;
63037         sb.scrollLeft = state.left;
63038         sb.scrollTop = state.top;
63039         this.syncScroll();
63040     },
63041
63042     syncScroll : function(){
63043         //Roo.log('GridView.syncScroll');
63044         var sb = this.scroller.dom;
63045         var sh = this.mainHd.dom;
63046         var bs = this.mainBody.dom;
63047         var lv = this.lockedBody.dom;
63048         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
63049         lv.scrollTop = bs.scrollTop = sb.scrollTop;
63050     },
63051
63052     handleScroll : function(e){
63053         this.syncScroll();
63054         var sb = this.scroller.dom;
63055         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
63056         e.stopEvent();
63057     },
63058
63059     handleWheel : function(e){
63060         var d = e.getWheelDelta();
63061         this.scroller.dom.scrollTop -= d*22;
63062         // set this here to prevent jumpy scrolling on large tables
63063         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
63064         e.stopEvent();
63065     },
63066
63067     renderRows : function(startRow, endRow){
63068         // pull in all the crap needed to render rows
63069         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
63070         var colCount = cm.getColumnCount();
63071
63072         if(ds.getCount() < 1){
63073             return ["", ""];
63074         }
63075
63076         // build a map for all the columns
63077         var cs = [];
63078         for(var i = 0; i < colCount; i++){
63079             var name = cm.getDataIndex(i);
63080             cs[i] = {
63081                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
63082                 renderer : cm.getRenderer(i),
63083                 id : cm.getColumnId(i),
63084                 locked : cm.isLocked(i),
63085                 has_editor : cm.isCellEditable(i)
63086             };
63087         }
63088
63089         startRow = startRow || 0;
63090         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63091
63092         // records to render
63093         var rs = ds.getRange(startRow, endRow);
63094
63095         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63096     },
63097
63098     // As much as I hate to duplicate code, this was branched because FireFox really hates
63099     // [].join("") on strings. The performance difference was substantial enough to
63100     // branch this function
63101     doRender : Roo.isGecko ?
63102             function(cs, rs, ds, startRow, colCount, stripe){
63103                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63104                 // buffers
63105                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63106                 
63107                 var hasListener = this.grid.hasListener('rowclass');
63108                 var rowcfg = {};
63109                 for(var j = 0, len = rs.length; j < len; j++){
63110                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
63111                     for(var i = 0; i < colCount; i++){
63112                         c = cs[i];
63113                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63114                         p.id = c.id;
63115                         p.css = p.attr = "";
63116                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63117                         if(p.value == undefined || p.value === "") {
63118                             p.value = "&#160;";
63119                         }
63120                         if(c.has_editor){
63121                             p.css += ' x-grid-editable-cell';
63122                         }
63123                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
63124                             p.css +=  ' x-grid-dirty-cell';
63125                         }
63126                         var markup = ct.apply(p);
63127                         if(!c.locked){
63128                             cb+= markup;
63129                         }else{
63130                             lcb+= markup;
63131                         }
63132                     }
63133                     var alt = [];
63134                     if(stripe && ((rowIndex+1) % 2 == 0)){
63135                         alt.push("x-grid-row-alt")
63136                     }
63137                     if(r.dirty){
63138                         alt.push(  " x-grid-dirty-row");
63139                     }
63140                     rp.cells = lcb;
63141                     if(this.getRowClass){
63142                         alt.push(this.getRowClass(r, rowIndex));
63143                     }
63144                     if (hasListener) {
63145                         rowcfg = {
63146                              
63147                             record: r,
63148                             rowIndex : rowIndex,
63149                             rowClass : ''
63150                         };
63151                         this.grid.fireEvent('rowclass', this, rowcfg);
63152                         alt.push(rowcfg.rowClass);
63153                     }
63154                     rp.alt = alt.join(" ");
63155                     lbuf+= rt.apply(rp);
63156                     rp.cells = cb;
63157                     buf+=  rt.apply(rp);
63158                 }
63159                 return [lbuf, buf];
63160             } :
63161             function(cs, rs, ds, startRow, colCount, stripe){
63162                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63163                 // buffers
63164                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63165                 var hasListener = this.grid.hasListener('rowclass');
63166  
63167                 var rowcfg = {};
63168                 for(var j = 0, len = rs.length; j < len; j++){
63169                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
63170                     for(var i = 0; i < colCount; i++){
63171                         c = cs[i];
63172                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63173                         p.id = c.id;
63174                         p.css = p.attr = "";
63175                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63176                         if(p.value == undefined || p.value === "") {
63177                             p.value = "&#160;";
63178                         }
63179                         //Roo.log(c);
63180                          if(c.has_editor){
63181                             p.css += ' x-grid-editable-cell';
63182                         }
63183                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
63184                             p.css += ' x-grid-dirty-cell' 
63185                         }
63186                         
63187                         var markup = ct.apply(p);
63188                         if(!c.locked){
63189                             cb[cb.length] = markup;
63190                         }else{
63191                             lcb[lcb.length] = markup;
63192                         }
63193                     }
63194                     var alt = [];
63195                     if(stripe && ((rowIndex+1) % 2 == 0)){
63196                         alt.push( "x-grid-row-alt");
63197                     }
63198                     if(r.dirty){
63199                         alt.push(" x-grid-dirty-row");
63200                     }
63201                     rp.cells = lcb;
63202                     if(this.getRowClass){
63203                         alt.push( this.getRowClass(r, rowIndex));
63204                     }
63205                     if (hasListener) {
63206                         rowcfg = {
63207                              
63208                             record: r,
63209                             rowIndex : rowIndex,
63210                             rowClass : ''
63211                         };
63212                         this.grid.fireEvent('rowclass', this, rowcfg);
63213                         alt.push(rowcfg.rowClass);
63214                     }
63215                     
63216                     rp.alt = alt.join(" ");
63217                     rp.cells = lcb.join("");
63218                     lbuf[lbuf.length] = rt.apply(rp);
63219                     rp.cells = cb.join("");
63220                     buf[buf.length] =  rt.apply(rp);
63221                 }
63222                 return [lbuf.join(""), buf.join("")];
63223             },
63224
63225     renderBody : function(){
63226         var markup = this.renderRows();
63227         var bt = this.templates.body;
63228         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
63229     },
63230
63231     /**
63232      * Refreshes the grid
63233      * @param {Boolean} headersToo
63234      */
63235     refresh : function(headersToo){
63236         this.fireEvent("beforerefresh", this);
63237         this.grid.stopEditing();
63238         var result = this.renderBody();
63239         this.lockedBody.update(result[0]);
63240         this.mainBody.update(result[1]);
63241         if(headersToo === true){
63242             this.updateHeaders();
63243             this.updateColumns();
63244             this.updateSplitters();
63245             this.updateHeaderSortState();
63246         }
63247         this.syncRowHeights();
63248         this.layout();
63249         this.fireEvent("refresh", this);
63250     },
63251
63252     handleColumnMove : function(cm, oldIndex, newIndex){
63253         this.indexMap = null;
63254         var s = this.getScrollState();
63255         this.refresh(true);
63256         this.restoreScroll(s);
63257         this.afterMove(newIndex);
63258     },
63259
63260     afterMove : function(colIndex){
63261         if(this.enableMoveAnim && Roo.enableFx){
63262             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
63263         }
63264         // if multisort - fix sortOrder, and reload..
63265         if (this.grid.dataSource.multiSort) {
63266             // the we can call sort again..
63267             var dm = this.grid.dataSource;
63268             var cm = this.grid.colModel;
63269             var so = [];
63270             for(var i = 0; i < cm.config.length; i++ ) {
63271                 
63272                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
63273                     continue; // dont' bother, it's not in sort list or being set.
63274                 }
63275                 
63276                 so.push(cm.config[i].dataIndex);
63277             };
63278             dm.sortOrder = so;
63279             dm.load(dm.lastOptions);
63280             
63281             
63282         }
63283         
63284     },
63285
63286     updateCell : function(dm, rowIndex, dataIndex){
63287         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
63288         if(typeof colIndex == "undefined"){ // not present in grid
63289             return;
63290         }
63291         var cm = this.grid.colModel;
63292         var cell = this.getCell(rowIndex, colIndex);
63293         var cellText = this.getCellText(rowIndex, colIndex);
63294
63295         var p = {
63296             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
63297             id : cm.getColumnId(colIndex),
63298             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
63299         };
63300         var renderer = cm.getRenderer(colIndex);
63301         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
63302         if(typeof val == "undefined" || val === "") {
63303             val = "&#160;";
63304         }
63305         cellText.innerHTML = val;
63306         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
63307         this.syncRowHeights(rowIndex, rowIndex);
63308     },
63309
63310     calcColumnWidth : function(colIndex, maxRowsToMeasure){
63311         var maxWidth = 0;
63312         if(this.grid.autoSizeHeaders){
63313             var h = this.getHeaderCellMeasure(colIndex);
63314             maxWidth = Math.max(maxWidth, h.scrollWidth);
63315         }
63316         var tb, index;
63317         if(this.cm.isLocked(colIndex)){
63318             tb = this.getLockedTable();
63319             index = colIndex;
63320         }else{
63321             tb = this.getBodyTable();
63322             index = colIndex - this.cm.getLockedCount();
63323         }
63324         if(tb && tb.rows){
63325             var rows = tb.rows;
63326             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
63327             for(var i = 0; i < stopIndex; i++){
63328                 var cell = rows[i].childNodes[index].firstChild;
63329                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
63330             }
63331         }
63332         return maxWidth + /*margin for error in IE*/ 5;
63333     },
63334     /**
63335      * Autofit a column to its content.
63336      * @param {Number} colIndex
63337      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
63338      */
63339      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
63340          if(this.cm.isHidden(colIndex)){
63341              return; // can't calc a hidden column
63342          }
63343         if(forceMinSize){
63344             var cid = this.cm.getColumnId(colIndex);
63345             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
63346            if(this.grid.autoSizeHeaders){
63347                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
63348            }
63349         }
63350         var newWidth = this.calcColumnWidth(colIndex);
63351         this.cm.setColumnWidth(colIndex,
63352             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
63353         if(!suppressEvent){
63354             this.grid.fireEvent("columnresize", colIndex, newWidth);
63355         }
63356     },
63357
63358     /**
63359      * Autofits all columns to their content and then expands to fit any extra space in the grid
63360      */
63361      autoSizeColumns : function(){
63362         var cm = this.grid.colModel;
63363         var colCount = cm.getColumnCount();
63364         for(var i = 0; i < colCount; i++){
63365             this.autoSizeColumn(i, true, true);
63366         }
63367         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
63368             this.fitColumns();
63369         }else{
63370             this.updateColumns();
63371             this.layout();
63372         }
63373     },
63374
63375     /**
63376      * Autofits all columns to the grid's width proportionate with their current size
63377      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
63378      */
63379     fitColumns : function(reserveScrollSpace){
63380         var cm = this.grid.colModel;
63381         var colCount = cm.getColumnCount();
63382         var cols = [];
63383         var width = 0;
63384         var i, w;
63385         for (i = 0; i < colCount; i++){
63386             if(!cm.isHidden(i) && !cm.isFixed(i)){
63387                 w = cm.getColumnWidth(i);
63388                 cols.push(i);
63389                 cols.push(w);
63390                 width += w;
63391             }
63392         }
63393         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
63394         if(reserveScrollSpace){
63395             avail -= 17;
63396         }
63397         var frac = (avail - cm.getTotalWidth())/width;
63398         while (cols.length){
63399             w = cols.pop();
63400             i = cols.pop();
63401             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
63402         }
63403         this.updateColumns();
63404         this.layout();
63405     },
63406
63407     onRowSelect : function(rowIndex){
63408         var row = this.getRowComposite(rowIndex);
63409         row.addClass("x-grid-row-selected");
63410     },
63411
63412     onRowDeselect : function(rowIndex){
63413         var row = this.getRowComposite(rowIndex);
63414         row.removeClass("x-grid-row-selected");
63415     },
63416
63417     onCellSelect : function(row, col){
63418         var cell = this.getCell(row, col);
63419         if(cell){
63420             Roo.fly(cell).addClass("x-grid-cell-selected");
63421         }
63422     },
63423
63424     onCellDeselect : function(row, col){
63425         var cell = this.getCell(row, col);
63426         if(cell){
63427             Roo.fly(cell).removeClass("x-grid-cell-selected");
63428         }
63429     },
63430
63431     updateHeaderSortState : function(){
63432         
63433         // sort state can be single { field: xxx, direction : yyy}
63434         // or   { xxx=>ASC , yyy : DESC ..... }
63435         
63436         var mstate = {};
63437         if (!this.ds.multiSort) { 
63438             var state = this.ds.getSortState();
63439             if(!state){
63440                 return;
63441             }
63442             mstate[state.field] = state.direction;
63443             // FIXME... - this is not used here.. but might be elsewhere..
63444             this.sortState = state;
63445             
63446         } else {
63447             mstate = this.ds.sortToggle;
63448         }
63449         //remove existing sort classes..
63450         
63451         var sc = this.sortClasses;
63452         var hds = this.el.select(this.headerSelector).removeClass(sc);
63453         
63454         for(var f in mstate) {
63455         
63456             var sortColumn = this.cm.findColumnIndex(f);
63457             
63458             if(sortColumn != -1){
63459                 var sortDir = mstate[f];        
63460                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
63461             }
63462         }
63463         
63464          
63465         
63466     },
63467
63468
63469     handleHeaderClick : function(g, index,e){
63470         
63471         Roo.log("header click");
63472         
63473         if (Roo.isTouch) {
63474             // touch events on header are handled by context
63475             this.handleHdCtx(g,index,e);
63476             return;
63477         }
63478         
63479         
63480         if(this.headersDisabled){
63481             return;
63482         }
63483         var dm = g.dataSource, cm = g.colModel;
63484         if(!cm.isSortable(index)){
63485             return;
63486         }
63487         g.stopEditing();
63488         
63489         if (dm.multiSort) {
63490             // update the sortOrder
63491             var so = [];
63492             for(var i = 0; i < cm.config.length; i++ ) {
63493                 
63494                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
63495                     continue; // dont' bother, it's not in sort list or being set.
63496                 }
63497                 
63498                 so.push(cm.config[i].dataIndex);
63499             };
63500             dm.sortOrder = so;
63501         }
63502         
63503         
63504         dm.sort(cm.getDataIndex(index));
63505     },
63506
63507
63508     destroy : function(){
63509         if(this.colMenu){
63510             this.colMenu.removeAll();
63511             Roo.menu.MenuMgr.unregister(this.colMenu);
63512             this.colMenu.getEl().remove();
63513             delete this.colMenu;
63514         }
63515         if(this.hmenu){
63516             this.hmenu.removeAll();
63517             Roo.menu.MenuMgr.unregister(this.hmenu);
63518             this.hmenu.getEl().remove();
63519             delete this.hmenu;
63520         }
63521         if(this.grid.enableColumnMove){
63522             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63523             if(dds){
63524                 for(var dd in dds){
63525                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
63526                         var elid = dds[dd].dragElId;
63527                         dds[dd].unreg();
63528                         Roo.get(elid).remove();
63529                     } else if(dds[dd].config.isTarget){
63530                         dds[dd].proxyTop.remove();
63531                         dds[dd].proxyBottom.remove();
63532                         dds[dd].unreg();
63533                     }
63534                     if(Roo.dd.DDM.locationCache[dd]){
63535                         delete Roo.dd.DDM.locationCache[dd];
63536                     }
63537                 }
63538                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63539             }
63540         }
63541         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
63542         this.bind(null, null);
63543         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
63544     },
63545
63546     handleLockChange : function(){
63547         this.refresh(true);
63548     },
63549
63550     onDenyColumnLock : function(){
63551
63552     },
63553
63554     onDenyColumnHide : function(){
63555
63556     },
63557
63558     handleHdMenuClick : function(item){
63559         var index = this.hdCtxIndex;
63560         var cm = this.cm, ds = this.ds;
63561         switch(item.id){
63562             case "asc":
63563                 ds.sort(cm.getDataIndex(index), "ASC");
63564                 break;
63565             case "desc":
63566                 ds.sort(cm.getDataIndex(index), "DESC");
63567                 break;
63568             case "lock":
63569                 var lc = cm.getLockedCount();
63570                 if(cm.getColumnCount(true) <= lc+1){
63571                     this.onDenyColumnLock();
63572                     return;
63573                 }
63574                 if(lc != index){
63575                     cm.setLocked(index, true, true);
63576                     cm.moveColumn(index, lc);
63577                     this.grid.fireEvent("columnmove", index, lc);
63578                 }else{
63579                     cm.setLocked(index, true);
63580                 }
63581             break;
63582             case "unlock":
63583                 var lc = cm.getLockedCount();
63584                 if((lc-1) != index){
63585                     cm.setLocked(index, false, true);
63586                     cm.moveColumn(index, lc-1);
63587                     this.grid.fireEvent("columnmove", index, lc-1);
63588                 }else{
63589                     cm.setLocked(index, false);
63590                 }
63591             break;
63592             case 'wider': // used to expand cols on touch..
63593             case 'narrow':
63594                 var cw = cm.getColumnWidth(index);
63595                 cw += (item.id == 'wider' ? 1 : -1) * 50;
63596                 cw = Math.max(0, cw);
63597                 cw = Math.min(cw,4000);
63598                 cm.setColumnWidth(index, cw);
63599                 break;
63600                 
63601             default:
63602                 index = cm.getIndexById(item.id.substr(4));
63603                 if(index != -1){
63604                     if(item.checked && cm.getColumnCount(true) <= 1){
63605                         this.onDenyColumnHide();
63606                         return false;
63607                     }
63608                     cm.setHidden(index, item.checked);
63609                 }
63610         }
63611         return true;
63612     },
63613
63614     beforeColMenuShow : function(){
63615         var cm = this.cm,  colCount = cm.getColumnCount();
63616         this.colMenu.removeAll();
63617         
63618         var items = [];
63619         for(var i = 0; i < colCount; i++){
63620             items.push({
63621                 id: "col-"+cm.getColumnId(i),
63622                 text: cm.getColumnHeader(i),
63623                 checked: !cm.isHidden(i),
63624                 hideOnClick:false
63625             });
63626         }
63627         
63628         if (this.grid.sortColMenu) {
63629             items.sort(function(a,b) {
63630                 if (a.text == b.text) {
63631                     return 0;
63632                 }
63633                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
63634             });
63635         }
63636         
63637         for(var i = 0; i < colCount; i++){
63638             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
63639         }
63640     },
63641
63642     handleHdCtx : function(g, index, e){
63643         e.stopEvent();
63644         var hd = this.getHeaderCell(index);
63645         this.hdCtxIndex = index;
63646         var ms = this.hmenu.items, cm = this.cm;
63647         ms.get("asc").setDisabled(!cm.isSortable(index));
63648         ms.get("desc").setDisabled(!cm.isSortable(index));
63649         if(this.grid.enableColLock !== false){
63650             ms.get("lock").setDisabled(cm.isLocked(index));
63651             ms.get("unlock").setDisabled(!cm.isLocked(index));
63652         }
63653         this.hmenu.show(hd, "tl-bl");
63654     },
63655
63656     handleHdOver : function(e){
63657         var hd = this.findHeaderCell(e.getTarget());
63658         if(hd && !this.headersDisabled){
63659             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
63660                this.fly(hd).addClass("x-grid-hd-over");
63661             }
63662         }
63663     },
63664
63665     handleHdOut : function(e){
63666         var hd = this.findHeaderCell(e.getTarget());
63667         if(hd){
63668             this.fly(hd).removeClass("x-grid-hd-over");
63669         }
63670     },
63671
63672     handleSplitDblClick : function(e, t){
63673         var i = this.getCellIndex(t);
63674         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
63675             this.autoSizeColumn(i, true);
63676             this.layout();
63677         }
63678     },
63679
63680     render : function(){
63681
63682         var cm = this.cm;
63683         var colCount = cm.getColumnCount();
63684
63685         if(this.grid.monitorWindowResize === true){
63686             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
63687         }
63688         var header = this.renderHeaders();
63689         var body = this.templates.body.apply({rows:""});
63690         var html = this.templates.master.apply({
63691             lockedBody: body,
63692             body: body,
63693             lockedHeader: header[0],
63694             header: header[1]
63695         });
63696
63697         //this.updateColumns();
63698
63699         this.grid.getGridEl().dom.innerHTML = html;
63700
63701         this.initElements();
63702         
63703         // a kludge to fix the random scolling effect in webkit
63704         this.el.on("scroll", function() {
63705             this.el.dom.scrollTop=0; // hopefully not recursive..
63706         },this);
63707
63708         this.scroller.on("scroll", this.handleScroll, this);
63709         this.lockedBody.on("mousewheel", this.handleWheel, this);
63710         this.mainBody.on("mousewheel", this.handleWheel, this);
63711
63712         this.mainHd.on("mouseover", this.handleHdOver, this);
63713         this.mainHd.on("mouseout", this.handleHdOut, this);
63714         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
63715                 {delegate: "."+this.splitClass});
63716
63717         this.lockedHd.on("mouseover", this.handleHdOver, this);
63718         this.lockedHd.on("mouseout", this.handleHdOut, this);
63719         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
63720                 {delegate: "."+this.splitClass});
63721
63722         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
63723             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63724         }
63725
63726         this.updateSplitters();
63727
63728         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
63729             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63730             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63731         }
63732
63733         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
63734             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
63735             this.hmenu.add(
63736                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
63737                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
63738             );
63739             if(this.grid.enableColLock !== false){
63740                 this.hmenu.add('-',
63741                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
63742                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
63743                 );
63744             }
63745             if (Roo.isTouch) {
63746                  this.hmenu.add('-',
63747                     {id:"wider", text: this.columnsWiderText},
63748                     {id:"narrow", text: this.columnsNarrowText }
63749                 );
63750                 
63751                  
63752             }
63753             
63754             if(this.grid.enableColumnHide !== false){
63755
63756                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
63757                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
63758                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
63759
63760                 this.hmenu.add('-',
63761                     {id:"columns", text: this.columnsText, menu: this.colMenu}
63762                 );
63763             }
63764             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
63765
63766             this.grid.on("headercontextmenu", this.handleHdCtx, this);
63767         }
63768
63769         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
63770             this.dd = new Roo.grid.GridDragZone(this.grid, {
63771                 ddGroup : this.grid.ddGroup || 'GridDD'
63772             });
63773             
63774         }
63775
63776         /*
63777         for(var i = 0; i < colCount; i++){
63778             if(cm.isHidden(i)){
63779                 this.hideColumn(i);
63780             }
63781             if(cm.config[i].align){
63782                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
63783                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
63784             }
63785         }*/
63786         
63787         this.updateHeaderSortState();
63788
63789         this.beforeInitialResize();
63790         this.layout(true);
63791
63792         // two part rendering gives faster view to the user
63793         this.renderPhase2.defer(1, this);
63794     },
63795
63796     renderPhase2 : function(){
63797         // render the rows now
63798         this.refresh();
63799         if(this.grid.autoSizeColumns){
63800             this.autoSizeColumns();
63801         }
63802     },
63803
63804     beforeInitialResize : function(){
63805
63806     },
63807
63808     onColumnSplitterMoved : function(i, w){
63809         this.userResized = true;
63810         var cm = this.grid.colModel;
63811         cm.setColumnWidth(i, w, true);
63812         var cid = cm.getColumnId(i);
63813         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63814         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63815         this.updateSplitters();
63816         this.layout();
63817         this.grid.fireEvent("columnresize", i, w);
63818     },
63819
63820     syncRowHeights : function(startIndex, endIndex){
63821         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
63822             startIndex = startIndex || 0;
63823             var mrows = this.getBodyTable().rows;
63824             var lrows = this.getLockedTable().rows;
63825             var len = mrows.length-1;
63826             endIndex = Math.min(endIndex || len, len);
63827             for(var i = startIndex; i <= endIndex; i++){
63828                 var m = mrows[i], l = lrows[i];
63829                 var h = Math.max(m.offsetHeight, l.offsetHeight);
63830                 m.style.height = l.style.height = h + "px";
63831             }
63832         }
63833     },
63834
63835     layout : function(initialRender, is2ndPass)
63836     {
63837         var g = this.grid;
63838         var auto = g.autoHeight;
63839         var scrollOffset = 16;
63840         var c = g.getGridEl(), cm = this.cm,
63841                 expandCol = g.autoExpandColumn,
63842                 gv = this;
63843         //c.beginMeasure();
63844
63845         if(!c.dom.offsetWidth){ // display:none?
63846             if(initialRender){
63847                 this.lockedWrap.show();
63848                 this.mainWrap.show();
63849             }
63850             return;
63851         }
63852
63853         var hasLock = this.cm.isLocked(0);
63854
63855         var tbh = this.headerPanel.getHeight();
63856         var bbh = this.footerPanel.getHeight();
63857
63858         if(auto){
63859             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
63860             var newHeight = ch + c.getBorderWidth("tb");
63861             if(g.maxHeight){
63862                 newHeight = Math.min(g.maxHeight, newHeight);
63863             }
63864             c.setHeight(newHeight);
63865         }
63866
63867         if(g.autoWidth){
63868             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
63869         }
63870
63871         var s = this.scroller;
63872
63873         var csize = c.getSize(true);
63874
63875         this.el.setSize(csize.width, csize.height);
63876
63877         this.headerPanel.setWidth(csize.width);
63878         this.footerPanel.setWidth(csize.width);
63879
63880         var hdHeight = this.mainHd.getHeight();
63881         var vw = csize.width;
63882         var vh = csize.height - (tbh + bbh);
63883
63884         s.setSize(vw, vh);
63885
63886         var bt = this.getBodyTable();
63887         
63888         if(cm.getLockedCount() == cm.config.length){
63889             bt = this.getLockedTable();
63890         }
63891         
63892         var ltWidth = hasLock ?
63893                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
63894
63895         var scrollHeight = bt.offsetHeight;
63896         var scrollWidth = ltWidth + bt.offsetWidth;
63897         var vscroll = false, hscroll = false;
63898
63899         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
63900
63901         var lw = this.lockedWrap, mw = this.mainWrap;
63902         var lb = this.lockedBody, mb = this.mainBody;
63903
63904         setTimeout(function(){
63905             var t = s.dom.offsetTop;
63906             var w = s.dom.clientWidth,
63907                 h = s.dom.clientHeight;
63908
63909             lw.setTop(t);
63910             lw.setSize(ltWidth, h);
63911
63912             mw.setLeftTop(ltWidth, t);
63913             mw.setSize(w-ltWidth, h);
63914
63915             lb.setHeight(h-hdHeight);
63916             mb.setHeight(h-hdHeight);
63917
63918             if(is2ndPass !== true && !gv.userResized && expandCol){
63919                 // high speed resize without full column calculation
63920                 
63921                 var ci = cm.getIndexById(expandCol);
63922                 if (ci < 0) {
63923                     ci = cm.findColumnIndex(expandCol);
63924                 }
63925                 ci = Math.max(0, ci); // make sure it's got at least the first col.
63926                 var expandId = cm.getColumnId(ci);
63927                 var  tw = cm.getTotalWidth(false);
63928                 var currentWidth = cm.getColumnWidth(ci);
63929                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
63930                 if(currentWidth != cw){
63931                     cm.setColumnWidth(ci, cw, true);
63932                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
63933                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
63934                     gv.updateSplitters();
63935                     gv.layout(false, true);
63936                 }
63937             }
63938
63939             if(initialRender){
63940                 lw.show();
63941                 mw.show();
63942             }
63943             //c.endMeasure();
63944         }, 10);
63945     },
63946
63947     onWindowResize : function(){
63948         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
63949             return;
63950         }
63951         this.layout();
63952     },
63953
63954     appendFooter : function(parentEl){
63955         return null;
63956     },
63957
63958     sortAscText : "Sort Ascending",
63959     sortDescText : "Sort Descending",
63960     lockText : "Lock Column",
63961     unlockText : "Unlock Column",
63962     columnsText : "Columns",
63963  
63964     columnsWiderText : "Wider",
63965     columnsNarrowText : "Thinner"
63966 });
63967
63968
63969 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
63970     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
63971     this.proxy.el.addClass('x-grid3-col-dd');
63972 };
63973
63974 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
63975     handleMouseDown : function(e){
63976
63977     },
63978
63979     callHandleMouseDown : function(e){
63980         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
63981     }
63982 });
63983 /*
63984  * Based on:
63985  * Ext JS Library 1.1.1
63986  * Copyright(c) 2006-2007, Ext JS, LLC.
63987  *
63988  * Originally Released Under LGPL - original licence link has changed is not relivant.
63989  *
63990  * Fork - LGPL
63991  * <script type="text/javascript">
63992  */
63993  /**
63994  * @extends Roo.dd.DDProxy
63995  * @class Roo.grid.SplitDragZone
63996  * Support for Column Header resizing
63997  * @constructor
63998  * @param {Object} config
63999  */
64000 // private
64001 // This is a support class used internally by the Grid components
64002 Roo.grid.SplitDragZone = function(grid, hd, hd2){
64003     this.grid = grid;
64004     this.view = grid.getView();
64005     this.proxy = this.view.resizeProxy;
64006     Roo.grid.SplitDragZone.superclass.constructor.call(
64007         this,
64008         hd, // ID
64009         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
64010         {  // CONFIG
64011             dragElId : Roo.id(this.proxy.dom),
64012             resizeFrame:false
64013         }
64014     );
64015     
64016     this.setHandleElId(Roo.id(hd));
64017     if (hd2 !== false) {
64018         this.setOuterHandleElId(Roo.id(hd2));
64019     }
64020     
64021     this.scroll = false;
64022 };
64023 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
64024     fly: Roo.Element.fly,
64025
64026     b4StartDrag : function(x, y){
64027         this.view.headersDisabled = true;
64028         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
64029                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
64030         );
64031         this.proxy.setHeight(h);
64032         
64033         // for old system colWidth really stored the actual width?
64034         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
64035         // which in reality did not work.. - it worked only for fixed sizes
64036         // for resizable we need to use actual sizes.
64037         var w = this.cm.getColumnWidth(this.cellIndex);
64038         if (!this.view.mainWrap) {
64039             // bootstrap.
64040             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
64041         }
64042         
64043         
64044         
64045         // this was w-this.grid.minColumnWidth;
64046         // doesnt really make sense? - w = thie curren width or the rendered one?
64047         var minw = Math.max(w-this.grid.minColumnWidth, 0);
64048         this.resetConstraints();
64049         this.setXConstraint(minw, 1000);
64050         this.setYConstraint(0, 0);
64051         this.minX = x - minw;
64052         this.maxX = x + 1000;
64053         this.startPos = x;
64054         if (!this.view.mainWrap) { // this is Bootstrap code..
64055             this.getDragEl().style.display='block';
64056         }
64057         
64058         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64059     },
64060
64061
64062     handleMouseDown : function(e){
64063         ev = Roo.EventObject.setEvent(e);
64064         var t = this.fly(ev.getTarget());
64065         if(t.hasClass("x-grid-split")){
64066             this.cellIndex = this.view.getCellIndex(t.dom);
64067             this.split = t.dom;
64068             this.cm = this.grid.colModel;
64069             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64070                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64071             }
64072         }
64073     },
64074
64075     endDrag : function(e){
64076         this.view.headersDisabled = false;
64077         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
64078         var diff = endX - this.startPos;
64079         // 
64080         var w = this.cm.getColumnWidth(this.cellIndex);
64081         if (!this.view.mainWrap) {
64082             w = 0;
64083         }
64084         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
64085     },
64086
64087     autoOffset : function(){
64088         this.setDelta(0,0);
64089     }
64090 });/*
64091  * Based on:
64092  * Ext JS Library 1.1.1
64093  * Copyright(c) 2006-2007, Ext JS, LLC.
64094  *
64095  * Originally Released Under LGPL - original licence link has changed is not relivant.
64096  *
64097  * Fork - LGPL
64098  * <script type="text/javascript">
64099  */
64100  
64101 // private
64102 // This is a support class used internally by the Grid components
64103 Roo.grid.GridDragZone = function(grid, config){
64104     this.view = grid.getView();
64105     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64106     if(this.view.lockedBody){
64107         this.setHandleElId(Roo.id(this.view.mainBody.dom));
64108         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
64109     }
64110     this.scroll = false;
64111     this.grid = grid;
64112     this.ddel = document.createElement('div');
64113     this.ddel.className = 'x-grid-dd-wrap';
64114 };
64115
64116 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
64117     ddGroup : "GridDD",
64118
64119     getDragData : function(e){
64120         var t = Roo.lib.Event.getTarget(e);
64121         var rowIndex = this.view.findRowIndex(t);
64122         var sm = this.grid.selModel;
64123             
64124         //Roo.log(rowIndex);
64125         
64126         if (sm.getSelectedCell) {
64127             // cell selection..
64128             if (!sm.getSelectedCell()) {
64129                 return false;
64130             }
64131             if (rowIndex != sm.getSelectedCell()[0]) {
64132                 return false;
64133             }
64134         
64135         }
64136         if (sm.getSelections && sm.getSelections().length < 1) {
64137             return false;
64138         }
64139         
64140         
64141         // before it used to all dragging of unseleted... - now we dont do that.
64142         if(rowIndex !== false){
64143             
64144             // if editorgrid.. 
64145             
64146             
64147             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
64148                
64149             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
64150               //  
64151             //}
64152             if (e.hasModifier()){
64153                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
64154             }
64155             
64156             Roo.log("getDragData");
64157             
64158             return {
64159                 grid: this.grid,
64160                 ddel: this.ddel,
64161                 rowIndex: rowIndex,
64162                 selections: sm.getSelections ? sm.getSelections() : (
64163                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
64164             };
64165         }
64166         return false;
64167     },
64168     
64169     
64170     onInitDrag : function(e){
64171         var data = this.dragData;
64172         this.ddel.innerHTML = this.grid.getDragDropText();
64173         this.proxy.update(this.ddel);
64174         // fire start drag?
64175     },
64176
64177     afterRepair : function(){
64178         this.dragging = false;
64179     },
64180
64181     getRepairXY : function(e, data){
64182         return false;
64183     },
64184
64185     onEndDrag : function(data, e){
64186         // fire end drag?
64187     },
64188
64189     onValidDrop : function(dd, e, id){
64190         // fire drag drop?
64191         this.hideProxy();
64192     },
64193
64194     beforeInvalidDrop : function(e, id){
64195
64196     }
64197 });/*
64198  * Based on:
64199  * Ext JS Library 1.1.1
64200  * Copyright(c) 2006-2007, Ext JS, LLC.
64201  *
64202  * Originally Released Under LGPL - original licence link has changed is not relivant.
64203  *
64204  * Fork - LGPL
64205  * <script type="text/javascript">
64206  */
64207  
64208
64209 /**
64210  * @class Roo.grid.ColumnModel
64211  * @extends Roo.util.Observable
64212  * This is the default implementation of a ColumnModel used by the Grid. It defines
64213  * the columns in the grid.
64214  * <br>Usage:<br>
64215  <pre><code>
64216  var colModel = new Roo.grid.ColumnModel([
64217         {header: "Ticker", width: 60, sortable: true, locked: true},
64218         {header: "Company Name", width: 150, sortable: true},
64219         {header: "Market Cap.", width: 100, sortable: true},
64220         {header: "$ Sales", width: 100, sortable: true, renderer: money},
64221         {header: "Employees", width: 100, sortable: true, resizable: false}
64222  ]);
64223  </code></pre>
64224  * <p>
64225  
64226  * The config options listed for this class are options which may appear in each
64227  * individual column definition.
64228  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
64229  * @constructor
64230  * @param {Object} config An Array of column config objects. See this class's
64231  * config objects for details.
64232 */
64233 Roo.grid.ColumnModel = function(config){
64234         /**
64235      * The config passed into the constructor
64236      */
64237     this.config = []; //config;
64238     this.lookup = {};
64239
64240     // if no id, create one
64241     // if the column does not have a dataIndex mapping,
64242     // map it to the order it is in the config
64243     for(var i = 0, len = config.length; i < len; i++){
64244         this.addColumn(config[i]);
64245         
64246     }
64247
64248     /**
64249      * The width of columns which have no width specified (defaults to 100)
64250      * @type Number
64251      */
64252     this.defaultWidth = 100;
64253
64254     /**
64255      * Default sortable of columns which have no sortable specified (defaults to false)
64256      * @type Boolean
64257      */
64258     this.defaultSortable = false;
64259
64260     this.addEvents({
64261         /**
64262              * @event widthchange
64263              * Fires when the width of a column changes.
64264              * @param {ColumnModel} this
64265              * @param {Number} columnIndex The column index
64266              * @param {Number} newWidth The new width
64267              */
64268             "widthchange": true,
64269         /**
64270              * @event headerchange
64271              * Fires when the text of a header changes.
64272              * @param {ColumnModel} this
64273              * @param {Number} columnIndex The column index
64274              * @param {Number} newText The new header text
64275              */
64276             "headerchange": true,
64277         /**
64278              * @event hiddenchange
64279              * Fires when a column is hidden or "unhidden".
64280              * @param {ColumnModel} this
64281              * @param {Number} columnIndex The column index
64282              * @param {Boolean} hidden true if hidden, false otherwise
64283              */
64284             "hiddenchange": true,
64285             /**
64286          * @event columnmoved
64287          * Fires when a column is moved.
64288          * @param {ColumnModel} this
64289          * @param {Number} oldIndex
64290          * @param {Number} newIndex
64291          */
64292         "columnmoved" : true,
64293         /**
64294          * @event columlockchange
64295          * Fires when a column's locked state is changed
64296          * @param {ColumnModel} this
64297          * @param {Number} colIndex
64298          * @param {Boolean} locked true if locked
64299          */
64300         "columnlockchange" : true
64301     });
64302     Roo.grid.ColumnModel.superclass.constructor.call(this);
64303 };
64304 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
64305     /**
64306      * @cfg {String} header The header text to display in the Grid view.
64307      */
64308         /**
64309      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
64310      */
64311         /**
64312      * @cfg {String} smHeader Header at Bootsrap Small width
64313      */
64314         /**
64315      * @cfg {String} mdHeader Header at Bootsrap Medium width
64316      */
64317         /**
64318      * @cfg {String} lgHeader Header at Bootsrap Large width
64319      */
64320         /**
64321      * @cfg {String} xlHeader Header at Bootsrap extra Large width
64322      */
64323     /**
64324      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
64325      * {@link Roo.data.Record} definition from which to draw the column's value. If not
64326      * specified, the column's index is used as an index into the Record's data Array.
64327      */
64328     /**
64329      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
64330      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
64331      */
64332     /**
64333      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
64334      * Defaults to the value of the {@link #defaultSortable} property.
64335      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
64336      */
64337     /**
64338      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
64339      */
64340     /**
64341      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
64342      */
64343     /**
64344      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
64345      */
64346     /**
64347      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
64348      */
64349     /**
64350      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
64351      * given the cell's data value. See {@link #setRenderer}. If not specified, the
64352      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
64353      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
64354      */
64355        /**
64356      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
64357      */
64358     /**
64359      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
64360      */
64361     /**
64362      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
64363      */
64364     /**
64365      * @cfg {String} cursor (Optional)
64366      */
64367     /**
64368      * @cfg {String} tooltip (Optional)
64369      */
64370     /**
64371      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
64372      */
64373     /**
64374      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
64375      */
64376     /**
64377      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
64378      */
64379     /**
64380      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
64381      */
64382         /**
64383      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
64384      */
64385     /**
64386      * Returns the id of the column at the specified index.
64387      * @param {Number} index The column index
64388      * @return {String} the id
64389      */
64390     getColumnId : function(index){
64391         return this.config[index].id;
64392     },
64393
64394     /**
64395      * Returns the column for a specified id.
64396      * @param {String} id The column id
64397      * @return {Object} the column
64398      */
64399     getColumnById : function(id){
64400         return this.lookup[id];
64401     },
64402
64403     
64404     /**
64405      * Returns the column Object for a specified dataIndex.
64406      * @param {String} dataIndex The column dataIndex
64407      * @return {Object|Boolean} the column or false if not found
64408      */
64409     getColumnByDataIndex: function(dataIndex){
64410         var index = this.findColumnIndex(dataIndex);
64411         return index > -1 ? this.config[index] : false;
64412     },
64413     
64414     /**
64415      * Returns the index for a specified column id.
64416      * @param {String} id The column id
64417      * @return {Number} the index, or -1 if not found
64418      */
64419     getIndexById : function(id){
64420         for(var i = 0, len = this.config.length; i < len; i++){
64421             if(this.config[i].id == id){
64422                 return i;
64423             }
64424         }
64425         return -1;
64426     },
64427     
64428     /**
64429      * Returns the index for a specified column dataIndex.
64430      * @param {String} dataIndex The column dataIndex
64431      * @return {Number} the index, or -1 if not found
64432      */
64433     
64434     findColumnIndex : function(dataIndex){
64435         for(var i = 0, len = this.config.length; i < len; i++){
64436             if(this.config[i].dataIndex == dataIndex){
64437                 return i;
64438             }
64439         }
64440         return -1;
64441     },
64442     
64443     
64444     moveColumn : function(oldIndex, newIndex){
64445         var c = this.config[oldIndex];
64446         this.config.splice(oldIndex, 1);
64447         this.config.splice(newIndex, 0, c);
64448         this.dataMap = null;
64449         this.fireEvent("columnmoved", this, oldIndex, newIndex);
64450     },
64451
64452     isLocked : function(colIndex){
64453         return this.config[colIndex].locked === true;
64454     },
64455
64456     setLocked : function(colIndex, value, suppressEvent){
64457         if(this.isLocked(colIndex) == value){
64458             return;
64459         }
64460         this.config[colIndex].locked = value;
64461         if(!suppressEvent){
64462             this.fireEvent("columnlockchange", this, colIndex, value);
64463         }
64464     },
64465
64466     getTotalLockedWidth : function(){
64467         var totalWidth = 0;
64468         for(var i = 0; i < this.config.length; i++){
64469             if(this.isLocked(i) && !this.isHidden(i)){
64470                 this.totalWidth += this.getColumnWidth(i);
64471             }
64472         }
64473         return totalWidth;
64474     },
64475
64476     getLockedCount : function(){
64477         for(var i = 0, len = this.config.length; i < len; i++){
64478             if(!this.isLocked(i)){
64479                 return i;
64480             }
64481         }
64482         
64483         return this.config.length;
64484     },
64485
64486     /**
64487      * Returns the number of columns.
64488      * @return {Number}
64489      */
64490     getColumnCount : function(visibleOnly){
64491         if(visibleOnly === true){
64492             var c = 0;
64493             for(var i = 0, len = this.config.length; i < len; i++){
64494                 if(!this.isHidden(i)){
64495                     c++;
64496                 }
64497             }
64498             return c;
64499         }
64500         return this.config.length;
64501     },
64502
64503     /**
64504      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
64505      * @param {Function} fn
64506      * @param {Object} scope (optional)
64507      * @return {Array} result
64508      */
64509     getColumnsBy : function(fn, scope){
64510         var r = [];
64511         for(var i = 0, len = this.config.length; i < len; i++){
64512             var c = this.config[i];
64513             if(fn.call(scope||this, c, i) === true){
64514                 r[r.length] = c;
64515             }
64516         }
64517         return r;
64518     },
64519
64520     /**
64521      * Returns true if the specified column is sortable.
64522      * @param {Number} col The column index
64523      * @return {Boolean}
64524      */
64525     isSortable : function(col){
64526         if(typeof this.config[col].sortable == "undefined"){
64527             return this.defaultSortable;
64528         }
64529         return this.config[col].sortable;
64530     },
64531
64532     /**
64533      * Returns the rendering (formatting) function defined for the column.
64534      * @param {Number} col The column index.
64535      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
64536      */
64537     getRenderer : function(col){
64538         if(!this.config[col].renderer){
64539             return Roo.grid.ColumnModel.defaultRenderer;
64540         }
64541         return this.config[col].renderer;
64542     },
64543
64544     /**
64545      * Sets the rendering (formatting) function for a column.
64546      * @param {Number} col The column index
64547      * @param {Function} fn The function to use to process the cell's raw data
64548      * to return HTML markup for the grid view. The render function is called with
64549      * the following parameters:<ul>
64550      * <li>Data value.</li>
64551      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
64552      * <li>css A CSS style string to apply to the table cell.</li>
64553      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
64554      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
64555      * <li>Row index</li>
64556      * <li>Column index</li>
64557      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
64558      */
64559     setRenderer : function(col, fn){
64560         this.config[col].renderer = fn;
64561     },
64562
64563     /**
64564      * Returns the width for the specified column.
64565      * @param {Number} col The column index
64566      * @param (optional) {String} gridSize bootstrap width size.
64567      * @return {Number}
64568      */
64569     getColumnWidth : function(col, gridSize)
64570         {
64571                 var cfg = this.config[col];
64572                 
64573                 if (typeof(gridSize) == 'undefined') {
64574                         return cfg.width * 1 || this.defaultWidth;
64575                 }
64576                 if (gridSize === false) { // if we set it..
64577                         return cfg.width || false;
64578                 }
64579                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
64580                 
64581                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
64582                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
64583                                 continue;
64584                         }
64585                         return cfg[ sizes[i] ];
64586                 }
64587                 return 1;
64588                 
64589     },
64590
64591     /**
64592      * Sets the width for a column.
64593      * @param {Number} col The column index
64594      * @param {Number} width The new width
64595      */
64596     setColumnWidth : function(col, width, suppressEvent){
64597         this.config[col].width = width;
64598         this.totalWidth = null;
64599         if(!suppressEvent){
64600              this.fireEvent("widthchange", this, col, width);
64601         }
64602     },
64603
64604     /**
64605      * Returns the total width of all columns.
64606      * @param {Boolean} includeHidden True to include hidden column widths
64607      * @return {Number}
64608      */
64609     getTotalWidth : function(includeHidden){
64610         if(!this.totalWidth){
64611             this.totalWidth = 0;
64612             for(var i = 0, len = this.config.length; i < len; i++){
64613                 if(includeHidden || !this.isHidden(i)){
64614                     this.totalWidth += this.getColumnWidth(i);
64615                 }
64616             }
64617         }
64618         return this.totalWidth;
64619     },
64620
64621     /**
64622      * Returns the header for the specified column.
64623      * @param {Number} col The column index
64624      * @return {String}
64625      */
64626     getColumnHeader : function(col){
64627         return this.config[col].header;
64628     },
64629
64630     /**
64631      * Sets the header for a column.
64632      * @param {Number} col The column index
64633      * @param {String} header The new header
64634      */
64635     setColumnHeader : function(col, header){
64636         this.config[col].header = header;
64637         this.fireEvent("headerchange", this, col, header);
64638     },
64639
64640     /**
64641      * Returns the tooltip for the specified column.
64642      * @param {Number} col The column index
64643      * @return {String}
64644      */
64645     getColumnTooltip : function(col){
64646             return this.config[col].tooltip;
64647     },
64648     /**
64649      * Sets the tooltip for a column.
64650      * @param {Number} col The column index
64651      * @param {String} tooltip The new tooltip
64652      */
64653     setColumnTooltip : function(col, tooltip){
64654             this.config[col].tooltip = tooltip;
64655     },
64656
64657     /**
64658      * Returns the dataIndex for the specified column.
64659      * @param {Number} col The column index
64660      * @return {Number}
64661      */
64662     getDataIndex : function(col){
64663         return this.config[col].dataIndex;
64664     },
64665
64666     /**
64667      * Sets the dataIndex for a column.
64668      * @param {Number} col The column index
64669      * @param {Number} dataIndex The new dataIndex
64670      */
64671     setDataIndex : function(col, dataIndex){
64672         this.config[col].dataIndex = dataIndex;
64673     },
64674
64675     
64676     
64677     /**
64678      * Returns true if the cell is editable.
64679      * @param {Number} colIndex The column index
64680      * @param {Number} rowIndex The row index - this is nto actually used..?
64681      * @return {Boolean}
64682      */
64683     isCellEditable : function(colIndex, rowIndex){
64684         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
64685     },
64686
64687     /**
64688      * Returns the editor defined for the cell/column.
64689      * return false or null to disable editing.
64690      * @param {Number} colIndex The column index
64691      * @param {Number} rowIndex The row index
64692      * @return {Object}
64693      */
64694     getCellEditor : function(colIndex, rowIndex){
64695         return this.config[colIndex].editor;
64696     },
64697
64698     /**
64699      * Sets if a column is editable.
64700      * @param {Number} col The column index
64701      * @param {Boolean} editable True if the column is editable
64702      */
64703     setEditable : function(col, editable){
64704         this.config[col].editable = editable;
64705     },
64706
64707
64708     /**
64709      * Returns true if the column is hidden.
64710      * @param {Number} colIndex The column index
64711      * @return {Boolean}
64712      */
64713     isHidden : function(colIndex){
64714         return this.config[colIndex].hidden;
64715     },
64716
64717
64718     /**
64719      * Returns true if the column width cannot be changed
64720      */
64721     isFixed : function(colIndex){
64722         return this.config[colIndex].fixed;
64723     },
64724
64725     /**
64726      * Returns true if the column can be resized
64727      * @return {Boolean}
64728      */
64729     isResizable : function(colIndex){
64730         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
64731     },
64732     /**
64733      * Sets if a column is hidden.
64734      * @param {Number} colIndex The column index
64735      * @param {Boolean} hidden True if the column is hidden
64736      */
64737     setHidden : function(colIndex, hidden){
64738         this.config[colIndex].hidden = hidden;
64739         this.totalWidth = null;
64740         this.fireEvent("hiddenchange", this, colIndex, hidden);
64741     },
64742
64743     /**
64744      * Sets the editor for a column.
64745      * @param {Number} col The column index
64746      * @param {Object} editor The editor object
64747      */
64748     setEditor : function(col, editor){
64749         this.config[col].editor = editor;
64750     },
64751     /**
64752      * Add a column (experimental...) - defaults to adding to the end..
64753      * @param {Object} config 
64754     */
64755     addColumn : function(c)
64756     {
64757     
64758         var i = this.config.length;
64759         this.config[i] = c;
64760         
64761         if(typeof c.dataIndex == "undefined"){
64762             c.dataIndex = i;
64763         }
64764         if(typeof c.renderer == "string"){
64765             c.renderer = Roo.util.Format[c.renderer];
64766         }
64767         if(typeof c.id == "undefined"){
64768             c.id = Roo.id();
64769         }
64770         if(c.editor && c.editor.xtype){
64771             c.editor  = Roo.factory(c.editor, Roo.grid);
64772         }
64773         if(c.editor && c.editor.isFormField){
64774             c.editor = new Roo.grid.GridEditor(c.editor);
64775         }
64776         this.lookup[c.id] = c;
64777     }
64778     
64779 });
64780
64781 Roo.grid.ColumnModel.defaultRenderer = function(value)
64782 {
64783     if(typeof value == "object") {
64784         return value;
64785     }
64786         if(typeof value == "string" && value.length < 1){
64787             return "&#160;";
64788         }
64789     
64790         return String.format("{0}", value);
64791 };
64792
64793 // Alias for backwards compatibility
64794 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
64795 /*
64796  * Based on:
64797  * Ext JS Library 1.1.1
64798  * Copyright(c) 2006-2007, Ext JS, LLC.
64799  *
64800  * Originally Released Under LGPL - original licence link has changed is not relivant.
64801  *
64802  * Fork - LGPL
64803  * <script type="text/javascript">
64804  */
64805
64806 /**
64807  * @class Roo.grid.AbstractSelectionModel
64808  * @extends Roo.util.Observable
64809  * @abstract
64810  * Abstract base class for grid SelectionModels.  It provides the interface that should be
64811  * implemented by descendant classes.  This class should not be directly instantiated.
64812  * @constructor
64813  */
64814 Roo.grid.AbstractSelectionModel = function(){
64815     this.locked = false;
64816     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
64817 };
64818
64819 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
64820     /** @ignore Called by the grid automatically. Do not call directly. */
64821     init : function(grid){
64822         this.grid = grid;
64823         this.initEvents();
64824     },
64825
64826     /**
64827      * Locks the selections.
64828      */
64829     lock : function(){
64830         this.locked = true;
64831     },
64832
64833     /**
64834      * Unlocks the selections.
64835      */
64836     unlock : function(){
64837         this.locked = false;
64838     },
64839
64840     /**
64841      * Returns true if the selections are locked.
64842      * @return {Boolean}
64843      */
64844     isLocked : function(){
64845         return this.locked;
64846     }
64847 });/*
64848  * Based on:
64849  * Ext JS Library 1.1.1
64850  * Copyright(c) 2006-2007, Ext JS, LLC.
64851  *
64852  * Originally Released Under LGPL - original licence link has changed is not relivant.
64853  *
64854  * Fork - LGPL
64855  * <script type="text/javascript">
64856  */
64857 /**
64858  * @extends Roo.grid.AbstractSelectionModel
64859  * @class Roo.grid.RowSelectionModel
64860  * The default SelectionModel used by {@link Roo.grid.Grid}.
64861  * It supports multiple selections and keyboard selection/navigation. 
64862  * @constructor
64863  * @param {Object} config
64864  */
64865 Roo.grid.RowSelectionModel = function(config){
64866     Roo.apply(this, config);
64867     this.selections = new Roo.util.MixedCollection(false, function(o){
64868         return o.id;
64869     });
64870
64871     this.last = false;
64872     this.lastActive = false;
64873
64874     this.addEvents({
64875         /**
64876         * @event selectionchange
64877         * Fires when the selection changes
64878         * @param {SelectionModel} this
64879         */
64880        "selectionchange" : true,
64881        /**
64882         * @event afterselectionchange
64883         * Fires after the selection changes (eg. by key press or clicking)
64884         * @param {SelectionModel} this
64885         */
64886        "afterselectionchange" : true,
64887        /**
64888         * @event beforerowselect
64889         * Fires when a row is selected being selected, return false to cancel.
64890         * @param {SelectionModel} this
64891         * @param {Number} rowIndex The selected index
64892         * @param {Boolean} keepExisting False if other selections will be cleared
64893         */
64894        "beforerowselect" : true,
64895        /**
64896         * @event rowselect
64897         * Fires when a row is selected.
64898         * @param {SelectionModel} this
64899         * @param {Number} rowIndex The selected index
64900         * @param {Roo.data.Record} r The record
64901         */
64902        "rowselect" : true,
64903        /**
64904         * @event rowdeselect
64905         * Fires when a row is deselected.
64906         * @param {SelectionModel} this
64907         * @param {Number} rowIndex The selected index
64908         */
64909         "rowdeselect" : true
64910     });
64911     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
64912     this.locked = false;
64913 };
64914
64915 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
64916     /**
64917      * @cfg {Boolean} singleSelect
64918      * True to allow selection of only one row at a time (defaults to false)
64919      */
64920     singleSelect : false,
64921
64922     // private
64923     initEvents : function(){
64924
64925         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
64926             this.grid.on("mousedown", this.handleMouseDown, this);
64927         }else{ // allow click to work like normal
64928             this.grid.on("rowclick", this.handleDragableRowClick, this);
64929         }
64930         // bootstrap does not have a view..
64931         var view = this.grid.view ? this.grid.view : this.grid;
64932         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
64933             "up" : function(e){
64934                 if(!e.shiftKey){
64935                     this.selectPrevious(e.shiftKey);
64936                 }else if(this.last !== false && this.lastActive !== false){
64937                     var last = this.last;
64938                     this.selectRange(this.last,  this.lastActive-1);
64939                     view.focusRow(this.lastActive);
64940                     if(last !== false){
64941                         this.last = last;
64942                     }
64943                 }else{
64944                     this.selectFirstRow();
64945                 }
64946                 this.fireEvent("afterselectionchange", this);
64947             },
64948             "down" : function(e){
64949                 if(!e.shiftKey){
64950                     this.selectNext(e.shiftKey);
64951                 }else if(this.last !== false && this.lastActive !== false){
64952                     var last = this.last;
64953                     this.selectRange(this.last,  this.lastActive+1);
64954                     view.focusRow(this.lastActive);
64955                     if(last !== false){
64956                         this.last = last;
64957                     }
64958                 }else{
64959                     this.selectFirstRow();
64960                 }
64961                 this.fireEvent("afterselectionchange", this);
64962             },
64963             scope: this
64964         });
64965
64966          
64967         view.on("refresh", this.onRefresh, this);
64968         view.on("rowupdated", this.onRowUpdated, this);
64969         view.on("rowremoved", this.onRemove, this);
64970     },
64971
64972     // private
64973     onRefresh : function(){
64974         var ds = this.grid.ds, i, v = this.grid.view;
64975         var s = this.selections;
64976         s.each(function(r){
64977             if((i = ds.indexOfId(r.id)) != -1){
64978                 v.onRowSelect(i);
64979                 s.add(ds.getAt(i)); // updating the selection relate data
64980             }else{
64981                 s.remove(r);
64982             }
64983         });
64984     },
64985
64986     // private
64987     onRemove : function(v, index, r){
64988         this.selections.remove(r);
64989     },
64990
64991     // private
64992     onRowUpdated : function(v, index, r){
64993         if(this.isSelected(r)){
64994             v.onRowSelect(index);
64995         }
64996     },
64997
64998     /**
64999      * Select records.
65000      * @param {Array} records The records to select
65001      * @param {Boolean} keepExisting (optional) True to keep existing selections
65002      */
65003     selectRecords : function(records, keepExisting){
65004         if(!keepExisting){
65005             this.clearSelections();
65006         }
65007         var ds = this.grid.ds;
65008         for(var i = 0, len = records.length; i < len; i++){
65009             this.selectRow(ds.indexOf(records[i]), true);
65010         }
65011     },
65012
65013     /**
65014      * Gets the number of selected rows.
65015      * @return {Number}
65016      */
65017     getCount : function(){
65018         return this.selections.length;
65019     },
65020
65021     /**
65022      * Selects the first row in the grid.
65023      */
65024     selectFirstRow : function(){
65025         this.selectRow(0);
65026     },
65027
65028     /**
65029      * Select the last row.
65030      * @param {Boolean} keepExisting (optional) True to keep existing selections
65031      */
65032     selectLastRow : function(keepExisting){
65033         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
65034     },
65035
65036     /**
65037      * Selects the row immediately following the last selected row.
65038      * @param {Boolean} keepExisting (optional) True to keep existing selections
65039      */
65040     selectNext : function(keepExisting){
65041         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
65042             this.selectRow(this.last+1, keepExisting);
65043             var view = this.grid.view ? this.grid.view : this.grid;
65044             view.focusRow(this.last);
65045         }
65046     },
65047
65048     /**
65049      * Selects the row that precedes the last selected row.
65050      * @param {Boolean} keepExisting (optional) True to keep existing selections
65051      */
65052     selectPrevious : function(keepExisting){
65053         if(this.last){
65054             this.selectRow(this.last-1, keepExisting);
65055             var view = this.grid.view ? this.grid.view : this.grid;
65056             view.focusRow(this.last);
65057         }
65058     },
65059
65060     /**
65061      * Returns the selected records
65062      * @return {Array} Array of selected records
65063      */
65064     getSelections : function(){
65065         return [].concat(this.selections.items);
65066     },
65067
65068     /**
65069      * Returns the first selected record.
65070      * @return {Record}
65071      */
65072     getSelected : function(){
65073         return this.selections.itemAt(0);
65074     },
65075
65076
65077     /**
65078      * Clears all selections.
65079      */
65080     clearSelections : function(fast){
65081         if(this.locked) {
65082             return;
65083         }
65084         if(fast !== true){
65085             var ds = this.grid.ds;
65086             var s = this.selections;
65087             s.each(function(r){
65088                 this.deselectRow(ds.indexOfId(r.id));
65089             }, this);
65090             s.clear();
65091         }else{
65092             this.selections.clear();
65093         }
65094         this.last = false;
65095     },
65096
65097
65098     /**
65099      * Selects all rows.
65100      */
65101     selectAll : function(){
65102         if(this.locked) {
65103             return;
65104         }
65105         this.selections.clear();
65106         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
65107             this.selectRow(i, true);
65108         }
65109     },
65110
65111     /**
65112      * Returns True if there is a selection.
65113      * @return {Boolean}
65114      */
65115     hasSelection : function(){
65116         return this.selections.length > 0;
65117     },
65118
65119     /**
65120      * Returns True if the specified row is selected.
65121      * @param {Number/Record} record The record or index of the record to check
65122      * @return {Boolean}
65123      */
65124     isSelected : function(index){
65125         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
65126         return (r && this.selections.key(r.id) ? true : false);
65127     },
65128
65129     /**
65130      * Returns True if the specified record id is selected.
65131      * @param {String} id The id of record to check
65132      * @return {Boolean}
65133      */
65134     isIdSelected : function(id){
65135         return (this.selections.key(id) ? true : false);
65136     },
65137
65138     // private
65139     handleMouseDown : function(e, t)
65140     {
65141         var view = this.grid.view ? this.grid.view : this.grid;
65142         var rowIndex;
65143         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
65144             return;
65145         };
65146         if(e.shiftKey && this.last !== false){
65147             var last = this.last;
65148             this.selectRange(last, rowIndex, e.ctrlKey);
65149             this.last = last; // reset the last
65150             view.focusRow(rowIndex);
65151         }else{
65152             var isSelected = this.isSelected(rowIndex);
65153             if(e.button !== 0 && isSelected){
65154                 view.focusRow(rowIndex);
65155             }else if(e.ctrlKey && isSelected){
65156                 this.deselectRow(rowIndex);
65157             }else if(!isSelected){
65158                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
65159                 view.focusRow(rowIndex);
65160             }
65161         }
65162         this.fireEvent("afterselectionchange", this);
65163     },
65164     // private
65165     handleDragableRowClick :  function(grid, rowIndex, e) 
65166     {
65167         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
65168             this.selectRow(rowIndex, false);
65169             var view = this.grid.view ? this.grid.view : this.grid;
65170             view.focusRow(rowIndex);
65171              this.fireEvent("afterselectionchange", this);
65172         }
65173     },
65174     
65175     /**
65176      * Selects multiple rows.
65177      * @param {Array} rows Array of the indexes of the row to select
65178      * @param {Boolean} keepExisting (optional) True to keep existing selections
65179      */
65180     selectRows : function(rows, keepExisting){
65181         if(!keepExisting){
65182             this.clearSelections();
65183         }
65184         for(var i = 0, len = rows.length; i < len; i++){
65185             this.selectRow(rows[i], true);
65186         }
65187     },
65188
65189     /**
65190      * Selects a range of rows. All rows in between startRow and endRow are also selected.
65191      * @param {Number} startRow The index of the first row in the range
65192      * @param {Number} endRow The index of the last row in the range
65193      * @param {Boolean} keepExisting (optional) True to retain existing selections
65194      */
65195     selectRange : function(startRow, endRow, keepExisting){
65196         if(this.locked) {
65197             return;
65198         }
65199         if(!keepExisting){
65200             this.clearSelections();
65201         }
65202         if(startRow <= endRow){
65203             for(var i = startRow; i <= endRow; i++){
65204                 this.selectRow(i, true);
65205             }
65206         }else{
65207             for(var i = startRow; i >= endRow; i--){
65208                 this.selectRow(i, true);
65209             }
65210         }
65211     },
65212
65213     /**
65214      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
65215      * @param {Number} startRow The index of the first row in the range
65216      * @param {Number} endRow The index of the last row in the range
65217      */
65218     deselectRange : function(startRow, endRow, preventViewNotify){
65219         if(this.locked) {
65220             return;
65221         }
65222         for(var i = startRow; i <= endRow; i++){
65223             this.deselectRow(i, preventViewNotify);
65224         }
65225     },
65226
65227     /**
65228      * Selects a row.
65229      * @param {Number} row The index of the row to select
65230      * @param {Boolean} keepExisting (optional) True to keep existing selections
65231      */
65232     selectRow : function(index, keepExisting, preventViewNotify){
65233         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
65234             return;
65235         }
65236         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
65237             if(!keepExisting || this.singleSelect){
65238                 this.clearSelections();
65239             }
65240             var r = this.grid.ds.getAt(index);
65241             this.selections.add(r);
65242             this.last = this.lastActive = index;
65243             if(!preventViewNotify){
65244                 var view = this.grid.view ? this.grid.view : this.grid;
65245                 view.onRowSelect(index);
65246             }
65247             this.fireEvent("rowselect", this, index, r);
65248             this.fireEvent("selectionchange", this);
65249         }
65250     },
65251
65252     /**
65253      * Deselects a row.
65254      * @param {Number} row The index of the row to deselect
65255      */
65256     deselectRow : function(index, preventViewNotify){
65257         if(this.locked) {
65258             return;
65259         }
65260         if(this.last == index){
65261             this.last = false;
65262         }
65263         if(this.lastActive == index){
65264             this.lastActive = false;
65265         }
65266         var r = this.grid.ds.getAt(index);
65267         this.selections.remove(r);
65268         if(!preventViewNotify){
65269             var view = this.grid.view ? this.grid.view : this.grid;
65270             view.onRowDeselect(index);
65271         }
65272         this.fireEvent("rowdeselect", this, index);
65273         this.fireEvent("selectionchange", this);
65274     },
65275
65276     // private
65277     restoreLast : function(){
65278         if(this._last){
65279             this.last = this._last;
65280         }
65281     },
65282
65283     // private
65284     acceptsNav : function(row, col, cm){
65285         return !cm.isHidden(col) && cm.isCellEditable(col, row);
65286     },
65287
65288     // private
65289     onEditorKey : function(field, e){
65290         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
65291         if(k == e.TAB){
65292             e.stopEvent();
65293             ed.completeEdit();
65294             if(e.shiftKey){
65295                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65296             }else{
65297                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65298             }
65299         }else if(k == e.ENTER && !e.ctrlKey){
65300             e.stopEvent();
65301             ed.completeEdit();
65302             if(e.shiftKey){
65303                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
65304             }else{
65305                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
65306             }
65307         }else if(k == e.ESC){
65308             ed.cancelEdit();
65309         }
65310         if(newCell){
65311             g.startEditing(newCell[0], newCell[1]);
65312         }
65313     }
65314 });/*
65315  * Based on:
65316  * Ext JS Library 1.1.1
65317  * Copyright(c) 2006-2007, Ext JS, LLC.
65318  *
65319  * Originally Released Under LGPL - original licence link has changed is not relivant.
65320  *
65321  * Fork - LGPL
65322  * <script type="text/javascript">
65323  */
65324 /**
65325  * @class Roo.grid.CellSelectionModel
65326  * @extends Roo.grid.AbstractSelectionModel
65327  * This class provides the basic implementation for cell selection in a grid.
65328  * @constructor
65329  * @param {Object} config The object containing the configuration of this model.
65330  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
65331  */
65332 Roo.grid.CellSelectionModel = function(config){
65333     Roo.apply(this, config);
65334
65335     this.selection = null;
65336
65337     this.addEvents({
65338         /**
65339              * @event beforerowselect
65340              * Fires before a cell is selected.
65341              * @param {SelectionModel} this
65342              * @param {Number} rowIndex The selected row index
65343              * @param {Number} colIndex The selected cell index
65344              */
65345             "beforecellselect" : true,
65346         /**
65347              * @event cellselect
65348              * Fires when a cell is selected.
65349              * @param {SelectionModel} this
65350              * @param {Number} rowIndex The selected row index
65351              * @param {Number} colIndex The selected cell index
65352              */
65353             "cellselect" : true,
65354         /**
65355              * @event selectionchange
65356              * Fires when the active selection changes.
65357              * @param {SelectionModel} this
65358              * @param {Object} selection null for no selection or an object (o) with two properties
65359                 <ul>
65360                 <li>o.record: the record object for the row the selection is in</li>
65361                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
65362                 </ul>
65363              */
65364             "selectionchange" : true,
65365         /**
65366              * @event tabend
65367              * Fires when the tab (or enter) was pressed on the last editable cell
65368              * You can use this to trigger add new row.
65369              * @param {SelectionModel} this
65370              */
65371             "tabend" : true,
65372          /**
65373              * @event beforeeditnext
65374              * Fires before the next editable sell is made active
65375              * You can use this to skip to another cell or fire the tabend
65376              *    if you set cell to false
65377              * @param {Object} eventdata object : { cell : [ row, col ] } 
65378              */
65379             "beforeeditnext" : true
65380     });
65381     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
65382 };
65383
65384 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
65385     
65386     enter_is_tab: false,
65387
65388     /** @ignore */
65389     initEvents : function(){
65390         this.grid.on("mousedown", this.handleMouseDown, this);
65391         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
65392         var view = this.grid.view;
65393         view.on("refresh", this.onViewChange, this);
65394         view.on("rowupdated", this.onRowUpdated, this);
65395         view.on("beforerowremoved", this.clearSelections, this);
65396         view.on("beforerowsinserted", this.clearSelections, this);
65397         if(this.grid.isEditor){
65398             this.grid.on("beforeedit", this.beforeEdit,  this);
65399         }
65400     },
65401
65402         //private
65403     beforeEdit : function(e){
65404         this.select(e.row, e.column, false, true, e.record);
65405     },
65406
65407         //private
65408     onRowUpdated : function(v, index, r){
65409         if(this.selection && this.selection.record == r){
65410             v.onCellSelect(index, this.selection.cell[1]);
65411         }
65412     },
65413
65414         //private
65415     onViewChange : function(){
65416         this.clearSelections(true);
65417     },
65418
65419         /**
65420          * Returns the currently selected cell,.
65421          * @return {Array} The selected cell (row, column) or null if none selected.
65422          */
65423     getSelectedCell : function(){
65424         return this.selection ? this.selection.cell : null;
65425     },
65426
65427     /**
65428      * Clears all selections.
65429      * @param {Boolean} true to prevent the gridview from being notified about the change.
65430      */
65431     clearSelections : function(preventNotify){
65432         var s = this.selection;
65433         if(s){
65434             if(preventNotify !== true){
65435                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
65436             }
65437             this.selection = null;
65438             this.fireEvent("selectionchange", this, null);
65439         }
65440     },
65441
65442     /**
65443      * Returns true if there is a selection.
65444      * @return {Boolean}
65445      */
65446     hasSelection : function(){
65447         return this.selection ? true : false;
65448     },
65449
65450     /** @ignore */
65451     handleMouseDown : function(e, t){
65452         var v = this.grid.getView();
65453         if(this.isLocked()){
65454             return;
65455         };
65456         var row = v.findRowIndex(t);
65457         var cell = v.findCellIndex(t);
65458         if(row !== false && cell !== false){
65459             this.select(row, cell);
65460         }
65461     },
65462
65463     /**
65464      * Selects a cell.
65465      * @param {Number} rowIndex
65466      * @param {Number} collIndex
65467      */
65468     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
65469         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
65470             this.clearSelections();
65471             r = r || this.grid.dataSource.getAt(rowIndex);
65472             this.selection = {
65473                 record : r,
65474                 cell : [rowIndex, colIndex]
65475             };
65476             if(!preventViewNotify){
65477                 var v = this.grid.getView();
65478                 v.onCellSelect(rowIndex, colIndex);
65479                 if(preventFocus !== true){
65480                     v.focusCell(rowIndex, colIndex);
65481                 }
65482             }
65483             this.fireEvent("cellselect", this, rowIndex, colIndex);
65484             this.fireEvent("selectionchange", this, this.selection);
65485         }
65486     },
65487
65488         //private
65489     isSelectable : function(rowIndex, colIndex, cm){
65490         return !cm.isHidden(colIndex);
65491     },
65492
65493     /** @ignore */
65494     handleKeyDown : function(e){
65495         //Roo.log('Cell Sel Model handleKeyDown');
65496         if(!e.isNavKeyPress()){
65497             return;
65498         }
65499         var g = this.grid, s = this.selection;
65500         if(!s){
65501             e.stopEvent();
65502             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
65503             if(cell){
65504                 this.select(cell[0], cell[1]);
65505             }
65506             return;
65507         }
65508         var sm = this;
65509         var walk = function(row, col, step){
65510             return g.walkCells(row, col, step, sm.isSelectable,  sm);
65511         };
65512         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
65513         var newCell;
65514
65515       
65516
65517         switch(k){
65518             case e.TAB:
65519                 // handled by onEditorKey
65520                 if (g.isEditor && g.editing) {
65521                     return;
65522                 }
65523                 if(e.shiftKey) {
65524                     newCell = walk(r, c-1, -1);
65525                 } else {
65526                     newCell = walk(r, c+1, 1);
65527                 }
65528                 break;
65529             
65530             case e.DOWN:
65531                newCell = walk(r+1, c, 1);
65532                 break;
65533             
65534             case e.UP:
65535                 newCell = walk(r-1, c, -1);
65536                 break;
65537             
65538             case e.RIGHT:
65539                 newCell = walk(r, c+1, 1);
65540                 break;
65541             
65542             case e.LEFT:
65543                 newCell = walk(r, c-1, -1);
65544                 break;
65545             
65546             case e.ENTER:
65547                 
65548                 if(g.isEditor && !g.editing){
65549                    g.startEditing(r, c);
65550                    e.stopEvent();
65551                    return;
65552                 }
65553                 
65554                 
65555              break;
65556         };
65557         if(newCell){
65558             this.select(newCell[0], newCell[1]);
65559             e.stopEvent();
65560             
65561         }
65562     },
65563
65564     acceptsNav : function(row, col, cm){
65565         return !cm.isHidden(col) && cm.isCellEditable(col, row);
65566     },
65567     /**
65568      * Selects a cell.
65569      * @param {Number} field (not used) - as it's normally used as a listener
65570      * @param {Number} e - event - fake it by using
65571      *
65572      * var e = Roo.EventObjectImpl.prototype;
65573      * e.keyCode = e.TAB
65574      *
65575      * 
65576      */
65577     onEditorKey : function(field, e){
65578         
65579         var k = e.getKey(),
65580             newCell,
65581             g = this.grid,
65582             ed = g.activeEditor,
65583             forward = false;
65584         ///Roo.log('onEditorKey' + k);
65585         
65586         
65587         if (this.enter_is_tab && k == e.ENTER) {
65588             k = e.TAB;
65589         }
65590         
65591         if(k == e.TAB){
65592             if(e.shiftKey){
65593                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65594             }else{
65595                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65596                 forward = true;
65597             }
65598             
65599             e.stopEvent();
65600             
65601         } else if(k == e.ENTER &&  !e.ctrlKey){
65602             ed.completeEdit();
65603             e.stopEvent();
65604             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65605         
65606                 } else if(k == e.ESC){
65607             ed.cancelEdit();
65608         }
65609                 
65610         if (newCell) {
65611             var ecall = { cell : newCell, forward : forward };
65612             this.fireEvent('beforeeditnext', ecall );
65613             newCell = ecall.cell;
65614                         forward = ecall.forward;
65615         }
65616                 
65617         if(newCell){
65618             //Roo.log('next cell after edit');
65619             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
65620         } else if (forward) {
65621             // tabbed past last
65622             this.fireEvent.defer(100, this, ['tabend',this]);
65623         }
65624     }
65625 });/*
65626  * Based on:
65627  * Ext JS Library 1.1.1
65628  * Copyright(c) 2006-2007, Ext JS, LLC.
65629  *
65630  * Originally Released Under LGPL - original licence link has changed is not relivant.
65631  *
65632  * Fork - LGPL
65633  * <script type="text/javascript">
65634  */
65635  
65636 /**
65637  * @class Roo.grid.EditorGrid
65638  * @extends Roo.grid.Grid
65639  * Class for creating and editable grid.
65640  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
65641  * The container MUST have some type of size defined for the grid to fill. The container will be 
65642  * automatically set to position relative if it isn't already.
65643  * @param {Object} dataSource The data model to bind to
65644  * @param {Object} colModel The column model with info about this grid's columns
65645  */
65646 Roo.grid.EditorGrid = function(container, config){
65647     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
65648     this.getGridEl().addClass("xedit-grid");
65649
65650     if(!this.selModel){
65651         this.selModel = new Roo.grid.CellSelectionModel();
65652     }
65653
65654     this.activeEditor = null;
65655
65656         this.addEvents({
65657             /**
65658              * @event beforeedit
65659              * Fires before cell editing is triggered. The edit event object has the following properties <br />
65660              * <ul style="padding:5px;padding-left:16px;">
65661              * <li>grid - This grid</li>
65662              * <li>record - The record being edited</li>
65663              * <li>field - The field name being edited</li>
65664              * <li>value - The value for the field being edited.</li>
65665              * <li>row - The grid row index</li>
65666              * <li>column - The grid column index</li>
65667              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65668              * </ul>
65669              * @param {Object} e An edit event (see above for description)
65670              */
65671             "beforeedit" : true,
65672             /**
65673              * @event afteredit
65674              * Fires after a cell is edited. <br />
65675              * <ul style="padding:5px;padding-left:16px;">
65676              * <li>grid - This grid</li>
65677              * <li>record - The record being edited</li>
65678              * <li>field - The field name being edited</li>
65679              * <li>value - The value being set</li>
65680              * <li>originalValue - The original value for the field, before the edit.</li>
65681              * <li>row - The grid row index</li>
65682              * <li>column - The grid column index</li>
65683              * </ul>
65684              * @param {Object} e An edit event (see above for description)
65685              */
65686             "afteredit" : true,
65687             /**
65688              * @event validateedit
65689              * Fires after a cell is edited, but before the value is set in the record. 
65690          * You can use this to modify the value being set in the field, Return false
65691              * to cancel the change. The edit event object has the following properties <br />
65692              * <ul style="padding:5px;padding-left:16px;">
65693          * <li>editor - This editor</li>
65694              * <li>grid - This grid</li>
65695              * <li>record - The record being edited</li>
65696              * <li>field - The field name being edited</li>
65697              * <li>value - The value being set</li>
65698              * <li>originalValue - The original value for the field, before the edit.</li>
65699              * <li>row - The grid row index</li>
65700              * <li>column - The grid column index</li>
65701              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65702              * </ul>
65703              * @param {Object} e An edit event (see above for description)
65704              */
65705             "validateedit" : true
65706         });
65707     this.on("bodyscroll", this.stopEditing,  this);
65708     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
65709 };
65710
65711 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
65712     /**
65713      * @cfg {Number} clicksToEdit
65714      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
65715      */
65716     clicksToEdit: 2,
65717
65718     // private
65719     isEditor : true,
65720     // private
65721     trackMouseOver: false, // causes very odd FF errors
65722
65723     onCellDblClick : function(g, row, col){
65724         this.startEditing(row, col);
65725     },
65726
65727     onEditComplete : function(ed, value, startValue){
65728         this.editing = false;
65729         this.activeEditor = null;
65730         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
65731         var r = ed.record;
65732         var field = this.colModel.getDataIndex(ed.col);
65733         var e = {
65734             grid: this,
65735             record: r,
65736             field: field,
65737             originalValue: startValue,
65738             value: value,
65739             row: ed.row,
65740             column: ed.col,
65741             cancel:false,
65742             editor: ed
65743         };
65744         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
65745         cell.show();
65746           
65747         if(String(value) !== String(startValue)){
65748             
65749             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
65750                 r.set(field, e.value);
65751                 // if we are dealing with a combo box..
65752                 // then we also set the 'name' colum to be the displayField
65753                 if (ed.field.displayField && ed.field.name) {
65754                     r.set(ed.field.name, ed.field.el.dom.value);
65755                 }
65756                 
65757                 delete e.cancel; //?? why!!!
65758                 this.fireEvent("afteredit", e);
65759             }
65760         } else {
65761             this.fireEvent("afteredit", e); // always fire it!
65762         }
65763         this.view.focusCell(ed.row, ed.col);
65764     },
65765
65766     /**
65767      * Starts editing the specified for the specified row/column
65768      * @param {Number} rowIndex
65769      * @param {Number} colIndex
65770      */
65771     startEditing : function(row, col){
65772         this.stopEditing();
65773         if(this.colModel.isCellEditable(col, row)){
65774             this.view.ensureVisible(row, col, true);
65775           
65776             var r = this.dataSource.getAt(row);
65777             var field = this.colModel.getDataIndex(col);
65778             var cell = Roo.get(this.view.getCell(row,col));
65779             var e = {
65780                 grid: this,
65781                 record: r,
65782                 field: field,
65783                 value: r.data[field],
65784                 row: row,
65785                 column: col,
65786                 cancel:false 
65787             };
65788             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
65789                 this.editing = true;
65790                 var ed = this.colModel.getCellEditor(col, row);
65791                 
65792                 if (!ed) {
65793                     return;
65794                 }
65795                 if(!ed.rendered){
65796                     ed.render(ed.parentEl || document.body);
65797                 }
65798                 ed.field.reset();
65799                
65800                 cell.hide();
65801                 
65802                 (function(){ // complex but required for focus issues in safari, ie and opera
65803                     ed.row = row;
65804                     ed.col = col;
65805                     ed.record = r;
65806                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
65807                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
65808                     this.activeEditor = ed;
65809                     var v = r.data[field];
65810                     ed.startEdit(this.view.getCell(row, col), v);
65811                     // combo's with 'displayField and name set
65812                     if (ed.field.displayField && ed.field.name) {
65813                         ed.field.el.dom.value = r.data[ed.field.name];
65814                     }
65815                     
65816                     
65817                 }).defer(50, this);
65818             }
65819         }
65820     },
65821         
65822     /**
65823      * Stops any active editing
65824      */
65825     stopEditing : function(){
65826         if(this.activeEditor){
65827             this.activeEditor.completeEdit();
65828         }
65829         this.activeEditor = null;
65830     },
65831         
65832          /**
65833      * Called to get grid's drag proxy text, by default returns this.ddText.
65834      * @return {String}
65835      */
65836     getDragDropText : function(){
65837         var count = this.selModel.getSelectedCell() ? 1 : 0;
65838         return String.format(this.ddText, count, count == 1 ? '' : 's');
65839     }
65840         
65841 });/*
65842  * Based on:
65843  * Ext JS Library 1.1.1
65844  * Copyright(c) 2006-2007, Ext JS, LLC.
65845  *
65846  * Originally Released Under LGPL - original licence link has changed is not relivant.
65847  *
65848  * Fork - LGPL
65849  * <script type="text/javascript">
65850  */
65851
65852 // private - not really -- you end up using it !
65853 // This is a support class used internally by the Grid components
65854
65855 /**
65856  * @class Roo.grid.GridEditor
65857  * @extends Roo.Editor
65858  * Class for creating and editable grid elements.
65859  * @param {Object} config any settings (must include field)
65860  */
65861 Roo.grid.GridEditor = function(field, config){
65862     if (!config && field.field) {
65863         config = field;
65864         field = Roo.factory(config.field, Roo.form);
65865     }
65866     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
65867     field.monitorTab = false;
65868 };
65869
65870 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
65871     
65872     /**
65873      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
65874      */
65875     
65876     alignment: "tl-tl",
65877     autoSize: "width",
65878     hideEl : false,
65879     cls: "x-small-editor x-grid-editor",
65880     shim:false,
65881     shadow:"frame"
65882 });/*
65883  * Based on:
65884  * Ext JS Library 1.1.1
65885  * Copyright(c) 2006-2007, Ext JS, LLC.
65886  *
65887  * Originally Released Under LGPL - original licence link has changed is not relivant.
65888  *
65889  * Fork - LGPL
65890  * <script type="text/javascript">
65891  */
65892   
65893
65894   
65895 Roo.grid.PropertyRecord = Roo.data.Record.create([
65896     {name:'name',type:'string'},  'value'
65897 ]);
65898
65899
65900 Roo.grid.PropertyStore = function(grid, source){
65901     this.grid = grid;
65902     this.store = new Roo.data.Store({
65903         recordType : Roo.grid.PropertyRecord
65904     });
65905     this.store.on('update', this.onUpdate,  this);
65906     if(source){
65907         this.setSource(source);
65908     }
65909     Roo.grid.PropertyStore.superclass.constructor.call(this);
65910 };
65911
65912
65913
65914 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
65915     setSource : function(o){
65916         this.source = o;
65917         this.store.removeAll();
65918         var data = [];
65919         for(var k in o){
65920             if(this.isEditableValue(o[k])){
65921                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
65922             }
65923         }
65924         this.store.loadRecords({records: data}, {}, true);
65925     },
65926
65927     onUpdate : function(ds, record, type){
65928         if(type == Roo.data.Record.EDIT){
65929             var v = record.data['value'];
65930             var oldValue = record.modified['value'];
65931             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
65932                 this.source[record.id] = v;
65933                 record.commit();
65934                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
65935             }else{
65936                 record.reject();
65937             }
65938         }
65939     },
65940
65941     getProperty : function(row){
65942        return this.store.getAt(row);
65943     },
65944
65945     isEditableValue: function(val){
65946         if(val && val instanceof Date){
65947             return true;
65948         }else if(typeof val == 'object' || typeof val == 'function'){
65949             return false;
65950         }
65951         return true;
65952     },
65953
65954     setValue : function(prop, value){
65955         this.source[prop] = value;
65956         this.store.getById(prop).set('value', value);
65957     },
65958
65959     getSource : function(){
65960         return this.source;
65961     }
65962 });
65963
65964 Roo.grid.PropertyColumnModel = function(grid, store){
65965     this.grid = grid;
65966     var g = Roo.grid;
65967     g.PropertyColumnModel.superclass.constructor.call(this, [
65968         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
65969         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
65970     ]);
65971     this.store = store;
65972     this.bselect = Roo.DomHelper.append(document.body, {
65973         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
65974             {tag: 'option', value: 'true', html: 'true'},
65975             {tag: 'option', value: 'false', html: 'false'}
65976         ]
65977     });
65978     Roo.id(this.bselect);
65979     var f = Roo.form;
65980     this.editors = {
65981         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
65982         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
65983         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
65984         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
65985         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
65986     };
65987     this.renderCellDelegate = this.renderCell.createDelegate(this);
65988     this.renderPropDelegate = this.renderProp.createDelegate(this);
65989 };
65990
65991 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
65992     
65993     
65994     nameText : 'Name',
65995     valueText : 'Value',
65996     
65997     dateFormat : 'm/j/Y',
65998     
65999     
66000     renderDate : function(dateVal){
66001         return dateVal.dateFormat(this.dateFormat);
66002     },
66003
66004     renderBool : function(bVal){
66005         return bVal ? 'true' : 'false';
66006     },
66007
66008     isCellEditable : function(colIndex, rowIndex){
66009         return colIndex == 1;
66010     },
66011
66012     getRenderer : function(col){
66013         return col == 1 ?
66014             this.renderCellDelegate : this.renderPropDelegate;
66015     },
66016
66017     renderProp : function(v){
66018         return this.getPropertyName(v);
66019     },
66020
66021     renderCell : function(val){
66022         var rv = val;
66023         if(val instanceof Date){
66024             rv = this.renderDate(val);
66025         }else if(typeof val == 'boolean'){
66026             rv = this.renderBool(val);
66027         }
66028         return Roo.util.Format.htmlEncode(rv);
66029     },
66030
66031     getPropertyName : function(name){
66032         var pn = this.grid.propertyNames;
66033         return pn && pn[name] ? pn[name] : name;
66034     },
66035
66036     getCellEditor : function(colIndex, rowIndex){
66037         var p = this.store.getProperty(rowIndex);
66038         var n = p.data['name'], val = p.data['value'];
66039         
66040         if(typeof(this.grid.customEditors[n]) == 'string'){
66041             return this.editors[this.grid.customEditors[n]];
66042         }
66043         if(typeof(this.grid.customEditors[n]) != 'undefined'){
66044             return this.grid.customEditors[n];
66045         }
66046         if(val instanceof Date){
66047             return this.editors['date'];
66048         }else if(typeof val == 'number'){
66049             return this.editors['number'];
66050         }else if(typeof val == 'boolean'){
66051             return this.editors['boolean'];
66052         }else{
66053             return this.editors['string'];
66054         }
66055     }
66056 });
66057
66058 /**
66059  * @class Roo.grid.PropertyGrid
66060  * @extends Roo.grid.EditorGrid
66061  * This class represents the  interface of a component based property grid control.
66062  * <br><br>Usage:<pre><code>
66063  var grid = new Roo.grid.PropertyGrid("my-container-id", {
66064       
66065  });
66066  // set any options
66067  grid.render();
66068  * </code></pre>
66069   
66070  * @constructor
66071  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66072  * The container MUST have some type of size defined for the grid to fill. The container will be
66073  * automatically set to position relative if it isn't already.
66074  * @param {Object} config A config object that sets properties on this grid.
66075  */
66076 Roo.grid.PropertyGrid = function(container, config){
66077     config = config || {};
66078     var store = new Roo.grid.PropertyStore(this);
66079     this.store = store;
66080     var cm = new Roo.grid.PropertyColumnModel(this, store);
66081     store.store.sort('name', 'ASC');
66082     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
66083         ds: store.store,
66084         cm: cm,
66085         enableColLock:false,
66086         enableColumnMove:false,
66087         stripeRows:false,
66088         trackMouseOver: false,
66089         clicksToEdit:1
66090     }, config));
66091     this.getGridEl().addClass('x-props-grid');
66092     this.lastEditRow = null;
66093     this.on('columnresize', this.onColumnResize, this);
66094     this.addEvents({
66095          /**
66096              * @event beforepropertychange
66097              * Fires before a property changes (return false to stop?)
66098              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66099              * @param {String} id Record Id
66100              * @param {String} newval New Value
66101          * @param {String} oldval Old Value
66102              */
66103         "beforepropertychange": true,
66104         /**
66105              * @event propertychange
66106              * Fires after a property changes
66107              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66108              * @param {String} id Record Id
66109              * @param {String} newval New Value
66110          * @param {String} oldval Old Value
66111              */
66112         "propertychange": true
66113     });
66114     this.customEditors = this.customEditors || {};
66115 };
66116 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
66117     
66118      /**
66119      * @cfg {Object} customEditors map of colnames=> custom editors.
66120      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
66121      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
66122      * false disables editing of the field.
66123          */
66124     
66125       /**
66126      * @cfg {Object} propertyNames map of property Names to their displayed value
66127          */
66128     
66129     render : function(){
66130         Roo.grid.PropertyGrid.superclass.render.call(this);
66131         this.autoSize.defer(100, this);
66132     },
66133
66134     autoSize : function(){
66135         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
66136         if(this.view){
66137             this.view.fitColumns();
66138         }
66139     },
66140
66141     onColumnResize : function(){
66142         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
66143         this.autoSize();
66144     },
66145     /**
66146      * Sets the data for the Grid
66147      * accepts a Key => Value object of all the elements avaiable.
66148      * @param {Object} data  to appear in grid.
66149      */
66150     setSource : function(source){
66151         this.store.setSource(source);
66152         //this.autoSize();
66153     },
66154     /**
66155      * Gets all the data from the grid.
66156      * @return {Object} data  data stored in grid
66157      */
66158     getSource : function(){
66159         return this.store.getSource();
66160     }
66161 });/*
66162   
66163  * Licence LGPL
66164  
66165  */
66166  
66167 /**
66168  * @class Roo.grid.Calendar
66169  * @extends Roo.grid.Grid
66170  * This class extends the Grid to provide a calendar widget
66171  * <br><br>Usage:<pre><code>
66172  var grid = new Roo.grid.Calendar("my-container-id", {
66173      ds: myDataStore,
66174      cm: myColModel,
66175      selModel: mySelectionModel,
66176      autoSizeColumns: true,
66177      monitorWindowResize: false,
66178      trackMouseOver: true
66179      eventstore : real data store..
66180  });
66181  // set any options
66182  grid.render();
66183   
66184   * @constructor
66185  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66186  * The container MUST have some type of size defined for the grid to fill. The container will be
66187  * automatically set to position relative if it isn't already.
66188  * @param {Object} config A config object that sets properties on this grid.
66189  */
66190 Roo.grid.Calendar = function(container, config){
66191         // initialize the container
66192         this.container = Roo.get(container);
66193         this.container.update("");
66194         this.container.setStyle("overflow", "hidden");
66195     this.container.addClass('x-grid-container');
66196
66197     this.id = this.container.id;
66198
66199     Roo.apply(this, config);
66200     // check and correct shorthanded configs
66201     
66202     var rows = [];
66203     var d =1;
66204     for (var r = 0;r < 6;r++) {
66205         
66206         rows[r]=[];
66207         for (var c =0;c < 7;c++) {
66208             rows[r][c]= '';
66209         }
66210     }
66211     if (this.eventStore) {
66212         this.eventStore= Roo.factory(this.eventStore, Roo.data);
66213         this.eventStore.on('load',this.onLoad, this);
66214         this.eventStore.on('beforeload',this.clearEvents, this);
66215          
66216     }
66217     
66218     this.dataSource = new Roo.data.Store({
66219             proxy: new Roo.data.MemoryProxy(rows),
66220             reader: new Roo.data.ArrayReader({}, [
66221                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
66222     });
66223
66224     this.dataSource.load();
66225     this.ds = this.dataSource;
66226     this.ds.xmodule = this.xmodule || false;
66227     
66228     
66229     var cellRender = function(v,x,r)
66230     {
66231         return String.format(
66232             '<div class="fc-day  fc-widget-content"><div>' +
66233                 '<div class="fc-event-container"></div>' +
66234                 '<div class="fc-day-number">{0}</div>'+
66235                 
66236                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
66237             '</div></div>', v);
66238     
66239     }
66240     
66241     
66242     this.colModel = new Roo.grid.ColumnModel( [
66243         {
66244             xtype: 'ColumnModel',
66245             xns: Roo.grid,
66246             dataIndex : 'weekday0',
66247             header : 'Sunday',
66248             renderer : cellRender
66249         },
66250         {
66251             xtype: 'ColumnModel',
66252             xns: Roo.grid,
66253             dataIndex : 'weekday1',
66254             header : 'Monday',
66255             renderer : cellRender
66256         },
66257         {
66258             xtype: 'ColumnModel',
66259             xns: Roo.grid,
66260             dataIndex : 'weekday2',
66261             header : 'Tuesday',
66262             renderer : cellRender
66263         },
66264         {
66265             xtype: 'ColumnModel',
66266             xns: Roo.grid,
66267             dataIndex : 'weekday3',
66268             header : 'Wednesday',
66269             renderer : cellRender
66270         },
66271         {
66272             xtype: 'ColumnModel',
66273             xns: Roo.grid,
66274             dataIndex : 'weekday4',
66275             header : 'Thursday',
66276             renderer : cellRender
66277         },
66278         {
66279             xtype: 'ColumnModel',
66280             xns: Roo.grid,
66281             dataIndex : 'weekday5',
66282             header : 'Friday',
66283             renderer : cellRender
66284         },
66285         {
66286             xtype: 'ColumnModel',
66287             xns: Roo.grid,
66288             dataIndex : 'weekday6',
66289             header : 'Saturday',
66290             renderer : cellRender
66291         }
66292     ]);
66293     this.cm = this.colModel;
66294     this.cm.xmodule = this.xmodule || false;
66295  
66296         
66297           
66298     //this.selModel = new Roo.grid.CellSelectionModel();
66299     //this.sm = this.selModel;
66300     //this.selModel.init(this);
66301     
66302     
66303     if(this.width){
66304         this.container.setWidth(this.width);
66305     }
66306
66307     if(this.height){
66308         this.container.setHeight(this.height);
66309     }
66310     /** @private */
66311         this.addEvents({
66312         // raw events
66313         /**
66314          * @event click
66315          * The raw click event for the entire grid.
66316          * @param {Roo.EventObject} e
66317          */
66318         "click" : true,
66319         /**
66320          * @event dblclick
66321          * The raw dblclick event for the entire grid.
66322          * @param {Roo.EventObject} e
66323          */
66324         "dblclick" : true,
66325         /**
66326          * @event contextmenu
66327          * The raw contextmenu event for the entire grid.
66328          * @param {Roo.EventObject} e
66329          */
66330         "contextmenu" : true,
66331         /**
66332          * @event mousedown
66333          * The raw mousedown event for the entire grid.
66334          * @param {Roo.EventObject} e
66335          */
66336         "mousedown" : true,
66337         /**
66338          * @event mouseup
66339          * The raw mouseup event for the entire grid.
66340          * @param {Roo.EventObject} e
66341          */
66342         "mouseup" : true,
66343         /**
66344          * @event mouseover
66345          * The raw mouseover event for the entire grid.
66346          * @param {Roo.EventObject} e
66347          */
66348         "mouseover" : true,
66349         /**
66350          * @event mouseout
66351          * The raw mouseout event for the entire grid.
66352          * @param {Roo.EventObject} e
66353          */
66354         "mouseout" : true,
66355         /**
66356          * @event keypress
66357          * The raw keypress event for the entire grid.
66358          * @param {Roo.EventObject} e
66359          */
66360         "keypress" : true,
66361         /**
66362          * @event keydown
66363          * The raw keydown event for the entire grid.
66364          * @param {Roo.EventObject} e
66365          */
66366         "keydown" : true,
66367
66368         // custom events
66369
66370         /**
66371          * @event cellclick
66372          * Fires when a cell is clicked
66373          * @param {Grid} this
66374          * @param {Number} rowIndex
66375          * @param {Number} columnIndex
66376          * @param {Roo.EventObject} e
66377          */
66378         "cellclick" : true,
66379         /**
66380          * @event celldblclick
66381          * Fires when a cell is double clicked
66382          * @param {Grid} this
66383          * @param {Number} rowIndex
66384          * @param {Number} columnIndex
66385          * @param {Roo.EventObject} e
66386          */
66387         "celldblclick" : true,
66388         /**
66389          * @event rowclick
66390          * Fires when a row is clicked
66391          * @param {Grid} this
66392          * @param {Number} rowIndex
66393          * @param {Roo.EventObject} e
66394          */
66395         "rowclick" : true,
66396         /**
66397          * @event rowdblclick
66398          * Fires when a row is double clicked
66399          * @param {Grid} this
66400          * @param {Number} rowIndex
66401          * @param {Roo.EventObject} e
66402          */
66403         "rowdblclick" : true,
66404         /**
66405          * @event headerclick
66406          * Fires when a header is clicked
66407          * @param {Grid} this
66408          * @param {Number} columnIndex
66409          * @param {Roo.EventObject} e
66410          */
66411         "headerclick" : true,
66412         /**
66413          * @event headerdblclick
66414          * Fires when a header cell is double clicked
66415          * @param {Grid} this
66416          * @param {Number} columnIndex
66417          * @param {Roo.EventObject} e
66418          */
66419         "headerdblclick" : true,
66420         /**
66421          * @event rowcontextmenu
66422          * Fires when a row is right clicked
66423          * @param {Grid} this
66424          * @param {Number} rowIndex
66425          * @param {Roo.EventObject} e
66426          */
66427         "rowcontextmenu" : true,
66428         /**
66429          * @event cellcontextmenu
66430          * Fires when a cell is right clicked
66431          * @param {Grid} this
66432          * @param {Number} rowIndex
66433          * @param {Number} cellIndex
66434          * @param {Roo.EventObject} e
66435          */
66436          "cellcontextmenu" : true,
66437         /**
66438          * @event headercontextmenu
66439          * Fires when a header is right clicked
66440          * @param {Grid} this
66441          * @param {Number} columnIndex
66442          * @param {Roo.EventObject} e
66443          */
66444         "headercontextmenu" : true,
66445         /**
66446          * @event bodyscroll
66447          * Fires when the body element is scrolled
66448          * @param {Number} scrollLeft
66449          * @param {Number} scrollTop
66450          */
66451         "bodyscroll" : true,
66452         /**
66453          * @event columnresize
66454          * Fires when the user resizes a column
66455          * @param {Number} columnIndex
66456          * @param {Number} newSize
66457          */
66458         "columnresize" : true,
66459         /**
66460          * @event columnmove
66461          * Fires when the user moves a column
66462          * @param {Number} oldIndex
66463          * @param {Number} newIndex
66464          */
66465         "columnmove" : true,
66466         /**
66467          * @event startdrag
66468          * Fires when row(s) start being dragged
66469          * @param {Grid} this
66470          * @param {Roo.GridDD} dd The drag drop object
66471          * @param {event} e The raw browser event
66472          */
66473         "startdrag" : true,
66474         /**
66475          * @event enddrag
66476          * Fires when a drag operation is complete
66477          * @param {Grid} this
66478          * @param {Roo.GridDD} dd The drag drop object
66479          * @param {event} e The raw browser event
66480          */
66481         "enddrag" : true,
66482         /**
66483          * @event dragdrop
66484          * Fires when dragged row(s) are dropped on a valid DD target
66485          * @param {Grid} this
66486          * @param {Roo.GridDD} dd The drag drop object
66487          * @param {String} targetId The target drag drop object
66488          * @param {event} e The raw browser event
66489          */
66490         "dragdrop" : true,
66491         /**
66492          * @event dragover
66493          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
66494          * @param {Grid} this
66495          * @param {Roo.GridDD} dd The drag drop object
66496          * @param {String} targetId The target drag drop object
66497          * @param {event} e The raw browser event
66498          */
66499         "dragover" : true,
66500         /**
66501          * @event dragenter
66502          *  Fires when the dragged row(s) first cross another DD target while being dragged
66503          * @param {Grid} this
66504          * @param {Roo.GridDD} dd The drag drop object
66505          * @param {String} targetId The target drag drop object
66506          * @param {event} e The raw browser event
66507          */
66508         "dragenter" : true,
66509         /**
66510          * @event dragout
66511          * Fires when the dragged row(s) leave another DD target while being dragged
66512          * @param {Grid} this
66513          * @param {Roo.GridDD} dd The drag drop object
66514          * @param {String} targetId The target drag drop object
66515          * @param {event} e The raw browser event
66516          */
66517         "dragout" : true,
66518         /**
66519          * @event rowclass
66520          * Fires when a row is rendered, so you can change add a style to it.
66521          * @param {GridView} gridview   The grid view
66522          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
66523          */
66524         'rowclass' : true,
66525
66526         /**
66527          * @event render
66528          * Fires when the grid is rendered
66529          * @param {Grid} grid
66530          */
66531         'render' : true,
66532             /**
66533              * @event select
66534              * Fires when a date is selected
66535              * @param {DatePicker} this
66536              * @param {Date} date The selected date
66537              */
66538         'select': true,
66539         /**
66540              * @event monthchange
66541              * Fires when the displayed month changes 
66542              * @param {DatePicker} this
66543              * @param {Date} date The selected month
66544              */
66545         'monthchange': true,
66546         /**
66547              * @event evententer
66548              * Fires when mouse over an event
66549              * @param {Calendar} this
66550              * @param {event} Event
66551              */
66552         'evententer': true,
66553         /**
66554              * @event eventleave
66555              * Fires when the mouse leaves an
66556              * @param {Calendar} this
66557              * @param {event}
66558              */
66559         'eventleave': true,
66560         /**
66561              * @event eventclick
66562              * Fires when the mouse click an
66563              * @param {Calendar} this
66564              * @param {event}
66565              */
66566         'eventclick': true,
66567         /**
66568              * @event eventrender
66569              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
66570              * @param {Calendar} this
66571              * @param {data} data to be modified
66572              */
66573         'eventrender': true
66574         
66575     });
66576
66577     Roo.grid.Grid.superclass.constructor.call(this);
66578     this.on('render', function() {
66579         this.view.el.addClass('x-grid-cal'); 
66580         
66581         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
66582
66583     },this);
66584     
66585     if (!Roo.grid.Calendar.style) {
66586         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
66587             
66588             
66589             '.x-grid-cal .x-grid-col' :  {
66590                 height: 'auto !important',
66591                 'vertical-align': 'top'
66592             },
66593             '.x-grid-cal  .fc-event-hori' : {
66594                 height: '14px'
66595             }
66596              
66597             
66598         }, Roo.id());
66599     }
66600
66601     
66602     
66603 };
66604 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
66605     /**
66606      * @cfg {Store} eventStore The store that loads events.
66607      */
66608     eventStore : 25,
66609
66610      
66611     activeDate : false,
66612     startDay : 0,
66613     autoWidth : true,
66614     monitorWindowResize : false,
66615
66616     
66617     resizeColumns : function() {
66618         var col = (this.view.el.getWidth() / 7) - 3;
66619         // loop through cols, and setWidth
66620         for(var i =0 ; i < 7 ; i++){
66621             this.cm.setColumnWidth(i, col);
66622         }
66623     },
66624      setDate :function(date) {
66625         
66626         Roo.log('setDate?');
66627         
66628         this.resizeColumns();
66629         var vd = this.activeDate;
66630         this.activeDate = date;
66631 //        if(vd && this.el){
66632 //            var t = date.getTime();
66633 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
66634 //                Roo.log('using add remove');
66635 //                
66636 //                this.fireEvent('monthchange', this, date);
66637 //                
66638 //                this.cells.removeClass("fc-state-highlight");
66639 //                this.cells.each(function(c){
66640 //                   if(c.dateValue == t){
66641 //                       c.addClass("fc-state-highlight");
66642 //                       setTimeout(function(){
66643 //                            try{c.dom.firstChild.focus();}catch(e){}
66644 //                       }, 50);
66645 //                       return false;
66646 //                   }
66647 //                   return true;
66648 //                });
66649 //                return;
66650 //            }
66651 //        }
66652         
66653         var days = date.getDaysInMonth();
66654         
66655         var firstOfMonth = date.getFirstDateOfMonth();
66656         var startingPos = firstOfMonth.getDay()-this.startDay;
66657         
66658         if(startingPos < this.startDay){
66659             startingPos += 7;
66660         }
66661         
66662         var pm = date.add(Date.MONTH, -1);
66663         var prevStart = pm.getDaysInMonth()-startingPos;
66664 //        
66665         
66666         
66667         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66668         
66669         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
66670         //this.cells.addClassOnOver('fc-state-hover');
66671         
66672         var cells = this.cells.elements;
66673         var textEls = this.textNodes;
66674         
66675         //Roo.each(cells, function(cell){
66676         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
66677         //});
66678         
66679         days += startingPos;
66680
66681         // convert everything to numbers so it's fast
66682         var day = 86400000;
66683         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
66684         //Roo.log(d);
66685         //Roo.log(pm);
66686         //Roo.log(prevStart);
66687         
66688         var today = new Date().clearTime().getTime();
66689         var sel = date.clearTime().getTime();
66690         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
66691         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
66692         var ddMatch = this.disabledDatesRE;
66693         var ddText = this.disabledDatesText;
66694         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
66695         var ddaysText = this.disabledDaysText;
66696         var format = this.format;
66697         
66698         var setCellClass = function(cal, cell){
66699             
66700             //Roo.log('set Cell Class');
66701             cell.title = "";
66702             var t = d.getTime();
66703             
66704             //Roo.log(d);
66705             
66706             
66707             cell.dateValue = t;
66708             if(t == today){
66709                 cell.className += " fc-today";
66710                 cell.className += " fc-state-highlight";
66711                 cell.title = cal.todayText;
66712             }
66713             if(t == sel){
66714                 // disable highlight in other month..
66715                 cell.className += " fc-state-highlight";
66716                 
66717             }
66718             // disabling
66719             if(t < min) {
66720                 //cell.className = " fc-state-disabled";
66721                 cell.title = cal.minText;
66722                 return;
66723             }
66724             if(t > max) {
66725                 //cell.className = " fc-state-disabled";
66726                 cell.title = cal.maxText;
66727                 return;
66728             }
66729             if(ddays){
66730                 if(ddays.indexOf(d.getDay()) != -1){
66731                     // cell.title = ddaysText;
66732                    // cell.className = " fc-state-disabled";
66733                 }
66734             }
66735             if(ddMatch && format){
66736                 var fvalue = d.dateFormat(format);
66737                 if(ddMatch.test(fvalue)){
66738                     cell.title = ddText.replace("%0", fvalue);
66739                    cell.className = " fc-state-disabled";
66740                 }
66741             }
66742             
66743             if (!cell.initialClassName) {
66744                 cell.initialClassName = cell.dom.className;
66745             }
66746             
66747             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
66748         };
66749
66750         var i = 0;
66751         
66752         for(; i < startingPos; i++) {
66753             cells[i].dayName =  (++prevStart);
66754             Roo.log(textEls[i]);
66755             d.setDate(d.getDate()+1);
66756             
66757             //cells[i].className = "fc-past fc-other-month";
66758             setCellClass(this, cells[i]);
66759         }
66760         
66761         var intDay = 0;
66762         
66763         for(; i < days; i++){
66764             intDay = i - startingPos + 1;
66765             cells[i].dayName =  (intDay);
66766             d.setDate(d.getDate()+1);
66767             
66768             cells[i].className = ''; // "x-date-active";
66769             setCellClass(this, cells[i]);
66770         }
66771         var extraDays = 0;
66772         
66773         for(; i < 42; i++) {
66774             //textEls[i].innerHTML = (++extraDays);
66775             
66776             d.setDate(d.getDate()+1);
66777             cells[i].dayName = (++extraDays);
66778             cells[i].className = "fc-future fc-other-month";
66779             setCellClass(this, cells[i]);
66780         }
66781         
66782         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
66783         
66784         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
66785         
66786         // this will cause all the cells to mis
66787         var rows= [];
66788         var i =0;
66789         for (var r = 0;r < 6;r++) {
66790             for (var c =0;c < 7;c++) {
66791                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
66792             }    
66793         }
66794         
66795         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66796         for(i=0;i<cells.length;i++) {
66797             
66798             this.cells.elements[i].dayName = cells[i].dayName ;
66799             this.cells.elements[i].className = cells[i].className;
66800             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
66801             this.cells.elements[i].title = cells[i].title ;
66802             this.cells.elements[i].dateValue = cells[i].dateValue ;
66803         }
66804         
66805         
66806         
66807         
66808         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
66809         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
66810         
66811         ////if(totalRows != 6){
66812             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
66813            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
66814        // }
66815         
66816         this.fireEvent('monthchange', this, date);
66817         
66818         
66819     },
66820  /**
66821      * Returns the grid's SelectionModel.
66822      * @return {SelectionModel}
66823      */
66824     getSelectionModel : function(){
66825         if(!this.selModel){
66826             this.selModel = new Roo.grid.CellSelectionModel();
66827         }
66828         return this.selModel;
66829     },
66830
66831     load: function() {
66832         this.eventStore.load()
66833         
66834         
66835         
66836     },
66837     
66838     findCell : function(dt) {
66839         dt = dt.clearTime().getTime();
66840         var ret = false;
66841         this.cells.each(function(c){
66842             //Roo.log("check " +c.dateValue + '?=' + dt);
66843             if(c.dateValue == dt){
66844                 ret = c;
66845                 return false;
66846             }
66847             return true;
66848         });
66849         
66850         return ret;
66851     },
66852     
66853     findCells : function(rec) {
66854         var s = rec.data.start_dt.clone().clearTime().getTime();
66855        // Roo.log(s);
66856         var e= rec.data.end_dt.clone().clearTime().getTime();
66857        // Roo.log(e);
66858         var ret = [];
66859         this.cells.each(function(c){
66860              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
66861             
66862             if(c.dateValue > e){
66863                 return ;
66864             }
66865             if(c.dateValue < s){
66866                 return ;
66867             }
66868             ret.push(c);
66869         });
66870         
66871         return ret;    
66872     },
66873     
66874     findBestRow: function(cells)
66875     {
66876         var ret = 0;
66877         
66878         for (var i =0 ; i < cells.length;i++) {
66879             ret  = Math.max(cells[i].rows || 0,ret);
66880         }
66881         return ret;
66882         
66883     },
66884     
66885     
66886     addItem : function(rec)
66887     {
66888         // look for vertical location slot in
66889         var cells = this.findCells(rec);
66890         
66891         rec.row = this.findBestRow(cells);
66892         
66893         // work out the location.
66894         
66895         var crow = false;
66896         var rows = [];
66897         for(var i =0; i < cells.length; i++) {
66898             if (!crow) {
66899                 crow = {
66900                     start : cells[i],
66901                     end :  cells[i]
66902                 };
66903                 continue;
66904             }
66905             if (crow.start.getY() == cells[i].getY()) {
66906                 // on same row.
66907                 crow.end = cells[i];
66908                 continue;
66909             }
66910             // different row.
66911             rows.push(crow);
66912             crow = {
66913                 start: cells[i],
66914                 end : cells[i]
66915             };
66916             
66917         }
66918         
66919         rows.push(crow);
66920         rec.els = [];
66921         rec.rows = rows;
66922         rec.cells = cells;
66923         for (var i = 0; i < cells.length;i++) {
66924             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
66925             
66926         }
66927         
66928         
66929     },
66930     
66931     clearEvents: function() {
66932         
66933         if (!this.eventStore.getCount()) {
66934             return;
66935         }
66936         // reset number of rows in cells.
66937         Roo.each(this.cells.elements, function(c){
66938             c.rows = 0;
66939         });
66940         
66941         this.eventStore.each(function(e) {
66942             this.clearEvent(e);
66943         },this);
66944         
66945     },
66946     
66947     clearEvent : function(ev)
66948     {
66949         if (ev.els) {
66950             Roo.each(ev.els, function(el) {
66951                 el.un('mouseenter' ,this.onEventEnter, this);
66952                 el.un('mouseleave' ,this.onEventLeave, this);
66953                 el.remove();
66954             },this);
66955             ev.els = [];
66956         }
66957     },
66958     
66959     
66960     renderEvent : function(ev,ctr) {
66961         if (!ctr) {
66962              ctr = this.view.el.select('.fc-event-container',true).first();
66963         }
66964         
66965          
66966         this.clearEvent(ev);
66967             //code
66968        
66969         
66970         
66971         ev.els = [];
66972         var cells = ev.cells;
66973         var rows = ev.rows;
66974         this.fireEvent('eventrender', this, ev);
66975         
66976         for(var i =0; i < rows.length; i++) {
66977             
66978             cls = '';
66979             if (i == 0) {
66980                 cls += ' fc-event-start';
66981             }
66982             if ((i+1) == rows.length) {
66983                 cls += ' fc-event-end';
66984             }
66985             
66986             //Roo.log(ev.data);
66987             // how many rows should it span..
66988             var cg = this.eventTmpl.append(ctr,Roo.apply({
66989                 fccls : cls
66990                 
66991             }, ev.data) , true);
66992             
66993             
66994             cg.on('mouseenter' ,this.onEventEnter, this, ev);
66995             cg.on('mouseleave' ,this.onEventLeave, this, ev);
66996             cg.on('click', this.onEventClick, this, ev);
66997             
66998             ev.els.push(cg);
66999             
67000             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
67001             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
67002             //Roo.log(cg);
67003              
67004             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
67005             cg.setWidth(ebox.right - sbox.x -2);
67006         }
67007     },
67008     
67009     renderEvents: function()
67010     {   
67011         // first make sure there is enough space..
67012         
67013         if (!this.eventTmpl) {
67014             this.eventTmpl = new Roo.Template(
67015                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
67016                     '<div class="fc-event-inner">' +
67017                         '<span class="fc-event-time">{time}</span>' +
67018                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
67019                     '</div>' +
67020                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
67021                 '</div>'
67022             );
67023                 
67024         }
67025                
67026         
67027         
67028         this.cells.each(function(c) {
67029             //Roo.log(c.select('.fc-day-content div',true).first());
67030             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
67031         });
67032         
67033         var ctr = this.view.el.select('.fc-event-container',true).first();
67034         
67035         var cls;
67036         this.eventStore.each(function(ev){
67037             
67038             this.renderEvent(ev);
67039              
67040              
67041         }, this);
67042         this.view.layout();
67043         
67044     },
67045     
67046     onEventEnter: function (e, el,event,d) {
67047         this.fireEvent('evententer', this, el, event);
67048     },
67049     
67050     onEventLeave: function (e, el,event,d) {
67051         this.fireEvent('eventleave', this, el, event);
67052     },
67053     
67054     onEventClick: function (e, el,event,d) {
67055         this.fireEvent('eventclick', this, el, event);
67056     },
67057     
67058     onMonthChange: function () {
67059         this.store.load();
67060     },
67061     
67062     onLoad: function () {
67063         
67064         //Roo.log('calendar onload');
67065 //         
67066         if(this.eventStore.getCount() > 0){
67067             
67068            
67069             
67070             this.eventStore.each(function(d){
67071                 
67072                 
67073                 // FIXME..
67074                 var add =   d.data;
67075                 if (typeof(add.end_dt) == 'undefined')  {
67076                     Roo.log("Missing End time in calendar data: ");
67077                     Roo.log(d);
67078                     return;
67079                 }
67080                 if (typeof(add.start_dt) == 'undefined')  {
67081                     Roo.log("Missing Start time in calendar data: ");
67082                     Roo.log(d);
67083                     return;
67084                 }
67085                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
67086                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
67087                 add.id = add.id || d.id;
67088                 add.title = add.title || '??';
67089                 
67090                 this.addItem(d);
67091                 
67092              
67093             },this);
67094         }
67095         
67096         this.renderEvents();
67097     }
67098     
67099
67100 });
67101 /*
67102  grid : {
67103                 xtype: 'Grid',
67104                 xns: Roo.grid,
67105                 listeners : {
67106                     render : function ()
67107                     {
67108                         _this.grid = this;
67109                         
67110                         if (!this.view.el.hasClass('course-timesheet')) {
67111                             this.view.el.addClass('course-timesheet');
67112                         }
67113                         if (this.tsStyle) {
67114                             this.ds.load({});
67115                             return; 
67116                         }
67117                         Roo.log('width');
67118                         Roo.log(_this.grid.view.el.getWidth());
67119                         
67120                         
67121                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
67122                             '.course-timesheet .x-grid-row' : {
67123                                 height: '80px'
67124                             },
67125                             '.x-grid-row td' : {
67126                                 'vertical-align' : 0
67127                             },
67128                             '.course-edit-link' : {
67129                                 'color' : 'blue',
67130                                 'text-overflow' : 'ellipsis',
67131                                 'overflow' : 'hidden',
67132                                 'white-space' : 'nowrap',
67133                                 'cursor' : 'pointer'
67134                             },
67135                             '.sub-link' : {
67136                                 'color' : 'green'
67137                             },
67138                             '.de-act-sup-link' : {
67139                                 'color' : 'purple',
67140                                 'text-decoration' : 'line-through'
67141                             },
67142                             '.de-act-link' : {
67143                                 'color' : 'red',
67144                                 'text-decoration' : 'line-through'
67145                             },
67146                             '.course-timesheet .course-highlight' : {
67147                                 'border-top-style': 'dashed !important',
67148                                 'border-bottom-bottom': 'dashed !important'
67149                             },
67150                             '.course-timesheet .course-item' : {
67151                                 'font-family'   : 'tahoma, arial, helvetica',
67152                                 'font-size'     : '11px',
67153                                 'overflow'      : 'hidden',
67154                                 'padding-left'  : '10px',
67155                                 'padding-right' : '10px',
67156                                 'padding-top' : '10px' 
67157                             }
67158                             
67159                         }, Roo.id());
67160                                 this.ds.load({});
67161                     }
67162                 },
67163                 autoWidth : true,
67164                 monitorWindowResize : false,
67165                 cellrenderer : function(v,x,r)
67166                 {
67167                     return v;
67168                 },
67169                 sm : {
67170                     xtype: 'CellSelectionModel',
67171                     xns: Roo.grid
67172                 },
67173                 dataSource : {
67174                     xtype: 'Store',
67175                     xns: Roo.data,
67176                     listeners : {
67177                         beforeload : function (_self, options)
67178                         {
67179                             options.params = options.params || {};
67180                             options.params._month = _this.monthField.getValue();
67181                             options.params.limit = 9999;
67182                             options.params['sort'] = 'when_dt';    
67183                             options.params['dir'] = 'ASC';    
67184                             this.proxy.loadResponse = this.loadResponse;
67185                             Roo.log("load?");
67186                             //this.addColumns();
67187                         },
67188                         load : function (_self, records, options)
67189                         {
67190                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
67191                                 // if you click on the translation.. you can edit it...
67192                                 var el = Roo.get(this);
67193                                 var id = el.dom.getAttribute('data-id');
67194                                 var d = el.dom.getAttribute('data-date');
67195                                 var t = el.dom.getAttribute('data-time');
67196                                 //var id = this.child('span').dom.textContent;
67197                                 
67198                                 //Roo.log(this);
67199                                 Pman.Dialog.CourseCalendar.show({
67200                                     id : id,
67201                                     when_d : d,
67202                                     when_t : t,
67203                                     productitem_active : id ? 1 : 0
67204                                 }, function() {
67205                                     _this.grid.ds.load({});
67206                                 });
67207                            
67208                            });
67209                            
67210                            _this.panel.fireEvent('resize', [ '', '' ]);
67211                         }
67212                     },
67213                     loadResponse : function(o, success, response){
67214                             // this is overridden on before load..
67215                             
67216                             Roo.log("our code?");       
67217                             //Roo.log(success);
67218                             //Roo.log(response)
67219                             delete this.activeRequest;
67220                             if(!success){
67221                                 this.fireEvent("loadexception", this, o, response);
67222                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67223                                 return;
67224                             }
67225                             var result;
67226                             try {
67227                                 result = o.reader.read(response);
67228                             }catch(e){
67229                                 Roo.log("load exception?");
67230                                 this.fireEvent("loadexception", this, o, response, e);
67231                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67232                                 return;
67233                             }
67234                             Roo.log("ready...");        
67235                             // loop through result.records;
67236                             // and set this.tdate[date] = [] << array of records..
67237                             _this.tdata  = {};
67238                             Roo.each(result.records, function(r){
67239                                 //Roo.log(r.data);
67240                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
67241                                     _this.tdata[r.data.when_dt.format('j')] = [];
67242                                 }
67243                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
67244                             });
67245                             
67246                             //Roo.log(_this.tdata);
67247                             
67248                             result.records = [];
67249                             result.totalRecords = 6;
67250                     
67251                             // let's generate some duumy records for the rows.
67252                             //var st = _this.dateField.getValue();
67253                             
67254                             // work out monday..
67255                             //st = st.add(Date.DAY, -1 * st.format('w'));
67256                             
67257                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67258                             
67259                             var firstOfMonth = date.getFirstDayOfMonth();
67260                             var days = date.getDaysInMonth();
67261                             var d = 1;
67262                             var firstAdded = false;
67263                             for (var i = 0; i < result.totalRecords ; i++) {
67264                                 //var d= st.add(Date.DAY, i);
67265                                 var row = {};
67266                                 var added = 0;
67267                                 for(var w = 0 ; w < 7 ; w++){
67268                                     if(!firstAdded && firstOfMonth != w){
67269                                         continue;
67270                                     }
67271                                     if(d > days){
67272                                         continue;
67273                                     }
67274                                     firstAdded = true;
67275                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
67276                                     row['weekday'+w] = String.format(
67277                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
67278                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
67279                                                     d,
67280                                                     date.format('Y-m-')+dd
67281                                                 );
67282                                     added++;
67283                                     if(typeof(_this.tdata[d]) != 'undefined'){
67284                                         Roo.each(_this.tdata[d], function(r){
67285                                             var is_sub = '';
67286                                             var deactive = '';
67287                                             var id = r.id;
67288                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
67289                                             if(r.parent_id*1>0){
67290                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
67291                                                 id = r.parent_id;
67292                                             }
67293                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
67294                                                 deactive = 'de-act-link';
67295                                             }
67296                                             
67297                                             row['weekday'+w] += String.format(
67298                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
67299                                                     id, //0
67300                                                     r.product_id_name, //1
67301                                                     r.when_dt.format('h:ia'), //2
67302                                                     is_sub, //3
67303                                                     deactive, //4
67304                                                     desc // 5
67305                                             );
67306                                         });
67307                                     }
67308                                     d++;
67309                                 }
67310                                 
67311                                 // only do this if something added..
67312                                 if(added > 0){ 
67313                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
67314                                 }
67315                                 
67316                                 
67317                                 // push it twice. (second one with an hour..
67318                                 
67319                             }
67320                             //Roo.log(result);
67321                             this.fireEvent("load", this, o, o.request.arg);
67322                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
67323                         },
67324                     sortInfo : {field: 'when_dt', direction : 'ASC' },
67325                     proxy : {
67326                         xtype: 'HttpProxy',
67327                         xns: Roo.data,
67328                         method : 'GET',
67329                         url : baseURL + '/Roo/Shop_course.php'
67330                     },
67331                     reader : {
67332                         xtype: 'JsonReader',
67333                         xns: Roo.data,
67334                         id : 'id',
67335                         fields : [
67336                             {
67337                                 'name': 'id',
67338                                 'type': 'int'
67339                             },
67340                             {
67341                                 'name': 'when_dt',
67342                                 'type': 'string'
67343                             },
67344                             {
67345                                 'name': 'end_dt',
67346                                 'type': 'string'
67347                             },
67348                             {
67349                                 'name': 'parent_id',
67350                                 'type': 'int'
67351                             },
67352                             {
67353                                 'name': 'product_id',
67354                                 'type': 'int'
67355                             },
67356                             {
67357                                 'name': 'productitem_id',
67358                                 'type': 'int'
67359                             },
67360                             {
67361                                 'name': 'guid',
67362                                 'type': 'int'
67363                             }
67364                         ]
67365                     }
67366                 },
67367                 toolbar : {
67368                     xtype: 'Toolbar',
67369                     xns: Roo,
67370                     items : [
67371                         {
67372                             xtype: 'Button',
67373                             xns: Roo.Toolbar,
67374                             listeners : {
67375                                 click : function (_self, e)
67376                                 {
67377                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67378                                     sd.setMonth(sd.getMonth()-1);
67379                                     _this.monthField.setValue(sd.format('Y-m-d'));
67380                                     _this.grid.ds.load({});
67381                                 }
67382                             },
67383                             text : "Back"
67384                         },
67385                         {
67386                             xtype: 'Separator',
67387                             xns: Roo.Toolbar
67388                         },
67389                         {
67390                             xtype: 'MonthField',
67391                             xns: Roo.form,
67392                             listeners : {
67393                                 render : function (_self)
67394                                 {
67395                                     _this.monthField = _self;
67396                                    // _this.monthField.set  today
67397                                 },
67398                                 select : function (combo, date)
67399                                 {
67400                                     _this.grid.ds.load({});
67401                                 }
67402                             },
67403                             value : (function() { return new Date(); })()
67404                         },
67405                         {
67406                             xtype: 'Separator',
67407                             xns: Roo.Toolbar
67408                         },
67409                         {
67410                             xtype: 'TextItem',
67411                             xns: Roo.Toolbar,
67412                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
67413                         },
67414                         {
67415                             xtype: 'Fill',
67416                             xns: Roo.Toolbar
67417                         },
67418                         {
67419                             xtype: 'Button',
67420                             xns: Roo.Toolbar,
67421                             listeners : {
67422                                 click : function (_self, e)
67423                                 {
67424                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67425                                     sd.setMonth(sd.getMonth()+1);
67426                                     _this.monthField.setValue(sd.format('Y-m-d'));
67427                                     _this.grid.ds.load({});
67428                                 }
67429                             },
67430                             text : "Next"
67431                         }
67432                     ]
67433                 },
67434                  
67435             }
67436         };
67437         
67438         *//*
67439  * Based on:
67440  * Ext JS Library 1.1.1
67441  * Copyright(c) 2006-2007, Ext JS, LLC.
67442  *
67443  * Originally Released Under LGPL - original licence link has changed is not relivant.
67444  *
67445  * Fork - LGPL
67446  * <script type="text/javascript">
67447  */
67448  
67449 /**
67450  * @class Roo.LoadMask
67451  * A simple utility class for generically masking elements while loading data.  If the element being masked has
67452  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
67453  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
67454  * element's UpdateManager load indicator and will be destroyed after the initial load.
67455  * @constructor
67456  * Create a new LoadMask
67457  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
67458  * @param {Object} config The config object
67459  */
67460 Roo.LoadMask = function(el, config){
67461     this.el = Roo.get(el);
67462     Roo.apply(this, config);
67463     if(this.store){
67464         this.store.on('beforeload', this.onBeforeLoad, this);
67465         this.store.on('load', this.onLoad, this);
67466         this.store.on('loadexception', this.onLoadException, this);
67467         this.removeMask = false;
67468     }else{
67469         var um = this.el.getUpdateManager();
67470         um.showLoadIndicator = false; // disable the default indicator
67471         um.on('beforeupdate', this.onBeforeLoad, this);
67472         um.on('update', this.onLoad, this);
67473         um.on('failure', this.onLoad, this);
67474         this.removeMask = true;
67475     }
67476 };
67477
67478 Roo.LoadMask.prototype = {
67479     /**
67480      * @cfg {Boolean} removeMask
67481      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
67482      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
67483      */
67484     removeMask : false,
67485     /**
67486      * @cfg {String} msg
67487      * The text to display in a centered loading message box (defaults to 'Loading...')
67488      */
67489     msg : 'Loading...',
67490     /**
67491      * @cfg {String} msgCls
67492      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
67493      */
67494     msgCls : 'x-mask-loading',
67495
67496     /**
67497      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
67498      * @type Boolean
67499      */
67500     disabled: false,
67501
67502     /**
67503      * Disables the mask to prevent it from being displayed
67504      */
67505     disable : function(){
67506        this.disabled = true;
67507     },
67508
67509     /**
67510      * Enables the mask so that it can be displayed
67511      */
67512     enable : function(){
67513         this.disabled = false;
67514     },
67515     
67516     onLoadException : function()
67517     {
67518         Roo.log(arguments);
67519         
67520         if (typeof(arguments[3]) != 'undefined') {
67521             Roo.MessageBox.alert("Error loading",arguments[3]);
67522         } 
67523         /*
67524         try {
67525             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
67526                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
67527             }   
67528         } catch(e) {
67529             
67530         }
67531         */
67532     
67533         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67534     },
67535     // private
67536     onLoad : function()
67537     {
67538         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67539     },
67540
67541     // private
67542     onBeforeLoad : function(){
67543         if(!this.disabled){
67544             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
67545         }
67546     },
67547
67548     // private
67549     destroy : function(){
67550         if(this.store){
67551             this.store.un('beforeload', this.onBeforeLoad, this);
67552             this.store.un('load', this.onLoad, this);
67553             this.store.un('loadexception', this.onLoadException, this);
67554         }else{
67555             var um = this.el.getUpdateManager();
67556             um.un('beforeupdate', this.onBeforeLoad, this);
67557             um.un('update', this.onLoad, this);
67558             um.un('failure', this.onLoad, this);
67559         }
67560     }
67561 };/*
67562  * Based on:
67563  * Ext JS Library 1.1.1
67564  * Copyright(c) 2006-2007, Ext JS, LLC.
67565  *
67566  * Originally Released Under LGPL - original licence link has changed is not relivant.
67567  *
67568  * Fork - LGPL
67569  * <script type="text/javascript">
67570  */
67571
67572
67573 /**
67574  * @class Roo.XTemplate
67575  * @extends Roo.Template
67576  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
67577 <pre><code>
67578 var t = new Roo.XTemplate(
67579         '&lt;select name="{name}"&gt;',
67580                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
67581         '&lt;/select&gt;'
67582 );
67583  
67584 // then append, applying the master template values
67585  </code></pre>
67586  *
67587  * Supported features:
67588  *
67589  *  Tags:
67590
67591 <pre><code>
67592       {a_variable} - output encoded.
67593       {a_variable.format:("Y-m-d")} - call a method on the variable
67594       {a_variable:raw} - unencoded output
67595       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
67596       {a_variable:this.method_on_template(...)} - call a method on the template object.
67597  
67598 </code></pre>
67599  *  The tpl tag:
67600 <pre><code>
67601         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
67602         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
67603         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
67604         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
67605   
67606         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
67607         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
67608 </code></pre>
67609  *      
67610  */
67611 Roo.XTemplate = function()
67612 {
67613     Roo.XTemplate.superclass.constructor.apply(this, arguments);
67614     if (this.html) {
67615         this.compile();
67616     }
67617 };
67618
67619
67620 Roo.extend(Roo.XTemplate, Roo.Template, {
67621
67622     /**
67623      * The various sub templates
67624      */
67625     tpls : false,
67626     /**
67627      *
67628      * basic tag replacing syntax
67629      * WORD:WORD()
67630      *
67631      * // you can fake an object call by doing this
67632      *  x.t:(test,tesT) 
67633      * 
67634      */
67635     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
67636
67637     /**
67638      * compile the template
67639      *
67640      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
67641      *
67642      */
67643     compile: function()
67644     {
67645         var s = this.html;
67646      
67647         s = ['<tpl>', s, '</tpl>'].join('');
67648     
67649         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
67650             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
67651             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
67652             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
67653             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
67654             m,
67655             id     = 0,
67656             tpls   = [];
67657     
67658         while(true == !!(m = s.match(re))){
67659             var forMatch   = m[0].match(nameRe),
67660                 ifMatch   = m[0].match(ifRe),
67661                 execMatch   = m[0].match(execRe),
67662                 namedMatch   = m[0].match(namedRe),
67663                 
67664                 exp  = null, 
67665                 fn   = null,
67666                 exec = null,
67667                 name = forMatch && forMatch[1] ? forMatch[1] : '';
67668                 
67669             if (ifMatch) {
67670                 // if - puts fn into test..
67671                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
67672                 if(exp){
67673                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
67674                 }
67675             }
67676             
67677             if (execMatch) {
67678                 // exec - calls a function... returns empty if true is  returned.
67679                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
67680                 if(exp){
67681                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
67682                 }
67683             }
67684             
67685             
67686             if (name) {
67687                 // for = 
67688                 switch(name){
67689                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
67690                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
67691                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
67692                 }
67693             }
67694             var uid = namedMatch ? namedMatch[1] : id;
67695             
67696             
67697             tpls.push({
67698                 id:     namedMatch ? namedMatch[1] : id,
67699                 target: name,
67700                 exec:   exec,
67701                 test:   fn,
67702                 body:   m[1] || ''
67703             });
67704             if (namedMatch) {
67705                 s = s.replace(m[0], '');
67706             } else { 
67707                 s = s.replace(m[0], '{xtpl'+ id + '}');
67708             }
67709             ++id;
67710         }
67711         this.tpls = [];
67712         for(var i = tpls.length-1; i >= 0; --i){
67713             this.compileTpl(tpls[i]);
67714             this.tpls[tpls[i].id] = tpls[i];
67715         }
67716         this.master = tpls[tpls.length-1];
67717         return this;
67718     },
67719     /**
67720      * same as applyTemplate, except it's done to one of the subTemplates
67721      * when using named templates, you can do:
67722      *
67723      * var str = pl.applySubTemplate('your-name', values);
67724      *
67725      * 
67726      * @param {Number} id of the template
67727      * @param {Object} values to apply to template
67728      * @param {Object} parent (normaly the instance of this object)
67729      */
67730     applySubTemplate : function(id, values, parent)
67731     {
67732         
67733         
67734         var t = this.tpls[id];
67735         
67736         
67737         try { 
67738             if(t.test && !t.test.call(this, values, parent)){
67739                 return '';
67740             }
67741         } catch(e) {
67742             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
67743             Roo.log(e.toString());
67744             Roo.log(t.test);
67745             return ''
67746         }
67747         try { 
67748             
67749             if(t.exec && t.exec.call(this, values, parent)){
67750                 return '';
67751             }
67752         } catch(e) {
67753             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
67754             Roo.log(e.toString());
67755             Roo.log(t.exec);
67756             return ''
67757         }
67758         try {
67759             var vs = t.target ? t.target.call(this, values, parent) : values;
67760             parent = t.target ? values : parent;
67761             if(t.target && vs instanceof Array){
67762                 var buf = [];
67763                 for(var i = 0, len = vs.length; i < len; i++){
67764                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
67765                 }
67766                 return buf.join('');
67767             }
67768             return t.compiled.call(this, vs, parent);
67769         } catch (e) {
67770             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
67771             Roo.log(e.toString());
67772             Roo.log(t.compiled);
67773             return '';
67774         }
67775     },
67776
67777     compileTpl : function(tpl)
67778     {
67779         var fm = Roo.util.Format;
67780         var useF = this.disableFormats !== true;
67781         var sep = Roo.isGecko ? "+" : ",";
67782         var undef = function(str) {
67783             Roo.log("Property not found :"  + str);
67784             return '';
67785         };
67786         
67787         var fn = function(m, name, format, args)
67788         {
67789             //Roo.log(arguments);
67790             args = args ? args.replace(/\\'/g,"'") : args;
67791             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
67792             if (typeof(format) == 'undefined') {
67793                 format= 'htmlEncode';
67794             }
67795             if (format == 'raw' ) {
67796                 format = false;
67797             }
67798             
67799             if(name.substr(0, 4) == 'xtpl'){
67800                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
67801             }
67802             
67803             // build an array of options to determine if value is undefined..
67804             
67805             // basically get 'xxxx.yyyy' then do
67806             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
67807             //    (function () { Roo.log("Property not found"); return ''; })() :
67808             //    ......
67809             
67810             var udef_ar = [];
67811             var lookfor = '';
67812             Roo.each(name.split('.'), function(st) {
67813                 lookfor += (lookfor.length ? '.': '') + st;
67814                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
67815             });
67816             
67817             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
67818             
67819             
67820             if(format && useF){
67821                 
67822                 args = args ? ',' + args : "";
67823                  
67824                 if(format.substr(0, 5) != "this."){
67825                     format = "fm." + format + '(';
67826                 }else{
67827                     format = 'this.call("'+ format.substr(5) + '", ';
67828                     args = ", values";
67829                 }
67830                 
67831                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
67832             }
67833              
67834             if (args.length) {
67835                 // called with xxyx.yuu:(test,test)
67836                 // change to ()
67837                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
67838             }
67839             // raw.. - :raw modifier..
67840             return "'"+ sep + udef_st  + name + ")"+sep+"'";
67841             
67842         };
67843         var body;
67844         // branched to use + in gecko and [].join() in others
67845         if(Roo.isGecko){
67846             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
67847                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
67848                     "';};};";
67849         }else{
67850             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
67851             body.push(tpl.body.replace(/(\r\n|\n)/g,
67852                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
67853             body.push("'].join('');};};");
67854             body = body.join('');
67855         }
67856         
67857         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
67858        
67859         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
67860         eval(body);
67861         
67862         return this;
67863     },
67864
67865     applyTemplate : function(values){
67866         return this.master.compiled.call(this, values, {});
67867         //var s = this.subs;
67868     },
67869
67870     apply : function(){
67871         return this.applyTemplate.apply(this, arguments);
67872     }
67873
67874  });
67875
67876 Roo.XTemplate.from = function(el){
67877     el = Roo.getDom(el);
67878     return new Roo.XTemplate(el.value || el.innerHTML);
67879 };