afeb13171e96348036538d0afe30cadf4958b895
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 } 
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux" 
704                );
705 /*
706  * Based on:
707  * Ext JS Library 1.1.1
708  * Copyright(c) 2006-2007, Ext JS, LLC.
709  *
710  * Originally Released Under LGPL - original licence link has changed is not relivant.
711  *
712  * Fork - LGPL
713  * <script type="text/javascript">
714  */
715
716 (function() {    
717     // wrappedn so fnCleanup is not in global scope...
718     if(Roo.isIE) {
719         function fnCleanUp() {
720             var p = Function.prototype;
721             delete p.createSequence;
722             delete p.defer;
723             delete p.createDelegate;
724             delete p.createCallback;
725             delete p.createInterceptor;
726
727             window.detachEvent("onunload", fnCleanUp);
728         }
729         window.attachEvent("onunload", fnCleanUp);
730     }
731 })();
732
733
734 /**
735  * @class Function
736  * These functions are available on every Function object (any JavaScript function).
737  */
738 Roo.apply(Function.prototype, {
739      /**
740      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742      * Will create a function that is bound to those 2 args.
743      * @return {Function} The new function
744     */
745     createCallback : function(/*args...*/){
746         // make args available, in function below
747         var args = arguments;
748         var method = this;
749         return function() {
750             return method.apply(window, args);
751         };
752     },
753
754     /**
755      * Creates a delegate (callback) that sets the scope to obj.
756      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757      * Will create a function that is automatically scoped to this.
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Function} The new function
763      */
764     createDelegate : function(obj, args, appendArgs){
765         var method = this;
766         return function() {
767             var callArgs = args || arguments;
768             if(appendArgs === true){
769                 callArgs = Array.prototype.slice.call(arguments, 0);
770                 callArgs = callArgs.concat(args);
771             }else if(typeof appendArgs == "number"){
772                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
775             }
776             return method.apply(obj || window, callArgs);
777         };
778     },
779
780     /**
781      * Calls this function after the number of millseconds specified.
782      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783      * @param {Object} obj (optional) The object for which the scope is set
784      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786      *                                             if a number the args are inserted at the specified position
787      * @return {Number} The timeout id that can be used with clearTimeout
788      */
789     defer : function(millis, obj, args, appendArgs){
790         var fn = this.createDelegate(obj, args, appendArgs);
791         if(millis){
792             return setTimeout(fn, millis);
793         }
794         fn();
795         return 0;
796     },
797     /**
798      * Create a combined function call sequence of the original function + the passed function.
799      * The resulting function returns the results of the original function.
800      * The passed fcn is called with the parameters of the original function
801      * @param {Function} fcn The function to sequence
802      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803      * @return {Function} The new function
804      */
805     createSequence : function(fcn, scope){
806         if(typeof fcn != "function"){
807             return this;
808         }
809         var method = this;
810         return function() {
811             var retval = method.apply(this || window, arguments);
812             fcn.apply(scope || this || window, arguments);
813             return retval;
814         };
815     },
816
817     /**
818      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819      * The resulting function returns the results of the original function.
820      * The passed fcn is called with the parameters of the original function.
821      * @addon
822      * @param {Function} fcn The function to call before the original
823      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824      * @return {Function} The new function
825      */
826     createInterceptor : function(fcn, scope){
827         if(typeof fcn != "function"){
828             return this;
829         }
830         var method = this;
831         return function() {
832             fcn.target = this;
833             fcn.method = method;
834             if(fcn.apply(scope || this || window, arguments) === false){
835                 return;
836             }
837             return method.apply(this || window, arguments);
838         };
839     }
840 });
841 /*
842  * Based on:
843  * Ext JS Library 1.1.1
844  * Copyright(c) 2006-2007, Ext JS, LLC.
845  *
846  * Originally Released Under LGPL - original licence link has changed is not relivant.
847  *
848  * Fork - LGPL
849  * <script type="text/javascript">
850  */
851
852 Roo.applyIf(String, {
853     
854     /** @scope String */
855     
856     /**
857      * Escapes the passed string for ' and \
858      * @param {String} string The string to escape
859      * @return {String} The escaped string
860      * @static
861      */
862     escape : function(string) {
863         return string.replace(/('|\\)/g, "\\$1");
864     },
865
866     /**
867      * Pads the left side of a string with a specified character.  This is especially useful
868      * for normalizing number and date strings.  Example usage:
869      * <pre><code>
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
872 </code></pre>
873      * @param {String} string The original string
874      * @param {Number} size The total length of the output string
875      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876      * @return {String} The padded string
877      * @static
878      */
879     leftPad : function (val, size, ch) {
880         var result = new String(val);
881         if(ch === null || ch === undefined || ch === '') {
882             ch = " ";
883         }
884         while (result.length < size) {
885             result = ch + result;
886         }
887         return result;
888     },
889
890     /**
891      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
892      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
893      * <pre><code>
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
897 </code></pre>
898      * @param {String} string The tokenized string to be formatted
899      * @param {String} value1 The value to replace token {0}
900      * @param {String} value2 Etc...
901      * @return {String} The formatted string
902      * @static
903      */
904     format : function(format){
905         var args = Array.prototype.slice.call(arguments, 1);
906         return format.replace(/\{(\d+)\}/g, function(m, i){
907             return Roo.util.Format.htmlEncode(args[i]);
908         });
909     }
910   
911     
912 });
913
914 /**
915  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
916  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
917  * they are already different, the first value passed in is returned.  Note that this method returns the new value
918  * but does not change the current string.
919  * <pre><code>
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
922
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
925 </code></pre>
926  * @param {String} value The value to compare to the current string
927  * @param {String} other The new value to use if the string already equals the first value passed in
928  * @return {String} The new value
929  */
930  
931 String.prototype.toggle = function(value, other){
932     return this == value ? other : value;
933 };
934
935
936 /**
937   * Remove invalid unicode characters from a string 
938   *
939   * @return {String} The clean string
940   */
941 String.prototype.unicodeClean = function () {
942     return this.replace(/[\s\S]/g,
943         function(character) {
944             if (character.charCodeAt()< 256) {
945               return character;
946            }
947            try {
948                 encodeURIComponent(character);
949            } catch(e) { 
950               return '';
951            }
952            return character;
953         }
954     );
955 };
956   
957
958 /**
959   * Make the first letter of a string uppercase
960   *
961   * @return {String} The new string.
962   */
963 String.prototype.toUpperCaseFirst = function () {
964     return this.charAt(0).toUpperCase() + this.slice(1);
965 };  
966   
967 /*
968  * Based on:
969  * Ext JS Library 1.1.1
970  * Copyright(c) 2006-2007, Ext JS, LLC.
971  *
972  * Originally Released Under LGPL - original licence link has changed is not relivant.
973  *
974  * Fork - LGPL
975  * <script type="text/javascript">
976  */
977
978  /**
979  * @class Number
980  */
981 Roo.applyIf(Number.prototype, {
982     /**
983      * Checks whether or not the current number is within a desired range.  If the number is already within the
984      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
985      * exceeded.  Note that this method returns the constrained value but does not change the current number.
986      * @param {Number} min The minimum number in the range
987      * @param {Number} max The maximum number in the range
988      * @return {Number} The constrained value if outside the range, otherwise the current value
989      */
990     constrain : function(min, max){
991         return Math.min(Math.max(this, min), max);
992     }
993 });/*
994  * Based on:
995  * Ext JS Library 1.1.1
996  * Copyright(c) 2006-2007, Ext JS, LLC.
997  *
998  * Originally Released Under LGPL - original licence link has changed is not relivant.
999  *
1000  * Fork - LGPL
1001  * <script type="text/javascript">
1002  */
1003  /**
1004  * @class Array
1005  */
1006 Roo.applyIf(Array.prototype, {
1007     /**
1008      * 
1009      * Checks whether or not the specified object exists in the array.
1010      * @param {Object} o The object to check for
1011      * @return {Number} The index of o in the array (or -1 if it is not found)
1012      */
1013     indexOf : function(o){
1014        for (var i = 0, len = this.length; i < len; i++){
1015               if(this[i] == o) { return i; }
1016        }
1017            return -1;
1018     },
1019
1020     /**
1021      * Removes the specified object from the array.  If the object is not found nothing happens.
1022      * @param {Object} o The object to remove
1023      */
1024     remove : function(o){
1025        var index = this.indexOf(o);
1026        if(index != -1){
1027            this.splice(index, 1);
1028        }
1029     },
1030     /**
1031      * Map (JS 1.6 compatibility)
1032      * @param {Function} function  to call
1033      */
1034     map : function(fun )
1035     {
1036         var len = this.length >>> 0;
1037         if (typeof fun != "function") {
1038             throw new TypeError();
1039         }
1040         var res = new Array(len);
1041         var thisp = arguments[1];
1042         for (var i = 0; i < len; i++)
1043         {
1044             if (i in this) {
1045                 res[i] = fun.call(thisp, this[i], i, this);
1046             }
1047         }
1048
1049         return res;
1050     },
1051     /**
1052      * equals
1053      * @param {Array} o The array to compare to
1054      * @returns {Boolean} true if the same
1055      */
1056     equals : function(b)
1057     {
1058             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1059         if (this === b) {
1060             return true;
1061         }
1062         if (b == null) {
1063             return false;
1064         }
1065         if (this.length !== b.length) {
1066             return false;
1067         }
1068           
1069         // sort?? a.sort().equals(b.sort());
1070           
1071         for (var i = 0; i < this.length; ++i) {
1072             if (this[i] !== b[i]) {
1073             return false;
1074             }
1075         }
1076         return true;
1077     } 
1078     
1079     
1080     
1081     
1082 });
1083
1084 Roo.applyIf(Array, {
1085  /**
1086      * from
1087      * @static
1088      * @param {Array} o Or Array like object (eg. nodelist)
1089      * @returns {Array} 
1090      */
1091     from : function(o)
1092     {
1093         var ret= [];
1094     
1095         for (var i =0; i < o.length; i++) { 
1096             ret[i] = o[i];
1097         }
1098         return ret;
1099       
1100     }
1101 });
1102 /*
1103  * Based on:
1104  * Ext JS Library 1.1.1
1105  * Copyright(c) 2006-2007, Ext JS, LLC.
1106  *
1107  * Originally Released Under LGPL - original licence link has changed is not relivant.
1108  *
1109  * Fork - LGPL
1110  * <script type="text/javascript">
1111  */
1112
1113 /**
1114  * @class Date
1115  *
1116  * The date parsing and format syntax is a subset of
1117  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1118  * supported will provide results equivalent to their PHP versions.
1119  *
1120  * Following is the list of all currently supported formats:
1121  *<pre>
1122 Sample date:
1123 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1124
1125 Format  Output      Description
1126 ------  ----------  --------------------------------------------------------------
1127   d      10         Day of the month, 2 digits with leading zeros
1128   D      Wed        A textual representation of a day, three letters
1129   j      10         Day of the month without leading zeros
1130   l      Wednesday  A full textual representation of the day of the week
1131   S      th         English ordinal day of month suffix, 2 chars (use with j)
1132   w      3          Numeric representation of the day of the week
1133   z      9          The julian date, or day of the year (0-365)
1134   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1135   F      January    A full textual representation of the month
1136   m      01         Numeric representation of a month, with leading zeros
1137   M      Jan        Month name abbreviation, three letters
1138   n      1          Numeric representation of a month, without leading zeros
1139   t      31         Number of days in the given month
1140   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1141   Y      2007       A full numeric representation of a year, 4 digits
1142   y      07         A two digit representation of a year
1143   a      pm         Lowercase Ante meridiem and Post meridiem
1144   A      PM         Uppercase Ante meridiem and Post meridiem
1145   g      3          12-hour format of an hour without leading zeros
1146   G      15         24-hour format of an hour without leading zeros
1147   h      03         12-hour format of an hour with leading zeros
1148   H      15         24-hour format of an hour with leading zeros
1149   i      05         Minutes with leading zeros
1150   s      01         Seconds, with leading zeros
1151   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1152   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1153   T      CST        Timezone setting of the machine running the code
1154   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1155 </pre>
1156  *
1157  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1158  * <pre><code>
1159 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1160 document.write(dt.format('Y-m-d'));                         //2007-01-10
1161 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1162 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1163  </code></pre>
1164  *
1165  * Here are some standard date/time patterns that you might find helpful.  They
1166  * are not part of the source of Date.js, but to use them you can simply copy this
1167  * block of code into any script that is included after Date.js and they will also become
1168  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1169  * <pre><code>
1170 Date.patterns = {
1171     ISO8601Long:"Y-m-d H:i:s",
1172     ISO8601Short:"Y-m-d",
1173     ShortDate: "n/j/Y",
1174     LongDate: "l, F d, Y",
1175     FullDateTime: "l, F d, Y g:i:s A",
1176     MonthDay: "F d",
1177     ShortTime: "g:i A",
1178     LongTime: "g:i:s A",
1179     SortableDateTime: "Y-m-d\\TH:i:s",
1180     UniversalSortableDateTime: "Y-m-d H:i:sO",
1181     YearMonth: "F, Y"
1182 };
1183 </code></pre>
1184  *
1185  * Example usage:
1186  * <pre><code>
1187 var dt = new Date();
1188 document.write(dt.format(Date.patterns.ShortDate));
1189  </code></pre>
1190  */
1191
1192 /*
1193  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1194  * They generate precompiled functions from date formats instead of parsing and
1195  * processing the pattern every time you format a date.  These functions are available
1196  * on every Date object (any javascript function).
1197  *
1198  * The original article and download are here:
1199  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1200  *
1201  */
1202  
1203  
1204  // was in core
1205 /**
1206  Returns the number of milliseconds between this date and date
1207  @param {Date} date (optional) Defaults to now
1208  @param {String} interval (optional) Default Date.MILLI, A valid date interval enum value (eg. Date.DAY) 
1209  @return {Number} The diff in milliseconds or units of interval
1210  @member Date getElapsed
1211  */
1212 Date.prototype.getElapsed = function(date, interval)
1213 {
1214     date = date ||  new Date();
1215     var ret = Math.abs(date.getTime()-this.getTime());
1216     switch (interval) {
1217        
1218         case  Date.SECOND:
1219             return Math.floor(ret / (1000));
1220         case  Date.MINUTE:
1221             return Math.floor(ret / (1000*60));
1222         case  Date.HOUR:
1223             return Math.floor(ret / (1000*60*60));
1224         case  Date.DAY:
1225             return Math.floor(ret / (1000*60*60*24));
1226         case  Date.MONTH: // this does not give exact number...??
1227             return ((date.format("Y") - this.format("Y")) * 12) + (date.format("m") - this.format("m"));
1228         case  Date.YEAR: // this does not give exact number...??
1229             return (date.format("Y") - this.format("Y"));
1230        
1231         case  Date.MILLI:
1232         default:
1233             return ret;
1234     }
1235 };
1236  
1237 // was in date file..
1238
1239
1240 // private
1241 Date.parseFunctions = {count:0};
1242 // private
1243 Date.parseRegexes = [];
1244 // private
1245 Date.formatFunctions = {count:0};
1246
1247 // private
1248 Date.prototype.dateFormat = function(format) {
1249     if (Date.formatFunctions[format] == null) {
1250         Date.createNewFormat(format);
1251     }
1252     var func = Date.formatFunctions[format];
1253     return this[func]();
1254 };
1255
1256
1257 /**
1258  * Formats a date given the supplied format string
1259  * @param {String} format The format string
1260  * @return {String} The formatted date
1261  * @method
1262  */
1263 Date.prototype.format = Date.prototype.dateFormat;
1264
1265 // private
1266 Date.createNewFormat = function(format) {
1267     var funcName = "format" + Date.formatFunctions.count++;
1268     Date.formatFunctions[format] = funcName;
1269     var code = "Date.prototype." + funcName + " = function(){return ";
1270     var special = false;
1271     var ch = '';
1272     for (var i = 0; i < format.length; ++i) {
1273         ch = format.charAt(i);
1274         if (!special && ch == "\\") {
1275             special = true;
1276         }
1277         else if (special) {
1278             special = false;
1279             code += "'" + String.escape(ch) + "' + ";
1280         }
1281         else {
1282             code += Date.getFormatCode(ch);
1283         }
1284     }
1285     /** eval:var:zzzzzzzzzzzzz */
1286     eval(code.substring(0, code.length - 3) + ";}");
1287 };
1288
1289 // private
1290 Date.getFormatCode = function(character) {
1291     switch (character) {
1292     case "d":
1293         return "String.leftPad(this.getDate(), 2, '0') + ";
1294     case "D":
1295         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1296     case "j":
1297         return "this.getDate() + ";
1298     case "l":
1299         return "Date.dayNames[this.getDay()] + ";
1300     case "S":
1301         return "this.getSuffix() + ";
1302     case "w":
1303         return "this.getDay() + ";
1304     case "z":
1305         return "this.getDayOfYear() + ";
1306     case "W":
1307         return "this.getWeekOfYear() + ";
1308     case "F":
1309         return "Date.monthNames[this.getMonth()] + ";
1310     case "m":
1311         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1312     case "M":
1313         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1314     case "n":
1315         return "(this.getMonth() + 1) + ";
1316     case "t":
1317         return "this.getDaysInMonth() + ";
1318     case "L":
1319         return "(this.isLeapYear() ? 1 : 0) + ";
1320     case "Y":
1321         return "this.getFullYear() + ";
1322     case "y":
1323         return "('' + this.getFullYear()).substring(2, 4) + ";
1324     case "a":
1325         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1326     case "A":
1327         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1328     case "g":
1329         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1330     case "G":
1331         return "this.getHours() + ";
1332     case "h":
1333         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1334     case "H":
1335         return "String.leftPad(this.getHours(), 2, '0') + ";
1336     case "i":
1337         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1338     case "s":
1339         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1340     case "O":
1341         return "this.getGMTOffset() + ";
1342     case "P":
1343         return "this.getGMTColonOffset() + ";
1344     case "T":
1345         return "this.getTimezone() + ";
1346     case "Z":
1347         return "(this.getTimezoneOffset() * -60) + ";
1348     default:
1349         return "'" + String.escape(character) + "' + ";
1350     }
1351 };
1352
1353 /**
1354  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1355  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1356  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1357  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1358  * string or the parse operation will fail.
1359  * Example Usage:
1360 <pre><code>
1361 //dt = Fri May 25 2007 (current date)
1362 var dt = new Date();
1363
1364 //dt = Thu May 25 2006 (today's month/day in 2006)
1365 dt = Date.parseDate("2006", "Y");
1366
1367 //dt = Sun Jan 15 2006 (all date parts specified)
1368 dt = Date.parseDate("2006-1-15", "Y-m-d");
1369
1370 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1371 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1372 </code></pre>
1373  * @param {String} input The unparsed date as a string
1374  * @param {String} format The format the date is in
1375  * @return {Date} The parsed date
1376  * @static
1377  */
1378 Date.parseDate = function(input, format) {
1379     if (Date.parseFunctions[format] == null) {
1380         Date.createParser(format);
1381     }
1382     var func = Date.parseFunctions[format];
1383     return Date[func](input);
1384 };
1385 /**
1386  * @private
1387  */
1388
1389 Date.createParser = function(format) {
1390     var funcName = "parse" + Date.parseFunctions.count++;
1391     var regexNum = Date.parseRegexes.length;
1392     var currentGroup = 1;
1393     Date.parseFunctions[format] = funcName;
1394
1395     var code = "Date." + funcName + " = function(input){\n"
1396         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1397         + "var d = new Date();\n"
1398         + "y = d.getFullYear();\n"
1399         + "m = d.getMonth();\n"
1400         + "d = d.getDate();\n"
1401         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1402         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1403         + "if (results && results.length > 0) {";
1404     var regex = "";
1405
1406     var special = false;
1407     var ch = '';
1408     for (var i = 0; i < format.length; ++i) {
1409         ch = format.charAt(i);
1410         if (!special && ch == "\\") {
1411             special = true;
1412         }
1413         else if (special) {
1414             special = false;
1415             regex += String.escape(ch);
1416         }
1417         else {
1418             var obj = Date.formatCodeToRegex(ch, currentGroup);
1419             currentGroup += obj.g;
1420             regex += obj.s;
1421             if (obj.g && obj.c) {
1422                 code += obj.c;
1423             }
1424         }
1425     }
1426
1427     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1428         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1429         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1430         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1431         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1432         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1433         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1434         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1435         + "else if (y >= 0 && m >= 0)\n"
1436         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1437         + "else if (y >= 0)\n"
1438         + "{v = new Date(y); v.setFullYear(y);}\n"
1439         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1440         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1441         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1442         + ";}";
1443
1444     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1445     /** eval:var:zzzzzzzzzzzzz */
1446     eval(code);
1447 };
1448
1449 // private
1450 Date.formatCodeToRegex = function(character, currentGroup) {
1451     switch (character) {
1452     case "D":
1453         return {g:0,
1454         c:null,
1455         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1456     case "j":
1457         return {g:1,
1458             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1459             s:"(\\d{1,2})"}; // day of month without leading zeroes
1460     case "d":
1461         return {g:1,
1462             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1463             s:"(\\d{2})"}; // day of month with leading zeroes
1464     case "l":
1465         return {g:0,
1466             c:null,
1467             s:"(?:" + Date.dayNames.join("|") + ")"};
1468     case "S":
1469         return {g:0,
1470             c:null,
1471             s:"(?:st|nd|rd|th)"};
1472     case "w":
1473         return {g:0,
1474             c:null,
1475             s:"\\d"};
1476     case "z":
1477         return {g:0,
1478             c:null,
1479             s:"(?:\\d{1,3})"};
1480     case "W":
1481         return {g:0,
1482             c:null,
1483             s:"(?:\\d{2})"};
1484     case "F":
1485         return {g:1,
1486             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1487             s:"(" + Date.monthNames.join("|") + ")"};
1488     case "M":
1489         return {g:1,
1490             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1491             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1492     case "n":
1493         return {g:1,
1494             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1495             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1496     case "m":
1497         return {g:1,
1498             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1499             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1500     case "t":
1501         return {g:0,
1502             c:null,
1503             s:"\\d{1,2}"};
1504     case "L":
1505         return {g:0,
1506             c:null,
1507             s:"(?:1|0)"};
1508     case "Y":
1509         return {g:1,
1510             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1511             s:"(\\d{4})"};
1512     case "y":
1513         return {g:1,
1514             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1515                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1516             s:"(\\d{1,2})"};
1517     case "a":
1518         return {g:1,
1519             c:"if (results[" + currentGroup + "] == 'am') {\n"
1520                 + "if (h == 12) { h = 0; }\n"
1521                 + "} else { if (h < 12) { h += 12; }}",
1522             s:"(am|pm)"};
1523     case "A":
1524         return {g:1,
1525             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1526                 + "if (h == 12) { h = 0; }\n"
1527                 + "} else { if (h < 12) { h += 12; }}",
1528             s:"(AM|PM)"};
1529     case "g":
1530     case "G":
1531         return {g:1,
1532             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1533             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1534     case "h":
1535     case "H":
1536         return {g:1,
1537             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1538             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1539     case "i":
1540         return {g:1,
1541             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1542             s:"(\\d{2})"};
1543     case "s":
1544         return {g:1,
1545             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1546             s:"(\\d{2})"};
1547     case "O":
1548         return {g:1,
1549             c:[
1550                 "o = results[", currentGroup, "];\n",
1551                 "var sn = o.substring(0,1);\n", // get + / - sign
1552                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1553                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1554                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1555                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1556             ].join(""),
1557             s:"([+\-]\\d{2,4})"};
1558     
1559     
1560     case "P":
1561         return {g:1,
1562                 c:[
1563                    "o = results[", currentGroup, "];\n",
1564                    "var sn = o.substring(0,1);\n",
1565                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1566                    "var mn = o.substring(4,6) % 60;\n",
1567                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1568                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1569             ].join(""),
1570             s:"([+\-]\\d{4})"};
1571     case "T":
1572         return {g:0,
1573             c:null,
1574             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1575     case "Z":
1576         return {g:1,
1577             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1578                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1579             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1580     default:
1581         return {g:0,
1582             c:null,
1583             s:String.escape(character)};
1584     }
1585 };
1586
1587 /**
1588  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1589  * @return {String} The abbreviated timezone name (e.g. 'CST')
1590  */
1591 Date.prototype.getTimezone = function() {
1592     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1593 };
1594
1595 /**
1596  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1597  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1598  */
1599 Date.prototype.getGMTOffset = function() {
1600     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1601         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1602         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1603 };
1604
1605 /**
1606  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1607  * @return {String} 2-characters representing hours and 2-characters representing minutes
1608  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1609  */
1610 Date.prototype.getGMTColonOffset = function() {
1611         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1612                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1613                 + ":"
1614                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1615 }
1616
1617 /**
1618  * Get the numeric day number of the year, adjusted for leap year.
1619  * @return {Number} 0 through 364 (365 in leap years)
1620  */
1621 Date.prototype.getDayOfYear = function() {
1622     var num = 0;
1623     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1624     for (var i = 0; i < this.getMonth(); ++i) {
1625         num += Date.daysInMonth[i];
1626     }
1627     return num + this.getDate() - 1;
1628 };
1629
1630 /**
1631  * Get the string representation of the numeric week number of the year
1632  * (equivalent to the format specifier 'W').
1633  * @return {String} '00' through '52'
1634  */
1635 Date.prototype.getWeekOfYear = function() {
1636     // Skip to Thursday of this week
1637     var now = this.getDayOfYear() + (4 - this.getDay());
1638     // Find the first Thursday of the year
1639     var jan1 = new Date(this.getFullYear(), 0, 1);
1640     var then = (7 - jan1.getDay() + 4);
1641     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1642 };
1643
1644 /**
1645  * Whether or not the current date is in a leap year.
1646  * @return {Boolean} True if the current date is in a leap year, else false
1647  */
1648 Date.prototype.isLeapYear = function() {
1649     var year = this.getFullYear();
1650     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1651 };
1652
1653 /**
1654  * Get the first day of the current month, adjusted for leap year.  The returned value
1655  * is the numeric day index within the week (0-6) which can be used in conjunction with
1656  * the {@link #monthNames} array to retrieve the textual day name.
1657  * Example:
1658  *<pre><code>
1659 var dt = new Date('1/10/2007');
1660 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1661 </code></pre>
1662  * @return {Number} The day number (0-6)
1663  */
1664 Date.prototype.getFirstDayOfMonth = function() {
1665     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1666     return (day < 0) ? (day + 7) : day;
1667 };
1668
1669 /**
1670  * Get the last day of the current month, adjusted for leap year.  The returned value
1671  * is the numeric day index within the week (0-6) which can be used in conjunction with
1672  * the {@link #monthNames} array to retrieve the textual day name.
1673  * Example:
1674  *<pre><code>
1675 var dt = new Date('1/10/2007');
1676 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1677 </code></pre>
1678  * @return {Number} The day number (0-6)
1679  */
1680 Date.prototype.getLastDayOfMonth = function() {
1681     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1682     return (day < 0) ? (day + 7) : day;
1683 };
1684
1685
1686 /**
1687  * Get the first date of this date's month
1688  * @return {Date}
1689  */
1690 Date.prototype.getFirstDateOfMonth = function() {
1691     return new Date(this.getFullYear(), this.getMonth(), 1);
1692 };
1693
1694 /**
1695  * Get the last date of this date's month
1696  * @return {Date}
1697  */
1698 Date.prototype.getLastDateOfMonth = function() {
1699     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1700 };
1701 /**
1702  * Get the number of days in the current month, adjusted for leap year.
1703  * @return {Number} The number of days in the month
1704  */
1705 Date.prototype.getDaysInMonth = function() {
1706     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1707     return Date.daysInMonth[this.getMonth()];
1708 };
1709
1710 /**
1711  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1712  * @return {String} 'st, 'nd', 'rd' or 'th'
1713  */
1714 Date.prototype.getSuffix = function() {
1715     switch (this.getDate()) {
1716         case 1:
1717         case 21:
1718         case 31:
1719             return "st";
1720         case 2:
1721         case 22:
1722             return "nd";
1723         case 3:
1724         case 23:
1725             return "rd";
1726         default:
1727             return "th";
1728     }
1729 };
1730
1731 // private
1732 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1733
1734 /**
1735  * An array of textual month names.
1736  * Override these values for international dates, for example...
1737  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1738  * @type Array
1739  * @static
1740  */
1741 Date.monthNames =
1742    ["January",
1743     "February",
1744     "March",
1745     "April",
1746     "May",
1747     "June",
1748     "July",
1749     "August",
1750     "September",
1751     "October",
1752     "November",
1753     "December"];
1754
1755 /**
1756  * An array of textual day names.
1757  * Override these values for international dates, for example...
1758  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1759  * @type Array
1760  * @static
1761  */
1762 Date.dayNames =
1763    ["Sunday",
1764     "Monday",
1765     "Tuesday",
1766     "Wednesday",
1767     "Thursday",
1768     "Friday",
1769     "Saturday"];
1770
1771 // private
1772 Date.y2kYear = 50;
1773 // private
1774 Date.monthNumbers = {
1775     Jan:0,
1776     Feb:1,
1777     Mar:2,
1778     Apr:3,
1779     May:4,
1780     Jun:5,
1781     Jul:6,
1782     Aug:7,
1783     Sep:8,
1784     Oct:9,
1785     Nov:10,
1786     Dec:11};
1787
1788 /**
1789  * Creates and returns a new Date instance with the exact same date value as the called instance.
1790  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1791  * variable will also be changed.  When the intention is to create a new variable that will not
1792  * modify the original instance, you should create a clone.
1793  *
1794  * Example of correctly cloning a date:
1795  * <pre><code>
1796 //wrong way:
1797 var orig = new Date('10/1/2006');
1798 var copy = orig;
1799 copy.setDate(5);
1800 document.write(orig);  //returns 'Thu Oct 05 2006'!
1801
1802 //correct way:
1803 var orig = new Date('10/1/2006');
1804 var copy = orig.clone();
1805 copy.setDate(5);
1806 document.write(orig);  //returns 'Thu Oct 01 2006'
1807 </code></pre>
1808  * @return {Date} The new Date instance
1809  */
1810 Date.prototype.clone = function() {
1811         return new Date(this.getTime());
1812 };
1813
1814 /**
1815  * Clears any time information from this date
1816  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1817  @return {Date} this or the clone
1818  */
1819 Date.prototype.clearTime = function(clone){
1820     if(clone){
1821         return this.clone().clearTime();
1822     }
1823     this.setHours(0);
1824     this.setMinutes(0);
1825     this.setSeconds(0);
1826     this.setMilliseconds(0);
1827     return this;
1828 };
1829
1830 // private
1831 // safari setMonth is broken -- check that this is only donw once...
1832 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1833     Date.brokenSetMonth = Date.prototype.setMonth;
1834         Date.prototype.setMonth = function(num){
1835                 if(num <= -1){
1836                         var n = Math.ceil(-num);
1837                         var back_year = Math.ceil(n/12);
1838                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1839                         this.setFullYear(this.getFullYear() - back_year);
1840                         return Date.brokenSetMonth.call(this, month);
1841                 } else {
1842                         return Date.brokenSetMonth.apply(this, arguments);
1843                 }
1844         };
1845 }
1846
1847 /** Date interval constant 
1848 * @static 
1849 * @type String */
1850 Date.MILLI = "ms";
1851 /** Date interval constant 
1852 * @static 
1853 * @type String */
1854 Date.SECOND = "s";
1855 /** Date interval constant 
1856 * @static 
1857 * @type String */
1858 Date.MINUTE = "mi";
1859 /** Date interval constant 
1860 * @static 
1861 * @type String */
1862 Date.HOUR = "h";
1863 /** Date interval constant 
1864 * @static 
1865 * @type String */
1866 Date.DAY = "d";
1867 /** Date interval constant 
1868 * @static 
1869 * @type String */
1870 Date.MONTH = "mo";
1871 /** Date interval constant 
1872 * @static 
1873 * @type String */
1874 Date.YEAR = "y";
1875
1876 /**
1877  * Provides a convenient method of performing basic date arithmetic.  This method
1878  * does not modify the Date instance being called - it creates and returns
1879  * a new Date instance containing the resulting date value.
1880  *
1881  * Examples:
1882  * <pre><code>
1883 //Basic usage:
1884 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1885 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1886
1887 //Negative values will subtract correctly:
1888 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1889 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1890
1891 //You can even chain several calls together in one line!
1892 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1893 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1894  </code></pre>
1895  *
1896  * @param {String} interval   A valid date interval enum value
1897  * @param {Number} value      The amount to add to the current date
1898  * @return {Date} The new Date instance
1899  */
1900 Date.prototype.add = function(interval, value){
1901   var d = this.clone();
1902   if (!interval || value === 0) { return d; }
1903   switch(interval.toLowerCase()){
1904     case Date.MILLI:
1905       d.setMilliseconds(this.getMilliseconds() + value);
1906       break;
1907     case Date.SECOND:
1908       d.setSeconds(this.getSeconds() + value);
1909       break;
1910     case Date.MINUTE:
1911       d.setMinutes(this.getMinutes() + value);
1912       break;
1913     case Date.HOUR:
1914       d.setHours(this.getHours() + value);
1915       break;
1916     case Date.DAY:
1917       d.setDate(this.getDate() + value);
1918       break;
1919     case Date.MONTH:
1920       var day = this.getDate();
1921       if(day > 28){
1922           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1923       }
1924       d.setDate(day);
1925       d.setMonth(this.getMonth() + value);
1926       break;
1927     case Date.YEAR:
1928       d.setFullYear(this.getFullYear() + value);
1929       break;
1930   }
1931   return d;
1932 };
1933 /**
1934  * @class Roo.lib.Dom
1935  * @licence LGPL
1936  * @static
1937  * 
1938  * Dom utils (from YIU afaik)
1939  *
1940  * 
1941  **/
1942 Roo.lib.Dom = {
1943     /**
1944      * Get the view width
1945      * @param {Boolean} full True will get the full document, otherwise it's the view width
1946      * @return {Number} The width
1947      */
1948      
1949     getViewWidth : function(full) {
1950         return full ? this.getDocumentWidth() : this.getViewportWidth();
1951     },
1952     /**
1953      * Get the view height
1954      * @param {Boolean} full True will get the full document, otherwise it's the view height
1955      * @return {Number} The height
1956      */
1957     getViewHeight : function(full) {
1958         return full ? this.getDocumentHeight() : this.getViewportHeight();
1959     },
1960     /**
1961      * Get the Full Document height 
1962      * @return {Number} The height
1963      */
1964     getDocumentHeight: function() {
1965         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1966         return Math.max(scrollHeight, this.getViewportHeight());
1967     },
1968     /**
1969      * Get the Full Document width
1970      * @return {Number} The width
1971      */
1972     getDocumentWidth: function() {
1973         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1974         return Math.max(scrollWidth, this.getViewportWidth());
1975     },
1976     /**
1977      * Get the Window Viewport height
1978      * @return {Number} The height
1979      */
1980     getViewportHeight: function() {
1981         var height = self.innerHeight;
1982         var mode = document.compatMode;
1983
1984         if ((mode || Roo.isIE) && !Roo.isOpera) {
1985             height = (mode == "CSS1Compat") ?
1986                      document.documentElement.clientHeight :
1987                      document.body.clientHeight;
1988         }
1989
1990         return height;
1991     },
1992     /**
1993      * Get the Window Viewport width
1994      * @return {Number} The width
1995      */
1996     getViewportWidth: function() {
1997         var width = self.innerWidth;
1998         var mode = document.compatMode;
1999
2000         if (mode || Roo.isIE) {
2001             width = (mode == "CSS1Compat") ?
2002                     document.documentElement.clientWidth :
2003                     document.body.clientWidth;
2004         }
2005         return width;
2006     },
2007
2008     isAncestor : function(p, c) {
2009         p = Roo.getDom(p);
2010         c = Roo.getDom(c);
2011         if (!p || !c) {
2012             return false;
2013         }
2014
2015         if (p.contains && !Roo.isSafari) {
2016             return p.contains(c);
2017         } else if (p.compareDocumentPosition) {
2018             return !!(p.compareDocumentPosition(c) & 16);
2019         } else {
2020             var parent = c.parentNode;
2021             while (parent) {
2022                 if (parent == p) {
2023                     return true;
2024                 }
2025                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2026                     return false;
2027                 }
2028                 parent = parent.parentNode;
2029             }
2030             return false;
2031         }
2032     },
2033
2034     getRegion : function(el) {
2035         return Roo.lib.Region.getRegion(el);
2036     },
2037
2038     getY : function(el) {
2039         return this.getXY(el)[1];
2040     },
2041
2042     getX : function(el) {
2043         return this.getXY(el)[0];
2044     },
2045
2046     getXY : function(el) {
2047         var p, pe, b, scroll, bd = document.body;
2048         el = Roo.getDom(el);
2049         var fly = Roo.lib.AnimBase.fly;
2050         if (el.getBoundingClientRect) {
2051             b = el.getBoundingClientRect();
2052             scroll = fly(document).getScroll();
2053             return [b.left + scroll.left, b.top + scroll.top];
2054         }
2055         var x = 0, y = 0;
2056
2057         p = el;
2058
2059         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2060
2061         while (p) {
2062
2063             x += p.offsetLeft;
2064             y += p.offsetTop;
2065
2066             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2067                 hasAbsolute = true;
2068             }
2069
2070             if (Roo.isGecko) {
2071                 pe = fly(p);
2072
2073                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2074                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2075
2076
2077                 x += bl;
2078                 y += bt;
2079
2080
2081                 if (p != el && pe.getStyle('overflow') != 'visible') {
2082                     x += bl;
2083                     y += bt;
2084                 }
2085             }
2086             p = p.offsetParent;
2087         }
2088
2089         if (Roo.isSafari && hasAbsolute) {
2090             x -= bd.offsetLeft;
2091             y -= bd.offsetTop;
2092         }
2093
2094         if (Roo.isGecko && !hasAbsolute) {
2095             var dbd = fly(bd);
2096             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2097             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2098         }
2099
2100         p = el.parentNode;
2101         while (p && p != bd) {
2102             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2103                 x -= p.scrollLeft;
2104                 y -= p.scrollTop;
2105             }
2106             p = p.parentNode;
2107         }
2108         return [x, y];
2109     },
2110  
2111   
2112
2113
2114     setXY : function(el, xy) {
2115         el = Roo.fly(el, '_setXY');
2116         el.position();
2117         var pts = el.translatePoints(xy);
2118         if (xy[0] !== false) {
2119             el.dom.style.left = pts.left + "px";
2120         }
2121         if (xy[1] !== false) {
2122             el.dom.style.top = pts.top + "px";
2123         }
2124     },
2125
2126     setX : function(el, x) {
2127         this.setXY(el, [x, false]);
2128     },
2129
2130     setY : function(el, y) {
2131         this.setXY(el, [false, y]);
2132     }
2133 };
2134 /*
2135  * Portions of this file are based on pieces of Yahoo User Interface Library
2136  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2137  * YUI licensed under the BSD License:
2138  * http://developer.yahoo.net/yui/license.txt
2139  * <script type="text/javascript">
2140  *
2141  */
2142
2143 Roo.lib.Event = function() {
2144     var loadComplete = false;
2145     var listeners = [];
2146     var unloadListeners = [];
2147     var retryCount = 0;
2148     var onAvailStack = [];
2149     var counter = 0;
2150     var lastError = null;
2151
2152     return {
2153         POLL_RETRYS: 200,
2154         POLL_INTERVAL: 20,
2155         EL: 0,
2156         TYPE: 1,
2157         FN: 2,
2158         WFN: 3,
2159         OBJ: 3,
2160         ADJ_SCOPE: 4,
2161         _interval: null,
2162
2163         startInterval: function() {
2164             if (!this._interval) {
2165                 var self = this;
2166                 var callback = function() {
2167                     self._tryPreloadAttach();
2168                 };
2169                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2170
2171             }
2172         },
2173
2174         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2175             onAvailStack.push({ id:         p_id,
2176                 fn:         p_fn,
2177                 obj:        p_obj,
2178                 override:   p_override,
2179                 checkReady: false    });
2180
2181             retryCount = this.POLL_RETRYS;
2182             this.startInterval();
2183         },
2184
2185
2186         addListener: function(el, eventName, fn) {
2187             el = Roo.getDom(el);
2188             if (!el || !fn) {
2189                 return false;
2190             }
2191
2192             if ("unload" == eventName) {
2193                 unloadListeners[unloadListeners.length] =
2194                 [el, eventName, fn];
2195                 return true;
2196             }
2197
2198             var wrappedFn = function(e) {
2199                 return fn(Roo.lib.Event.getEvent(e));
2200             };
2201
2202             var li = [el, eventName, fn, wrappedFn];
2203
2204             var index = listeners.length;
2205             listeners[index] = li;
2206
2207             this.doAdd(el, eventName, wrappedFn, false);
2208             return true;
2209
2210         },
2211
2212
2213         removeListener: function(el, eventName, fn) {
2214             var i, len;
2215
2216             el = Roo.getDom(el);
2217
2218             if(!fn) {
2219                 return this.purgeElement(el, false, eventName);
2220             }
2221
2222
2223             if ("unload" == eventName) {
2224
2225                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2226                     var li = unloadListeners[i];
2227                     if (li &&
2228                         li[0] == el &&
2229                         li[1] == eventName &&
2230                         li[2] == fn) {
2231                         unloadListeners.splice(i, 1);
2232                         return true;
2233                     }
2234                 }
2235
2236                 return false;
2237             }
2238
2239             var cacheItem = null;
2240
2241
2242             var index = arguments[3];
2243
2244             if ("undefined" == typeof index) {
2245                 index = this._getCacheIndex(el, eventName, fn);
2246             }
2247
2248             if (index >= 0) {
2249                 cacheItem = listeners[index];
2250             }
2251
2252             if (!el || !cacheItem) {
2253                 return false;
2254             }
2255
2256             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2257
2258             delete listeners[index][this.WFN];
2259             delete listeners[index][this.FN];
2260             listeners.splice(index, 1);
2261
2262             return true;
2263
2264         },
2265
2266
2267         getTarget: function(ev, resolveTextNode) {
2268             ev = ev.browserEvent || ev;
2269             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2270             var t = ev.target || ev.srcElement;
2271             return this.resolveTextNode(t);
2272         },
2273
2274
2275         resolveTextNode: function(node) {
2276             if (Roo.isSafari && node && 3 == node.nodeType) {
2277                 return node.parentNode;
2278             } else {
2279                 return node;
2280             }
2281         },
2282
2283
2284         getPageX: function(ev) {
2285             ev = ev.browserEvent || ev;
2286             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2287             var x = ev.pageX;
2288             if (!x && 0 !== x) {
2289                 x = ev.clientX || 0;
2290
2291                 if (Roo.isIE) {
2292                     x += this.getScroll()[1];
2293                 }
2294             }
2295
2296             return x;
2297         },
2298
2299
2300         getPageY: function(ev) {
2301             ev = ev.browserEvent || ev;
2302             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2303             var y = ev.pageY;
2304             if (!y && 0 !== y) {
2305                 y = ev.clientY || 0;
2306
2307                 if (Roo.isIE) {
2308                     y += this.getScroll()[0];
2309                 }
2310             }
2311
2312
2313             return y;
2314         },
2315
2316
2317         getXY: function(ev) {
2318             ev = ev.browserEvent || ev;
2319             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2320             return [this.getPageX(ev), this.getPageY(ev)];
2321         },
2322
2323
2324         getRelatedTarget: function(ev) {
2325             ev = ev.browserEvent || ev;
2326             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2327             var t = ev.relatedTarget;
2328             if (!t) {
2329                 if (ev.type == "mouseout") {
2330                     t = ev.toElement;
2331                 } else if (ev.type == "mouseover") {
2332                     t = ev.fromElement;
2333                 }
2334             }
2335
2336             return this.resolveTextNode(t);
2337         },
2338
2339
2340         getTime: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2343             if (!ev.time) {
2344                 var t = new Date().getTime();
2345                 try {
2346                     ev.time = t;
2347                 } catch(ex) {
2348                     this.lastError = ex;
2349                     return t;
2350                 }
2351             }
2352
2353             return ev.time;
2354         },
2355
2356
2357         stopEvent: function(ev) {
2358             this.stopPropagation(ev);
2359             this.preventDefault(ev);
2360         },
2361
2362
2363         stopPropagation: function(ev) {
2364             ev = ev.browserEvent || ev;
2365             if (ev.stopPropagation) {
2366                 ev.stopPropagation();
2367             } else {
2368                 ev.cancelBubble = true;
2369             }
2370         },
2371
2372
2373         preventDefault: function(ev) {
2374             ev = ev.browserEvent || ev;
2375             if(ev.preventDefault) {
2376                 ev.preventDefault();
2377             } else {
2378                 ev.returnValue = false;
2379             }
2380         },
2381
2382
2383         getEvent: function(e) {
2384             var ev = e || window.event;
2385             if (!ev) {
2386                 var c = this.getEvent.caller;
2387                 while (c) {
2388                     ev = c.arguments[0];
2389                     if (ev && Event == ev.constructor) {
2390                         break;
2391                     }
2392                     c = c.caller;
2393                 }
2394             }
2395             return ev;
2396         },
2397
2398
2399         getCharCode: function(ev) {
2400             ev = ev.browserEvent || ev;
2401             return ev.charCode || ev.keyCode || 0;
2402         },
2403
2404
2405         _getCacheIndex: function(el, eventName, fn) {
2406             for (var i = 0,len = listeners.length; i < len; ++i) {
2407                 var li = listeners[i];
2408                 if (li &&
2409                     li[this.FN] == fn &&
2410                     li[this.EL] == el &&
2411                     li[this.TYPE] == eventName) {
2412                     return i;
2413                 }
2414             }
2415
2416             return -1;
2417         },
2418
2419
2420         elCache: {},
2421
2422
2423         getEl: function(id) {
2424             return document.getElementById(id);
2425         },
2426
2427
2428         clearCache: function() {
2429         },
2430
2431
2432         _load: function(e) {
2433             loadComplete = true;
2434             var EU = Roo.lib.Event;
2435
2436
2437             if (Roo.isIE) {
2438                 EU.doRemove(window, "load", EU._load);
2439             }
2440         },
2441
2442
2443         _tryPreloadAttach: function() {
2444
2445             if (this.locked) {
2446                 return false;
2447             }
2448
2449             this.locked = true;
2450
2451
2452             var tryAgain = !loadComplete;
2453             if (!tryAgain) {
2454                 tryAgain = (retryCount > 0);
2455             }
2456
2457
2458             var notAvail = [];
2459             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2460                 var item = onAvailStack[i];
2461                 if (item) {
2462                     var el = this.getEl(item.id);
2463
2464                     if (el) {
2465                         if (!item.checkReady ||
2466                             loadComplete ||
2467                             el.nextSibling ||
2468                             (document && document.body)) {
2469
2470                             var scope = el;
2471                             if (item.override) {
2472                                 if (item.override === true) {
2473                                     scope = item.obj;
2474                                 } else {
2475                                     scope = item.override;
2476                                 }
2477                             }
2478                             item.fn.call(scope, item.obj);
2479                             onAvailStack[i] = null;
2480                         }
2481                     } else {
2482                         notAvail.push(item);
2483                     }
2484                 }
2485             }
2486
2487             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2488
2489             if (tryAgain) {
2490
2491                 this.startInterval();
2492             } else {
2493                 clearInterval(this._interval);
2494                 this._interval = null;
2495             }
2496
2497             this.locked = false;
2498
2499             return true;
2500
2501         },
2502
2503
2504         purgeElement: function(el, recurse, eventName) {
2505             var elListeners = this.getListeners(el, eventName);
2506             if (elListeners) {
2507                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2508                     var l = elListeners[i];
2509                     this.removeListener(el, l.type, l.fn);
2510                 }
2511             }
2512
2513             if (recurse && el && el.childNodes) {
2514                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2515                     this.purgeElement(el.childNodes[i], recurse, eventName);
2516                 }
2517             }
2518         },
2519
2520
2521         getListeners: function(el, eventName) {
2522             var results = [], searchLists;
2523             if (!eventName) {
2524                 searchLists = [listeners, unloadListeners];
2525             } else if (eventName == "unload") {
2526                 searchLists = [unloadListeners];
2527             } else {
2528                 searchLists = [listeners];
2529             }
2530
2531             for (var j = 0; j < searchLists.length; ++j) {
2532                 var searchList = searchLists[j];
2533                 if (searchList && searchList.length > 0) {
2534                     for (var i = 0,len = searchList.length; i < len; ++i) {
2535                         var l = searchList[i];
2536                         if (l && l[this.EL] === el &&
2537                             (!eventName || eventName === l[this.TYPE])) {
2538                             results.push({
2539                                 type:   l[this.TYPE],
2540                                 fn:     l[this.FN],
2541                                 obj:    l[this.OBJ],
2542                                 adjust: l[this.ADJ_SCOPE],
2543                                 index:  i
2544                             });
2545                         }
2546                     }
2547                 }
2548             }
2549
2550             return (results.length) ? results : null;
2551         },
2552
2553
2554         _unload: function(e) {
2555
2556             var EU = Roo.lib.Event, i, j, l, len, index;
2557
2558             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2559                 l = unloadListeners[i];
2560                 if (l) {
2561                     var scope = window;
2562                     if (l[EU.ADJ_SCOPE]) {
2563                         if (l[EU.ADJ_SCOPE] === true) {
2564                             scope = l[EU.OBJ];
2565                         } else {
2566                             scope = l[EU.ADJ_SCOPE];
2567                         }
2568                     }
2569                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2570                     unloadListeners[i] = null;
2571                     l = null;
2572                     scope = null;
2573                 }
2574             }
2575
2576             unloadListeners = null;
2577
2578             if (listeners && listeners.length > 0) {
2579                 j = listeners.length;
2580                 while (j) {
2581                     index = j - 1;
2582                     l = listeners[index];
2583                     if (l) {
2584                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2585                                 l[EU.FN], index);
2586                     }
2587                     j = j - 1;
2588                 }
2589                 l = null;
2590
2591                 EU.clearCache();
2592             }
2593
2594             EU.doRemove(window, "unload", EU._unload);
2595
2596         },
2597
2598
2599         getScroll: function() {
2600             var dd = document.documentElement, db = document.body;
2601             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2602                 return [dd.scrollTop, dd.scrollLeft];
2603             } else if (db) {
2604                 return [db.scrollTop, db.scrollLeft];
2605             } else {
2606                 return [0, 0];
2607             }
2608         },
2609
2610
2611         doAdd: function () {
2612             if (window.addEventListener) {
2613                 return function(el, eventName, fn, capture) {
2614                     el.addEventListener(eventName, fn, (capture));
2615                 };
2616             } else if (window.attachEvent) {
2617                 return function(el, eventName, fn, capture) {
2618                     el.attachEvent("on" + eventName, fn);
2619                 };
2620             } else {
2621                 return function() {
2622                 };
2623             }
2624         }(),
2625
2626
2627         doRemove: function() {
2628             if (window.removeEventListener) {
2629                 return function (el, eventName, fn, capture) {
2630                     el.removeEventListener(eventName, fn, (capture));
2631                 };
2632             } else if (window.detachEvent) {
2633                 return function (el, eventName, fn) {
2634                     el.detachEvent("on" + eventName, fn);
2635                 };
2636             } else {
2637                 return function() {
2638                 };
2639             }
2640         }()
2641     };
2642     
2643 }();
2644 (function() {     
2645    
2646     var E = Roo.lib.Event;
2647     E.on = E.addListener;
2648     E.un = E.removeListener;
2649
2650     if (document && document.body) {
2651         E._load();
2652     } else {
2653         E.doAdd(window, "load", E._load);
2654     }
2655     E.doAdd(window, "unload", E._unload);
2656     E._tryPreloadAttach();
2657 })();
2658
2659  
2660
2661 (function() {
2662     /**
2663      * @class Roo.lib.Ajax
2664      *
2665      * provide a simple Ajax request utility functions
2666      * 
2667      * Portions of this file are based on pieces of Yahoo User Interface Library
2668     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2669     * YUI licensed under the BSD License:
2670     * http://developer.yahoo.net/yui/license.txt
2671     * <script type="text/javascript">
2672     *
2673      *
2674      */
2675     Roo.lib.Ajax = {
2676         /**
2677          * @static 
2678          */
2679         request : function(method, uri, cb, data, options) {
2680             if(options){
2681                 var hs = options.headers;
2682                 if(hs){
2683                     for(var h in hs){
2684                         if(hs.hasOwnProperty(h)){
2685                             this.initHeader(h, hs[h], false);
2686                         }
2687                     }
2688                 }
2689                 if(options.xmlData){
2690                     this.initHeader('Content-Type', 'text/xml', false);
2691                     method = 'POST';
2692                     data = options.xmlData;
2693                 }
2694             }
2695
2696             return this.asyncRequest(method, uri, cb, data);
2697         },
2698         /**
2699          * serialize a form
2700          *
2701          * @static
2702          * @param {DomForm} form element
2703          * @return {String} urlencode form output.
2704          */
2705         serializeForm : function(form) {
2706             if(typeof form == 'string') {
2707                 form = (document.getElementById(form) || document.forms[form]);
2708             }
2709
2710             var el, name, val, disabled, data = '', hasSubmit = false;
2711             for (var i = 0; i < form.elements.length; i++) {
2712                 el = form.elements[i];
2713                 disabled = form.elements[i].disabled;
2714                 name = form.elements[i].name;
2715                 val = form.elements[i].value;
2716
2717                 if (!disabled && name){
2718                     switch (el.type)
2719                             {
2720                         case 'select-one':
2721                         case 'select-multiple':
2722                             for (var j = 0; j < el.options.length; j++) {
2723                                 if (el.options[j].selected) {
2724                                     if (Roo.isIE) {
2725                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2726                                     }
2727                                     else {
2728                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2729                                     }
2730                                 }
2731                             }
2732                             break;
2733                         case 'radio':
2734                         case 'checkbox':
2735                             if (el.checked) {
2736                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2737                             }
2738                             break;
2739                         case 'file':
2740
2741                         case undefined:
2742
2743                         case 'reset':
2744
2745                         case 'button':
2746
2747                             break;
2748                         case 'submit':
2749                             if(hasSubmit == false) {
2750                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2751                                 hasSubmit = true;
2752                             }
2753                             break;
2754                         default:
2755                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2756                             break;
2757                     }
2758                 }
2759             }
2760             data = data.substr(0, data.length - 1);
2761             return data;
2762         },
2763
2764         headers:{},
2765
2766         hasHeaders:false,
2767
2768         useDefaultHeader:true,
2769
2770         defaultPostHeader:'application/x-www-form-urlencoded',
2771
2772         useDefaultXhrHeader:true,
2773
2774         defaultXhrHeader:'XMLHttpRequest',
2775
2776         hasDefaultHeaders:true,
2777
2778         defaultHeaders:{},
2779
2780         poll:{},
2781
2782         timeout:{},
2783
2784         pollInterval:50,
2785
2786         transactionId:0,
2787
2788         setProgId:function(id)
2789         {
2790             this.activeX.unshift(id);
2791         },
2792
2793         setDefaultPostHeader:function(b)
2794         {
2795             this.useDefaultHeader = b;
2796         },
2797
2798         setDefaultXhrHeader:function(b)
2799         {
2800             this.useDefaultXhrHeader = b;
2801         },
2802
2803         setPollingInterval:function(i)
2804         {
2805             if (typeof i == 'number' && isFinite(i)) {
2806                 this.pollInterval = i;
2807             }
2808         },
2809
2810         createXhrObject:function(transactionId)
2811         {
2812             var obj,http;
2813             try
2814             {
2815
2816                 http = new XMLHttpRequest();
2817
2818                 obj = { conn:http, tId:transactionId };
2819             }
2820             catch(e)
2821             {
2822                 for (var i = 0; i < this.activeX.length; ++i) {
2823                     try
2824                     {
2825
2826                         http = new ActiveXObject(this.activeX[i]);
2827
2828                         obj = { conn:http, tId:transactionId };
2829                         break;
2830                     }
2831                     catch(e) {
2832                     }
2833                 }
2834             }
2835             finally
2836             {
2837                 return obj;
2838             }
2839         },
2840
2841         getConnectionObject:function()
2842         {
2843             var o;
2844             var tId = this.transactionId;
2845
2846             try
2847             {
2848                 o = this.createXhrObject(tId);
2849                 if (o) {
2850                     this.transactionId++;
2851                 }
2852             }
2853             catch(e) {
2854             }
2855             finally
2856             {
2857                 return o;
2858             }
2859         },
2860
2861         asyncRequest:function(method, uri, callback, postData)
2862         {
2863             var o = this.getConnectionObject();
2864
2865             if (!o) {
2866                 return null;
2867             }
2868             else {
2869                 o.conn.open(method, uri, true);
2870
2871                 if (this.useDefaultXhrHeader) {
2872                     if (!this.defaultHeaders['X-Requested-With']) {
2873                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2874                     }
2875                 }
2876
2877                 if(postData && this.useDefaultHeader){
2878                     this.initHeader('Content-Type', this.defaultPostHeader);
2879                 }
2880
2881                  if (this.hasDefaultHeaders || this.hasHeaders) {
2882                     this.setHeader(o);
2883                 }
2884
2885                 this.handleReadyState(o, callback);
2886                 o.conn.send(postData || null);
2887
2888                 return o;
2889             }
2890         },
2891
2892         handleReadyState:function(o, callback)
2893         {
2894             var oConn = this;
2895
2896             if (callback && callback.timeout) {
2897                 
2898                 this.timeout[o.tId] = window.setTimeout(function() {
2899                     oConn.abort(o, callback, true);
2900                 }, callback.timeout);
2901             }
2902
2903             this.poll[o.tId] = window.setInterval(
2904                     function() {
2905                         if (o.conn && o.conn.readyState == 4) {
2906                             window.clearInterval(oConn.poll[o.tId]);
2907                             delete oConn.poll[o.tId];
2908
2909                             if(callback && callback.timeout) {
2910                                 window.clearTimeout(oConn.timeout[o.tId]);
2911                                 delete oConn.timeout[o.tId];
2912                             }
2913
2914                             oConn.handleTransactionResponse(o, callback);
2915                         }
2916                     }
2917                     , this.pollInterval);
2918         },
2919
2920         handleTransactionResponse:function(o, callback, isAbort)
2921         {
2922
2923             if (!callback) {
2924                 this.releaseObject(o);
2925                 return;
2926             }
2927
2928             var httpStatus, responseObject;
2929
2930             try
2931             {
2932                 if (o.conn.status !== undefined && o.conn.status != 0) {
2933                     httpStatus = o.conn.status;
2934                 }
2935                 else {
2936                     httpStatus = 13030;
2937                 }
2938             }
2939             catch(e) {
2940
2941
2942                 httpStatus = 13030;
2943             }
2944
2945             if (httpStatus >= 200 && httpStatus < 300) {
2946                 responseObject = this.createResponseObject(o, callback.argument);
2947                 if (callback.success) {
2948                     if (!callback.scope) {
2949                         callback.success(responseObject);
2950                     }
2951                     else {
2952
2953
2954                         callback.success.apply(callback.scope, [responseObject]);
2955                     }
2956                 }
2957             }
2958             else {
2959                 switch (httpStatus) {
2960
2961                     case 12002:
2962                     case 12029:
2963                     case 12030:
2964                     case 12031:
2965                     case 12152:
2966                     case 13030:
2967                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2968                         if (callback.failure) {
2969                             if (!callback.scope) {
2970                                 callback.failure(responseObject);
2971                             }
2972                             else {
2973                                 callback.failure.apply(callback.scope, [responseObject]);
2974                             }
2975                         }
2976                         break;
2977                     default:
2978                         responseObject = this.createResponseObject(o, callback.argument);
2979                         if (callback.failure) {
2980                             if (!callback.scope) {
2981                                 callback.failure(responseObject);
2982                             }
2983                             else {
2984                                 callback.failure.apply(callback.scope, [responseObject]);
2985                             }
2986                         }
2987                 }
2988             }
2989
2990             this.releaseObject(o);
2991             responseObject = null;
2992         },
2993
2994         createResponseObject:function(o, callbackArg)
2995         {
2996             var obj = {};
2997             var headerObj = {};
2998
2999             try
3000             {
3001                 var headerStr = o.conn.getAllResponseHeaders();
3002                 var header = headerStr.split('\n');
3003                 for (var i = 0; i < header.length; i++) {
3004                     var delimitPos = header[i].indexOf(':');
3005                     if (delimitPos != -1) {
3006                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
3007                     }
3008                 }
3009             }
3010             catch(e) {
3011             }
3012
3013             obj.tId = o.tId;
3014             obj.status = o.conn.status;
3015             obj.statusText = o.conn.statusText;
3016             obj.getResponseHeader = headerObj;
3017             obj.getAllResponseHeaders = headerStr;
3018             obj.responseText = o.conn.responseText;
3019             obj.responseXML = o.conn.responseXML;
3020
3021             if (typeof callbackArg !== undefined) {
3022                 obj.argument = callbackArg;
3023             }
3024
3025             return obj;
3026         },
3027
3028         createExceptionObject:function(tId, callbackArg, isAbort)
3029         {
3030             var COMM_CODE = 0;
3031             var COMM_ERROR = 'communication failure';
3032             var ABORT_CODE = -1;
3033             var ABORT_ERROR = 'transaction aborted';
3034
3035             var obj = {};
3036
3037             obj.tId = tId;
3038             if (isAbort) {
3039                 obj.status = ABORT_CODE;
3040                 obj.statusText = ABORT_ERROR;
3041             }
3042             else {
3043                 obj.status = COMM_CODE;
3044                 obj.statusText = COMM_ERROR;
3045             }
3046
3047             if (callbackArg) {
3048                 obj.argument = callbackArg;
3049             }
3050
3051             return obj;
3052         },
3053
3054         initHeader:function(label, value, isDefault)
3055         {
3056             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3057
3058             if (headerObj[label] === undefined) {
3059                 headerObj[label] = value;
3060             }
3061             else {
3062
3063
3064                 headerObj[label] = value + "," + headerObj[label];
3065             }
3066
3067             if (isDefault) {
3068                 this.hasDefaultHeaders = true;
3069             }
3070             else {
3071                 this.hasHeaders = true;
3072             }
3073         },
3074
3075
3076         setHeader:function(o)
3077         {
3078             if (this.hasDefaultHeaders) {
3079                 for (var prop in this.defaultHeaders) {
3080                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3081                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3082                     }
3083                 }
3084             }
3085
3086             if (this.hasHeaders) {
3087                 for (var prop in this.headers) {
3088                     if (this.headers.hasOwnProperty(prop)) {
3089                         o.conn.setRequestHeader(prop, this.headers[prop]);
3090                     }
3091                 }
3092                 this.headers = {};
3093                 this.hasHeaders = false;
3094             }
3095         },
3096
3097         resetDefaultHeaders:function() {
3098             delete this.defaultHeaders;
3099             this.defaultHeaders = {};
3100             this.hasDefaultHeaders = false;
3101         },
3102
3103         abort:function(o, callback, isTimeout)
3104         {
3105             if(this.isCallInProgress(o)) {
3106                 o.conn.abort();
3107                 window.clearInterval(this.poll[o.tId]);
3108                 delete this.poll[o.tId];
3109                 if (isTimeout) {
3110                     delete this.timeout[o.tId];
3111                 }
3112
3113                 this.handleTransactionResponse(o, callback, true);
3114
3115                 return true;
3116             }
3117             else {
3118                 return false;
3119             }
3120         },
3121
3122
3123         isCallInProgress:function(o)
3124         {
3125             if (o && o.conn) {
3126                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3127             }
3128             else {
3129
3130                 return false;
3131             }
3132         },
3133
3134
3135         releaseObject:function(o)
3136         {
3137
3138             o.conn = null;
3139
3140             o = null;
3141         },
3142
3143         activeX:[
3144         'MSXML2.XMLHTTP.3.0',
3145         'MSXML2.XMLHTTP',
3146         'Microsoft.XMLHTTP'
3147         ]
3148
3149
3150     };
3151 })();/*
3152  * Portions of this file are based on pieces of Yahoo User Interface Library
3153  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3154  * YUI licensed under the BSD License:
3155  * http://developer.yahoo.net/yui/license.txt
3156  * <script type="text/javascript">
3157  *
3158  */
3159
3160 Roo.lib.Region = function(t, r, b, l) {
3161     this.top = t;
3162     this[1] = t;
3163     this.right = r;
3164     this.bottom = b;
3165     this.left = l;
3166     this[0] = l;
3167 };
3168
3169
3170 Roo.lib.Region.prototype = {
3171     contains : function(region) {
3172         return ( region.left >= this.left &&
3173                  region.right <= this.right &&
3174                  region.top >= this.top &&
3175                  region.bottom <= this.bottom    );
3176
3177     },
3178
3179     getArea : function() {
3180         return ( (this.bottom - this.top) * (this.right - this.left) );
3181     },
3182
3183     intersect : function(region) {
3184         var t = Math.max(this.top, region.top);
3185         var r = Math.min(this.right, region.right);
3186         var b = Math.min(this.bottom, region.bottom);
3187         var l = Math.max(this.left, region.left);
3188
3189         if (b >= t && r >= l) {
3190             return new Roo.lib.Region(t, r, b, l);
3191         } else {
3192             return null;
3193         }
3194     },
3195     union : function(region) {
3196         var t = Math.min(this.top, region.top);
3197         var r = Math.max(this.right, region.right);
3198         var b = Math.max(this.bottom, region.bottom);
3199         var l = Math.min(this.left, region.left);
3200
3201         return new Roo.lib.Region(t, r, b, l);
3202     },
3203
3204     adjust : function(t, l, b, r) {
3205         this.top += t;
3206         this.left += l;
3207         this.right += r;
3208         this.bottom += b;
3209         return this;
3210     }
3211 };
3212
3213 Roo.lib.Region.getRegion = function(el) {
3214     var p = Roo.lib.Dom.getXY(el);
3215
3216     var t = p[1];
3217     var r = p[0] + el.offsetWidth;
3218     var b = p[1] + el.offsetHeight;
3219     var l = p[0];
3220
3221     return new Roo.lib.Region(t, r, b, l);
3222 };
3223 /*
3224  * Portions of this file are based on pieces of Yahoo User Interface Library
3225  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3226  * YUI licensed under the BSD License:
3227  * http://developer.yahoo.net/yui/license.txt
3228  * <script type="text/javascript">
3229  *
3230  */
3231 //@@dep Roo.lib.Region
3232
3233
3234 Roo.lib.Point = function(x, y) {
3235     if (x instanceof Array) {
3236         y = x[1];
3237         x = x[0];
3238     }
3239     this.x = this.right = this.left = this[0] = x;
3240     this.y = this.top = this.bottom = this[1] = y;
3241 };
3242
3243 Roo.lib.Point.prototype = new Roo.lib.Region();
3244 /*
3245  * Portions of this file are based on pieces of Yahoo User Interface Library
3246  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3247  * YUI licensed under the BSD License:
3248  * http://developer.yahoo.net/yui/license.txt
3249  * <script type="text/javascript">
3250  *
3251  */
3252  
3253 (function() {   
3254
3255     Roo.lib.Anim = {
3256         scroll : function(el, args, duration, easing, cb, scope) {
3257             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3258         },
3259
3260         motion : function(el, args, duration, easing, cb, scope) {
3261             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3262         },
3263
3264         color : function(el, args, duration, easing, cb, scope) {
3265             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3266         },
3267
3268         run : function(el, args, duration, easing, cb, scope, type) {
3269             type = type || Roo.lib.AnimBase;
3270             if (typeof easing == "string") {
3271                 easing = Roo.lib.Easing[easing];
3272             }
3273             var anim = new type(el, args, duration, easing);
3274             anim.animateX(function() {
3275                 Roo.callback(cb, scope);
3276             });
3277             return anim;
3278         }
3279     };
3280 })();/*
3281  * Portions of this file are based on pieces of Yahoo User Interface Library
3282  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3283  * YUI licensed under the BSD License:
3284  * http://developer.yahoo.net/yui/license.txt
3285  * <script type="text/javascript">
3286  *
3287  */
3288
3289 (function() {    
3290     var libFlyweight;
3291     
3292     function fly(el) {
3293         if (!libFlyweight) {
3294             libFlyweight = new Roo.Element.Flyweight();
3295         }
3296         libFlyweight.dom = el;
3297         return libFlyweight;
3298     }
3299
3300     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3301     
3302    
3303     
3304     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3305         if (el) {
3306             this.init(el, attributes, duration, method);
3307         }
3308     };
3309
3310     Roo.lib.AnimBase.fly = fly;
3311     
3312     
3313     
3314     Roo.lib.AnimBase.prototype = {
3315
3316         toString: function() {
3317             var el = this.getEl();
3318             var id = el.id || el.tagName;
3319             return ("Anim " + id);
3320         },
3321
3322         patterns: {
3323             noNegatives:        /width|height|opacity|padding/i,
3324             offsetAttribute:  /^((width|height)|(top|left))$/,
3325             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3326             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3327         },
3328
3329
3330         doMethod: function(attr, start, end) {
3331             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3332         },
3333
3334
3335         setAttribute: function(attr, val, unit) {
3336             if (this.patterns.noNegatives.test(attr)) {
3337                 val = (val > 0) ? val : 0;
3338             }
3339
3340             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3341         },
3342
3343
3344         getAttribute: function(attr) {
3345             var el = this.getEl();
3346             var val = fly(el).getStyle(attr);
3347
3348             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3349                 return parseFloat(val);
3350             }
3351
3352             var a = this.patterns.offsetAttribute.exec(attr) || [];
3353             var pos = !!( a[3] );
3354             var box = !!( a[2] );
3355
3356
3357             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3358                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3359             } else {
3360                 val = 0;
3361             }
3362
3363             return val;
3364         },
3365
3366
3367         getDefaultUnit: function(attr) {
3368             if (this.patterns.defaultUnit.test(attr)) {
3369                 return 'px';
3370             }
3371
3372             return '';
3373         },
3374
3375         animateX : function(callback, scope) {
3376             var f = function() {
3377                 this.onComplete.removeListener(f);
3378                 if (typeof callback == "function") {
3379                     callback.call(scope || this, this);
3380                 }
3381             };
3382             this.onComplete.addListener(f, this);
3383             this.animate();
3384         },
3385
3386
3387         setRuntimeAttribute: function(attr) {
3388             var start;
3389             var end;
3390             var attributes = this.attributes;
3391
3392             this.runtimeAttributes[attr] = {};
3393
3394             var isset = function(prop) {
3395                 return (typeof prop !== 'undefined');
3396             };
3397
3398             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3399                 return false;
3400             }
3401
3402             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3403
3404
3405             if (isset(attributes[attr]['to'])) {
3406                 end = attributes[attr]['to'];
3407             } else if (isset(attributes[attr]['by'])) {
3408                 if (start.constructor == Array) {
3409                     end = [];
3410                     for (var i = 0, len = start.length; i < len; ++i) {
3411                         end[i] = start[i] + attributes[attr]['by'][i];
3412                     }
3413                 } else {
3414                     end = start + attributes[attr]['by'];
3415                 }
3416             }
3417
3418             this.runtimeAttributes[attr].start = start;
3419             this.runtimeAttributes[attr].end = end;
3420
3421
3422             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3423         },
3424
3425
3426         init: function(el, attributes, duration, method) {
3427
3428             var isAnimated = false;
3429
3430
3431             var startTime = null;
3432
3433
3434             var actualFrames = 0;
3435
3436
3437             el = Roo.getDom(el);
3438
3439
3440             this.attributes = attributes || {};
3441
3442
3443             this.duration = duration || 1;
3444
3445
3446             this.method = method || Roo.lib.Easing.easeNone;
3447
3448
3449             this.useSeconds = true;
3450
3451
3452             this.currentFrame = 0;
3453
3454
3455             this.totalFrames = Roo.lib.AnimMgr.fps;
3456
3457
3458             this.getEl = function() {
3459                 return el;
3460             };
3461
3462
3463             this.isAnimated = function() {
3464                 return isAnimated;
3465             };
3466
3467
3468             this.getStartTime = function() {
3469                 return startTime;
3470             };
3471
3472             this.runtimeAttributes = {};
3473
3474
3475             this.animate = function() {
3476                 if (this.isAnimated()) {
3477                     return false;
3478                 }
3479
3480                 this.currentFrame = 0;
3481
3482                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3483
3484                 Roo.lib.AnimMgr.registerElement(this);
3485             };
3486
3487
3488             this.stop = function(finish) {
3489                 if (finish) {
3490                     this.currentFrame = this.totalFrames;
3491                     this._onTween.fire();
3492                 }
3493                 Roo.lib.AnimMgr.stop(this);
3494             };
3495
3496             var onStart = function() {
3497                 this.onStart.fire();
3498
3499                 this.runtimeAttributes = {};
3500                 for (var attr in this.attributes) {
3501                     this.setRuntimeAttribute(attr);
3502                 }
3503
3504                 isAnimated = true;
3505                 actualFrames = 0;
3506                 startTime = new Date();
3507             };
3508
3509
3510             var onTween = function() {
3511                 var data = {
3512                     duration: new Date() - this.getStartTime(),
3513                     currentFrame: this.currentFrame
3514                 };
3515
3516                 data.toString = function() {
3517                     return (
3518                             'duration: ' + data.duration +
3519                             ', currentFrame: ' + data.currentFrame
3520                             );
3521                 };
3522
3523                 this.onTween.fire(data);
3524
3525                 var runtimeAttributes = this.runtimeAttributes;
3526
3527                 for (var attr in runtimeAttributes) {
3528                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3529                 }
3530
3531                 actualFrames += 1;
3532             };
3533
3534             var onComplete = function() {
3535                 var actual_duration = (new Date() - startTime) / 1000 ;
3536
3537                 var data = {
3538                     duration: actual_duration,
3539                     frames: actualFrames,
3540                     fps: actualFrames / actual_duration
3541                 };
3542
3543                 data.toString = function() {
3544                     return (
3545                             'duration: ' + data.duration +
3546                             ', frames: ' + data.frames +
3547                             ', fps: ' + data.fps
3548                             );
3549                 };
3550
3551                 isAnimated = false;
3552                 actualFrames = 0;
3553                 this.onComplete.fire(data);
3554             };
3555
3556
3557             this._onStart = new Roo.util.Event(this);
3558             this.onStart = new Roo.util.Event(this);
3559             this.onTween = new Roo.util.Event(this);
3560             this._onTween = new Roo.util.Event(this);
3561             this.onComplete = new Roo.util.Event(this);
3562             this._onComplete = new Roo.util.Event(this);
3563             this._onStart.addListener(onStart);
3564             this._onTween.addListener(onTween);
3565             this._onComplete.addListener(onComplete);
3566         }
3567     };
3568 })();
3569 /*
3570  * Portions of this file are based on pieces of Yahoo User Interface Library
3571  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3572  * YUI licensed under the BSD License:
3573  * http://developer.yahoo.net/yui/license.txt
3574  * <script type="text/javascript">
3575  *
3576  */
3577
3578 Roo.lib.AnimMgr = new function() {
3579
3580     var thread = null;
3581
3582
3583     var queue = [];
3584
3585
3586     var tweenCount = 0;
3587
3588
3589     this.fps = 1000;
3590
3591
3592     this.delay = 1;
3593
3594
3595     this.registerElement = function(tween) {
3596         queue[queue.length] = tween;
3597         tweenCount += 1;
3598         tween._onStart.fire();
3599         this.start();
3600     };
3601
3602
3603     this.unRegister = function(tween, index) {
3604         tween._onComplete.fire();
3605         index = index || getIndex(tween);
3606         if (index != -1) {
3607             queue.splice(index, 1);
3608         }
3609
3610         tweenCount -= 1;
3611         if (tweenCount <= 0) {
3612             this.stop();
3613         }
3614     };
3615
3616
3617     this.start = function() {
3618         if (thread === null) {
3619             thread = setInterval(this.run, this.delay);
3620         }
3621     };
3622
3623
3624     this.stop = function(tween) {
3625         if (!tween) {
3626             clearInterval(thread);
3627
3628             for (var i = 0, len = queue.length; i < len; ++i) {
3629                 if (queue[0].isAnimated()) {
3630                     this.unRegister(queue[0], 0);
3631                 }
3632             }
3633
3634             queue = [];
3635             thread = null;
3636             tweenCount = 0;
3637         }
3638         else {
3639             this.unRegister(tween);
3640         }
3641     };
3642
3643
3644     this.run = function() {
3645         for (var i = 0, len = queue.length; i < len; ++i) {
3646             var tween = queue[i];
3647             if (!tween || !tween.isAnimated()) {
3648                 continue;
3649             }
3650
3651             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3652             {
3653                 tween.currentFrame += 1;
3654
3655                 if (tween.useSeconds) {
3656                     correctFrame(tween);
3657                 }
3658                 tween._onTween.fire();
3659             }
3660             else {
3661                 Roo.lib.AnimMgr.stop(tween, i);
3662             }
3663         }
3664     };
3665
3666     var getIndex = function(anim) {
3667         for (var i = 0, len = queue.length; i < len; ++i) {
3668             if (queue[i] == anim) {
3669                 return i;
3670             }
3671         }
3672         return -1;
3673     };
3674
3675
3676     var correctFrame = function(tween) {
3677         var frames = tween.totalFrames;
3678         var frame = tween.currentFrame;
3679         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3680         var elapsed = (new Date() - tween.getStartTime());
3681         var tweak = 0;
3682
3683         if (elapsed < tween.duration * 1000) {
3684             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3685         } else {
3686             tweak = frames - (frame + 1);
3687         }
3688         if (tweak > 0 && isFinite(tweak)) {
3689             if (tween.currentFrame + tweak >= frames) {
3690                 tweak = frames - (frame + 1);
3691             }
3692
3693             tween.currentFrame += tweak;
3694         }
3695     };
3696 };
3697
3698     /*
3699  * Portions of this file are based on pieces of Yahoo User Interface Library
3700  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3701  * YUI licensed under the BSD License:
3702  * http://developer.yahoo.net/yui/license.txt
3703  * <script type="text/javascript">
3704  *
3705  */
3706 Roo.lib.Bezier = new function() {
3707
3708         this.getPosition = function(points, t) {
3709             var n = points.length;
3710             var tmp = [];
3711
3712             for (var i = 0; i < n; ++i) {
3713                 tmp[i] = [points[i][0], points[i][1]];
3714             }
3715
3716             for (var j = 1; j < n; ++j) {
3717                 for (i = 0; i < n - j; ++i) {
3718                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3719                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3720                 }
3721             }
3722
3723             return [ tmp[0][0], tmp[0][1] ];
3724
3725         };
3726     }; 
3727
3728 /**
3729  * @class Roo.lib.Color
3730  * @constructor
3731  * An abstract Color implementation. Concrete Color implementations should use
3732  * an instance of this function as their prototype, and implement the getRGB and
3733  * getHSL functions. getRGB should return an object representing the RGB
3734  * components of this Color, with the red, green, and blue components in the
3735  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3736  * return an object representing the HSL components of this Color, with the hue
3737  * component in the range [0,360), the saturation and lightness components in
3738  * the range [0,100], and the alpha component in the range [0,1].
3739  *
3740  *
3741  * Color.js
3742  *
3743  * Functions for Color handling and processing.
3744  *
3745  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3746  *
3747  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3748  * rights to this program, with the intention of it becoming part of the public
3749  * domain. Because this program is released into the public domain, it comes with
3750  * no warranty either expressed or implied, to the extent permitted by law.
3751  * 
3752  * For more free and public domain JavaScript code by the same author, visit:
3753  * http://www.safalra.com/web-design/javascript/
3754  * 
3755  */
3756 Roo.lib.Color = function() { }
3757
3758
3759 Roo.apply(Roo.lib.Color.prototype, {
3760   
3761   rgb : null,
3762   hsv : null,
3763   hsl : null,
3764   
3765   /**
3766    * getIntegerRGB
3767    * @return {Object} an object representing the RGBA components of this Color. The red,
3768    * green, and blue components are converted to integers in the range [0,255].
3769    * The alpha is a value in the range [0,1].
3770    */
3771   getIntegerRGB : function(){
3772
3773     // get the RGB components of this Color
3774     var rgb = this.getRGB();
3775
3776     // return the integer components
3777     return {
3778       'r' : Math.round(rgb.r),
3779       'g' : Math.round(rgb.g),
3780       'b' : Math.round(rgb.b),
3781       'a' : rgb.a
3782     };
3783
3784   },
3785
3786   /**
3787    * getPercentageRGB
3788    * @return {Object} an object representing the RGBA components of this Color. The red,
3789    * green, and blue components are converted to numbers in the range [0,100].
3790    * The alpha is a value in the range [0,1].
3791    */
3792   getPercentageRGB : function(){
3793
3794     // get the RGB components of this Color
3795     var rgb = this.getRGB();
3796
3797     // return the percentage components
3798     return {
3799       'r' : 100 * rgb.r / 255,
3800       'g' : 100 * rgb.g / 255,
3801       'b' : 100 * rgb.b / 255,
3802       'a' : rgb.a
3803     };
3804
3805   },
3806
3807   /**
3808    * getCSSHexadecimalRGB
3809    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3810    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3811    * are two-digit hexadecimal numbers.
3812    */
3813   getCSSHexadecimalRGB : function()
3814   {
3815
3816     // get the integer RGB components
3817     var rgb = this.getIntegerRGB();
3818
3819     // determine the hexadecimal equivalents
3820     var r16 = rgb.r.toString(16);
3821     var g16 = rgb.g.toString(16);
3822     var b16 = rgb.b.toString(16);
3823
3824     // return the CSS RGB Color value
3825     return '#'
3826         + (r16.length == 2 ? r16 : '0' + r16)
3827         + (g16.length == 2 ? g16 : '0' + g16)
3828         + (b16.length == 2 ? b16 : '0' + b16);
3829
3830   },
3831
3832   /**
3833    * getCSSIntegerRGB
3834    * @return {String} a string representing this Color as a CSS integer RGB Color
3835    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3836    * are integers in the range [0,255].
3837    */
3838   getCSSIntegerRGB : function(){
3839
3840     // get the integer RGB components
3841     var rgb = this.getIntegerRGB();
3842
3843     // return the CSS RGB Color value
3844     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3845
3846   },
3847
3848   /**
3849    * getCSSIntegerRGBA
3850    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3851    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3852    * b are integers in the range [0,255] and a is in the range [0,1].
3853    */
3854   getCSSIntegerRGBA : function(){
3855
3856     // get the integer RGB components
3857     var rgb = this.getIntegerRGB();
3858
3859     // return the CSS integer RGBA Color value
3860     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3861
3862   },
3863
3864   /**
3865    * getCSSPercentageRGB
3866    * @return {String} a string representing this Color as a CSS percentage RGB Color
3867    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3868    * b are in the range [0,100].
3869    */
3870   getCSSPercentageRGB : function(){
3871
3872     // get the percentage RGB components
3873     var rgb = this.getPercentageRGB();
3874
3875     // return the CSS RGB Color value
3876     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3877
3878   },
3879
3880   /**
3881    * getCSSPercentageRGBA
3882    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3883    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3884    * and b are in the range [0,100] and a is in the range [0,1].
3885    */
3886   getCSSPercentageRGBA : function(){
3887
3888     // get the percentage RGB components
3889     var rgb = this.getPercentageRGB();
3890
3891     // return the CSS percentage RGBA Color value
3892     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3893
3894   },
3895
3896   /**
3897    * getCSSHSL
3898    * @return {String} a string representing this Color as a CSS HSL Color value - that
3899    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3900    * s and l are in the range [0,100].
3901    */
3902   getCSSHSL : function(){
3903
3904     // get the HSL components
3905     var hsl = this.getHSL();
3906
3907     // return the CSS HSL Color value
3908     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3909
3910   },
3911
3912   /**
3913    * getCSSHSLA
3914    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3915    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3916    * s and l are in the range [0,100], and a is in the range [0,1].
3917    */
3918   getCSSHSLA : function(){
3919
3920     // get the HSL components
3921     var hsl = this.getHSL();
3922
3923     // return the CSS HSL Color value
3924     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3925
3926   },
3927
3928   /**
3929    * Sets the Color of the specified node to this Color. This functions sets
3930    * the CSS 'color' property for the node. The parameter is:
3931    * 
3932    * @param {DomElement} node - the node whose Color should be set
3933    */
3934   setNodeColor : function(node){
3935
3936     // set the Color of the node
3937     node.style.color = this.getCSSHexadecimalRGB();
3938
3939   },
3940
3941   /**
3942    * Sets the background Color of the specified node to this Color. This
3943    * functions sets the CSS 'background-color' property for the node. The
3944    * parameter is:
3945    *
3946    * @param {DomElement} node - the node whose background Color should be set
3947    */
3948   setNodeBackgroundColor : function(node){
3949
3950     // set the background Color of the node
3951     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3952
3953   },
3954   // convert between formats..
3955   toRGB: function()
3956   {
3957     var r = this.getIntegerRGB();
3958     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3959     
3960   },
3961   toHSL : function()
3962   {
3963      var hsl = this.getHSL();
3964   // return the CSS HSL Color value
3965     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3966     
3967   },
3968   
3969   toHSV : function()
3970   {
3971     var rgb = this.toRGB();
3972     var hsv = rgb.getHSV();
3973    // return the CSS HSL Color value
3974     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3975     
3976   },
3977   
3978   // modify  v = 0 ... 1 (eg. 0.5)
3979   saturate : function(v)
3980   {
3981       var rgb = this.toRGB();
3982       var hsv = rgb.getHSV();
3983       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3984       
3985   
3986   },
3987   
3988    
3989   /**
3990    * getRGB
3991    * @return {Object} the RGB and alpha components of this Color as an object with r,
3992    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3993    * the range [0,1].
3994    */
3995   getRGB: function(){
3996    
3997     // return the RGB components
3998     return {
3999       'r' : this.rgb.r,
4000       'g' : this.rgb.g,
4001       'b' : this.rgb.b,
4002       'a' : this.alpha
4003     };
4004
4005   },
4006
4007   /**
4008    * getHSV
4009    * @return {Object} the HSV and alpha components of this Color as an object with h,
4010    * s, v, and a properties. h is in the range [0,360), s and v are in the range
4011    * [0,100], and a is in the range [0,1].
4012    */
4013   getHSV : function()
4014   {
4015     
4016     // calculate the HSV components if necessary
4017     if (this.hsv == null) {
4018       this.calculateHSV();
4019     }
4020
4021     // return the HSV components
4022     return {
4023       'h' : this.hsv.h,
4024       's' : this.hsv.s,
4025       'v' : this.hsv.v,
4026       'a' : this.alpha
4027     };
4028
4029   },
4030
4031   /**
4032    * getHSL
4033    * @return {Object} the HSL and alpha components of this Color as an object with h,
4034    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4035    * [0,100], and a is in the range [0,1].
4036    */
4037   getHSL : function(){
4038     
4039      
4040     // calculate the HSV components if necessary
4041     if (this.hsl == null) { this.calculateHSL(); }
4042
4043     // return the HSL components
4044     return {
4045       'h' : this.hsl.h,
4046       's' : this.hsl.s,
4047       'l' : this.hsl.l,
4048       'a' : this.alpha
4049     };
4050
4051   }
4052   
4053
4054 });
4055
4056
4057 /**
4058  * @class Roo.lib.RGBColor
4059  * @extends Roo.lib.Color
4060  * Creates a Color specified in the RGB Color space, with an optional alpha
4061  * component. The parameters are:
4062  * @constructor
4063  * 
4064
4065  * @param {Number} r - the red component, clipped to the range [0,255]
4066  * @param {Number} g - the green component, clipped to the range [0,255]
4067  * @param {Number} b - the blue component, clipped to the range [0,255]
4068  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4069  *     optional and defaults to 1
4070  */
4071 Roo.lib.RGBColor = function (r, g, b, a){
4072
4073   // store the alpha component after clipping it if necessary
4074   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4075
4076   // store the RGB components after clipping them if necessary
4077   this.rgb =
4078       {
4079         'r' : Math.max(0, Math.min(255, r)),
4080         'g' : Math.max(0, Math.min(255, g)),
4081         'b' : Math.max(0, Math.min(255, b))
4082       };
4083
4084   // initialise the HSV and HSL components to null
4085   
4086
4087   /* 
4088    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4089    * range [0,360). The parameters are:
4090    *
4091    * maximum - the maximum of the RGB component values
4092    * range   - the range of the RGB component values
4093    */
4094    
4095
4096 }
4097 // this does an 'exteds'
4098 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4099
4100   
4101     getHue  : function(maximum, range)
4102     {
4103       var rgb = this.rgb;
4104        
4105       // check whether the range is zero
4106       if (range == 0){
4107   
4108         // set the hue to zero (any hue is acceptable as the Color is grey)
4109         var hue = 0;
4110   
4111       }else{
4112   
4113         // determine which of the components has the highest value and set the hue
4114         switch (maximum){
4115   
4116           // red has the highest value
4117           case rgb.r:
4118             var hue = (rgb.g - rgb.b) / range * 60;
4119             if (hue < 0) { hue += 360; }
4120             break;
4121   
4122           // green has the highest value
4123           case rgb.g:
4124             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4125             break;
4126   
4127           // blue has the highest value
4128           case rgb.b:
4129             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4130             break;
4131   
4132         }
4133   
4134       }
4135   
4136       // return the hue
4137       return hue;
4138   
4139     },
4140
4141   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4142    * be returned be the getHSV function.
4143    */
4144    calculateHSV : function(){
4145     var rgb = this.rgb;
4146     // get the maximum and range of the RGB component values
4147     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4148     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4149
4150     // store the HSV components
4151     this.hsv =
4152         {
4153           'h' : this.getHue(maximum, range),
4154           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4155           'v' : maximum / 2.55
4156         };
4157
4158   },
4159
4160   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4161    * be returned be the getHSL function.
4162    */
4163    calculateHSL : function(){
4164     var rgb = this.rgb;
4165     // get the maximum and range of the RGB component values
4166     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4167     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4168
4169     // determine the lightness in the range [0,1]
4170     var l = maximum / 255 - range / 510;
4171
4172     // store the HSL components
4173     this.hsl =
4174         {
4175           'h' : this.getHue(maximum, range),
4176           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4177           'l' : 100 * l
4178         };
4179
4180   }
4181
4182 });
4183
4184 /**
4185  * @class Roo.lib.HSVColor
4186  * @extends Roo.lib.Color
4187  * Creates a Color specified in the HSV Color space, with an optional alpha
4188  * component. The parameters are:
4189  * @constructor
4190  *
4191  * @param {Number} h - the hue component, wrapped to the range [0,360)
4192  * @param {Number} s - the saturation component, clipped to the range [0,100]
4193  * @param {Number} v - the value component, clipped to the range [0,100]
4194  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4195  *     optional and defaults to 1
4196  */
4197 Roo.lib.HSVColor = function (h, s, v, a){
4198
4199   // store the alpha component after clipping it if necessary
4200   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4201
4202   // store the HSV components after clipping or wrapping them if necessary
4203   this.hsv =
4204       {
4205         'h' : (h % 360 + 360) % 360,
4206         's' : Math.max(0, Math.min(100, s)),
4207         'v' : Math.max(0, Math.min(100, v))
4208       };
4209
4210   // initialise the RGB and HSL components to null
4211   this.rgb = null;
4212   this.hsl = null;
4213 }
4214
4215 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4216   /* Calculates and stores the RGB components of this HSVColor so that they can
4217    * be returned be the getRGB function.
4218    */
4219   calculateRGB: function ()
4220   {
4221     var hsv = this.hsv;
4222     // check whether the saturation is zero
4223     if (hsv.s == 0){
4224
4225       // set the Color to the appropriate shade of grey
4226       var r = hsv.v;
4227       var g = hsv.v;
4228       var b = hsv.v;
4229
4230     }else{
4231
4232       // set some temporary values
4233       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4234       var p  = hsv.v * (1 - hsv.s / 100);
4235       var q  = hsv.v * (1 - hsv.s / 100 * f);
4236       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4237
4238       // set the RGB Color components to their temporary values
4239       switch (Math.floor(hsv.h / 60)){
4240         case 0: var r = hsv.v; var g = t; var b = p; break;
4241         case 1: var r = q; var g = hsv.v; var b = p; break;
4242         case 2: var r = p; var g = hsv.v; var b = t; break;
4243         case 3: var r = p; var g = q; var b = hsv.v; break;
4244         case 4: var r = t; var g = p; var b = hsv.v; break;
4245         case 5: var r = hsv.v; var g = p; var b = q; break;
4246       }
4247
4248     }
4249
4250     // store the RGB components
4251     this.rgb =
4252         {
4253           'r' : r * 2.55,
4254           'g' : g * 2.55,
4255           'b' : b * 2.55
4256         };
4257
4258   },
4259
4260   /* Calculates and stores the HSL components of this HSVColor so that they can
4261    * be returned be the getHSL function.
4262    */
4263   calculateHSL : function (){
4264
4265     var hsv = this.hsv;
4266     // determine the lightness in the range [0,100]
4267     var l = (2 - hsv.s / 100) * hsv.v / 2;
4268
4269     // store the HSL components
4270     this.hsl =
4271         {
4272           'h' : hsv.h,
4273           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4274           'l' : l
4275         };
4276
4277     // correct a division-by-zero error
4278     if (isNaN(hsl.s)) { hsl.s = 0; }
4279
4280   } 
4281  
4282
4283 });
4284  
4285
4286 /**
4287  * @class Roo.lib.HSLColor
4288  * @extends Roo.lib.Color
4289  *
4290  * @constructor
4291  * Creates a Color specified in the HSL Color space, with an optional alpha
4292  * component. The parameters are:
4293  *
4294  * @param {Number} h - the hue component, wrapped to the range [0,360)
4295  * @param {Number} s - the saturation component, clipped to the range [0,100]
4296  * @param {Number} l - the lightness component, clipped to the range [0,100]
4297  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4298  *     optional and defaults to 1
4299  */
4300
4301 Roo.lib.HSLColor = function(h, s, l, a){
4302
4303   // store the alpha component after clipping it if necessary
4304   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4305
4306   // store the HSL components after clipping or wrapping them if necessary
4307   this.hsl =
4308       {
4309         'h' : (h % 360 + 360) % 360,
4310         's' : Math.max(0, Math.min(100, s)),
4311         'l' : Math.max(0, Math.min(100, l))
4312       };
4313
4314   // initialise the RGB and HSV components to null
4315 }
4316
4317 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4318
4319   /* Calculates and stores the RGB components of this HSLColor so that they can
4320    * be returned be the getRGB function.
4321    */
4322   calculateRGB: function (){
4323
4324     // check whether the saturation is zero
4325     if (this.hsl.s == 0){
4326
4327       // store the RGB components representing the appropriate shade of grey
4328       this.rgb =
4329           {
4330             'r' : this.hsl.l * 2.55,
4331             'g' : this.hsl.l * 2.55,
4332             'b' : this.hsl.l * 2.55
4333           };
4334
4335     }else{
4336
4337       // set some temporary values
4338       var p = this.hsl.l < 50
4339             ? this.hsl.l * (1 + hsl.s / 100)
4340             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4341       var q = 2 * hsl.l - p;
4342
4343       // initialise the RGB components
4344       this.rgb =
4345           {
4346             'r' : (h + 120) / 60 % 6,
4347             'g' : h / 60,
4348             'b' : (h + 240) / 60 % 6
4349           };
4350
4351       // loop over the RGB components
4352       for (var key in this.rgb){
4353
4354         // ensure that the property is not inherited from the root object
4355         if (this.rgb.hasOwnProperty(key)){
4356
4357           // set the component to its value in the range [0,100]
4358           if (this.rgb[key] < 1){
4359             this.rgb[key] = q + (p - q) * this.rgb[key];
4360           }else if (this.rgb[key] < 3){
4361             this.rgb[key] = p;
4362           }else if (this.rgb[key] < 4){
4363             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4364           }else{
4365             this.rgb[key] = q;
4366           }
4367
4368           // set the component to its value in the range [0,255]
4369           this.rgb[key] *= 2.55;
4370
4371         }
4372
4373       }
4374
4375     }
4376
4377   },
4378
4379   /* Calculates and stores the HSV components of this HSLColor so that they can
4380    * be returned be the getHSL function.
4381    */
4382    calculateHSV : function(){
4383
4384     // set a temporary value
4385     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4386
4387     // store the HSV components
4388     this.hsv =
4389         {
4390           'h' : this.hsl.h,
4391           's' : 200 * t / (this.hsl.l + t),
4392           'v' : t + this.hsl.l
4393         };
4394
4395     // correct a division-by-zero error
4396     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4397
4398   }
4399  
4400
4401 });
4402 /*
4403  * Portions of this file are based on pieces of Yahoo User Interface Library
4404  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4405  * YUI licensed under the BSD License:
4406  * http://developer.yahoo.net/yui/license.txt
4407  * <script type="text/javascript">
4408  *
4409  */
4410 (function() {
4411
4412     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4413         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4414     };
4415
4416     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4417
4418     var fly = Roo.lib.AnimBase.fly;
4419     var Y = Roo.lib;
4420     var superclass = Y.ColorAnim.superclass;
4421     var proto = Y.ColorAnim.prototype;
4422
4423     proto.toString = function() {
4424         var el = this.getEl();
4425         var id = el.id || el.tagName;
4426         return ("ColorAnim " + id);
4427     };
4428
4429     proto.patterns.color = /color$/i;
4430     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4431     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4432     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4433     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4434
4435
4436     proto.parseColor = function(s) {
4437         if (s.length == 3) {
4438             return s;
4439         }
4440
4441         var c = this.patterns.hex.exec(s);
4442         if (c && c.length == 4) {
4443             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4444         }
4445
4446         c = this.patterns.rgb.exec(s);
4447         if (c && c.length == 4) {
4448             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4449         }
4450
4451         c = this.patterns.hex3.exec(s);
4452         if (c && c.length == 4) {
4453             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4454         }
4455
4456         return null;
4457     };
4458     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4459     proto.getAttribute = function(attr) {
4460         var el = this.getEl();
4461         if (this.patterns.color.test(attr)) {
4462             var val = fly(el).getStyle(attr);
4463
4464             if (this.patterns.transparent.test(val)) {
4465                 var parent = el.parentNode;
4466                 val = fly(parent).getStyle(attr);
4467
4468                 while (parent && this.patterns.transparent.test(val)) {
4469                     parent = parent.parentNode;
4470                     val = fly(parent).getStyle(attr);
4471                     if (parent.tagName.toUpperCase() == 'HTML') {
4472                         val = '#fff';
4473                     }
4474                 }
4475             }
4476         } else {
4477             val = superclass.getAttribute.call(this, attr);
4478         }
4479
4480         return val;
4481     };
4482     proto.getAttribute = function(attr) {
4483         var el = this.getEl();
4484         if (this.patterns.color.test(attr)) {
4485             var val = fly(el).getStyle(attr);
4486
4487             if (this.patterns.transparent.test(val)) {
4488                 var parent = el.parentNode;
4489                 val = fly(parent).getStyle(attr);
4490
4491                 while (parent && this.patterns.transparent.test(val)) {
4492                     parent = parent.parentNode;
4493                     val = fly(parent).getStyle(attr);
4494                     if (parent.tagName.toUpperCase() == 'HTML') {
4495                         val = '#fff';
4496                     }
4497                 }
4498             }
4499         } else {
4500             val = superclass.getAttribute.call(this, attr);
4501         }
4502
4503         return val;
4504     };
4505
4506     proto.doMethod = function(attr, start, end) {
4507         var val;
4508
4509         if (this.patterns.color.test(attr)) {
4510             val = [];
4511             for (var i = 0, len = start.length; i < len; ++i) {
4512                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4513             }
4514
4515             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4516         }
4517         else {
4518             val = superclass.doMethod.call(this, attr, start, end);
4519         }
4520
4521         return val;
4522     };
4523
4524     proto.setRuntimeAttribute = function(attr) {
4525         superclass.setRuntimeAttribute.call(this, attr);
4526
4527         if (this.patterns.color.test(attr)) {
4528             var attributes = this.attributes;
4529             var start = this.parseColor(this.runtimeAttributes[attr].start);
4530             var end = this.parseColor(this.runtimeAttributes[attr].end);
4531
4532             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4533                 end = this.parseColor(attributes[attr].by);
4534
4535                 for (var i = 0, len = start.length; i < len; ++i) {
4536                     end[i] = start[i] + end[i];
4537                 }
4538             }
4539
4540             this.runtimeAttributes[attr].start = start;
4541             this.runtimeAttributes[attr].end = end;
4542         }
4543     };
4544 })();
4545
4546 /*
4547  * Portions of this file are based on pieces of Yahoo User Interface Library
4548  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4549  * YUI licensed under the BSD License:
4550  * http://developer.yahoo.net/yui/license.txt
4551  * <script type="text/javascript">
4552  *
4553  */
4554 Roo.lib.Easing = {
4555
4556
4557     easeNone: function (t, b, c, d) {
4558         return c * t / d + b;
4559     },
4560
4561
4562     easeIn: function (t, b, c, d) {
4563         return c * (t /= d) * t + b;
4564     },
4565
4566
4567     easeOut: function (t, b, c, d) {
4568         return -c * (t /= d) * (t - 2) + b;
4569     },
4570
4571
4572     easeBoth: function (t, b, c, d) {
4573         if ((t /= d / 2) < 1) {
4574             return c / 2 * t * t + b;
4575         }
4576
4577         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4578     },
4579
4580
4581     easeInStrong: function (t, b, c, d) {
4582         return c * (t /= d) * t * t * t + b;
4583     },
4584
4585
4586     easeOutStrong: function (t, b, c, d) {
4587         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4588     },
4589
4590
4591     easeBothStrong: function (t, b, c, d) {
4592         if ((t /= d / 2) < 1) {
4593             return c / 2 * t * t * t * t + b;
4594         }
4595
4596         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4597     },
4598
4599
4600
4601     elasticIn: function (t, b, c, d, a, p) {
4602         if (t == 0) {
4603             return b;
4604         }
4605         if ((t /= d) == 1) {
4606             return b + c;
4607         }
4608         if (!p) {
4609             p = d * .3;
4610         }
4611
4612         if (!a || a < Math.abs(c)) {
4613             a = c;
4614             var s = p / 4;
4615         }
4616         else {
4617             var s = p / (2 * Math.PI) * Math.asin(c / a);
4618         }
4619
4620         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4621     },
4622
4623
4624     elasticOut: function (t, b, c, d, a, p) {
4625         if (t == 0) {
4626             return b;
4627         }
4628         if ((t /= d) == 1) {
4629             return b + c;
4630         }
4631         if (!p) {
4632             p = d * .3;
4633         }
4634
4635         if (!a || a < Math.abs(c)) {
4636             a = c;
4637             var s = p / 4;
4638         }
4639         else {
4640             var s = p / (2 * Math.PI) * Math.asin(c / a);
4641         }
4642
4643         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4644     },
4645
4646
4647     elasticBoth: function (t, b, c, d, a, p) {
4648         if (t == 0) {
4649             return b;
4650         }
4651
4652         if ((t /= d / 2) == 2) {
4653             return b + c;
4654         }
4655
4656         if (!p) {
4657             p = d * (.3 * 1.5);
4658         }
4659
4660         if (!a || a < Math.abs(c)) {
4661             a = c;
4662             var s = p / 4;
4663         }
4664         else {
4665             var s = p / (2 * Math.PI) * Math.asin(c / a);
4666         }
4667
4668         if (t < 1) {
4669             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4670                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4671         }
4672         return a * Math.pow(2, -10 * (t -= 1)) *
4673                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4674     },
4675
4676
4677
4678     backIn: function (t, b, c, d, s) {
4679         if (typeof s == 'undefined') {
4680             s = 1.70158;
4681         }
4682         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4683     },
4684
4685
4686     backOut: function (t, b, c, d, s) {
4687         if (typeof s == 'undefined') {
4688             s = 1.70158;
4689         }
4690         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4691     },
4692
4693
4694     backBoth: function (t, b, c, d, s) {
4695         if (typeof s == 'undefined') {
4696             s = 1.70158;
4697         }
4698
4699         if ((t /= d / 2 ) < 1) {
4700             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4701         }
4702         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4703     },
4704
4705
4706     bounceIn: function (t, b, c, d) {
4707         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4708     },
4709
4710
4711     bounceOut: function (t, b, c, d) {
4712         if ((t /= d) < (1 / 2.75)) {
4713             return c * (7.5625 * t * t) + b;
4714         } else if (t < (2 / 2.75)) {
4715             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4716         } else if (t < (2.5 / 2.75)) {
4717             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4718         }
4719         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4720     },
4721
4722
4723     bounceBoth: function (t, b, c, d) {
4724         if (t < d / 2) {
4725             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4726         }
4727         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4728     }
4729 };/*
4730  * Portions of this file are based on pieces of Yahoo User Interface Library
4731  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4732  * YUI licensed under the BSD License:
4733  * http://developer.yahoo.net/yui/license.txt
4734  * <script type="text/javascript">
4735  *
4736  */
4737     (function() {
4738         Roo.lib.Motion = function(el, attributes, duration, method) {
4739             if (el) {
4740                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4741             }
4742         };
4743
4744         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4745
4746
4747         var Y = Roo.lib;
4748         var superclass = Y.Motion.superclass;
4749         var proto = Y.Motion.prototype;
4750
4751         proto.toString = function() {
4752             var el = this.getEl();
4753             var id = el.id || el.tagName;
4754             return ("Motion " + id);
4755         };
4756
4757         proto.patterns.points = /^points$/i;
4758
4759         proto.setAttribute = function(attr, val, unit) {
4760             if (this.patterns.points.test(attr)) {
4761                 unit = unit || 'px';
4762                 superclass.setAttribute.call(this, 'left', val[0], unit);
4763                 superclass.setAttribute.call(this, 'top', val[1], unit);
4764             } else {
4765                 superclass.setAttribute.call(this, attr, val, unit);
4766             }
4767         };
4768
4769         proto.getAttribute = function(attr) {
4770             if (this.patterns.points.test(attr)) {
4771                 var val = [
4772                         superclass.getAttribute.call(this, 'left'),
4773                         superclass.getAttribute.call(this, 'top')
4774                         ];
4775             } else {
4776                 val = superclass.getAttribute.call(this, attr);
4777             }
4778
4779             return val;
4780         };
4781
4782         proto.doMethod = function(attr, start, end) {
4783             var val = null;
4784
4785             if (this.patterns.points.test(attr)) {
4786                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4787                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4788             } else {
4789                 val = superclass.doMethod.call(this, attr, start, end);
4790             }
4791             return val;
4792         };
4793
4794         proto.setRuntimeAttribute = function(attr) {
4795             if (this.patterns.points.test(attr)) {
4796                 var el = this.getEl();
4797                 var attributes = this.attributes;
4798                 var start;
4799                 var control = attributes['points']['control'] || [];
4800                 var end;
4801                 var i, len;
4802
4803                 if (control.length > 0 && !(control[0] instanceof Array)) {
4804                     control = [control];
4805                 } else {
4806                     var tmp = [];
4807                     for (i = 0,len = control.length; i < len; ++i) {
4808                         tmp[i] = control[i];
4809                     }
4810                     control = tmp;
4811                 }
4812
4813                 Roo.fly(el).position();
4814
4815                 if (isset(attributes['points']['from'])) {
4816                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4817                 }
4818                 else {
4819                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4820                 }
4821
4822                 start = this.getAttribute('points');
4823
4824
4825                 if (isset(attributes['points']['to'])) {
4826                     end = translateValues.call(this, attributes['points']['to'], start);
4827
4828                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4829                     for (i = 0,len = control.length; i < len; ++i) {
4830                         control[i] = translateValues.call(this, control[i], start);
4831                     }
4832
4833
4834                 } else if (isset(attributes['points']['by'])) {
4835                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4836
4837                     for (i = 0,len = control.length; i < len; ++i) {
4838                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4839                     }
4840                 }
4841
4842                 this.runtimeAttributes[attr] = [start];
4843
4844                 if (control.length > 0) {
4845                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4846                 }
4847
4848                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4849             }
4850             else {
4851                 superclass.setRuntimeAttribute.call(this, attr);
4852             }
4853         };
4854
4855         var translateValues = function(val, start) {
4856             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4857             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4858
4859             return val;
4860         };
4861
4862         var isset = function(prop) {
4863             return (typeof prop !== 'undefined');
4864         };
4865     })();
4866 /*
4867  * Portions of this file are based on pieces of Yahoo User Interface Library
4868  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4869  * YUI licensed under the BSD License:
4870  * http://developer.yahoo.net/yui/license.txt
4871  * <script type="text/javascript">
4872  *
4873  */
4874     (function() {
4875         Roo.lib.Scroll = function(el, attributes, duration, method) {
4876             if (el) {
4877                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4878             }
4879         };
4880
4881         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4882
4883
4884         var Y = Roo.lib;
4885         var superclass = Y.Scroll.superclass;
4886         var proto = Y.Scroll.prototype;
4887
4888         proto.toString = function() {
4889             var el = this.getEl();
4890             var id = el.id || el.tagName;
4891             return ("Scroll " + id);
4892         };
4893
4894         proto.doMethod = function(attr, start, end) {
4895             var val = null;
4896
4897             if (attr == 'scroll') {
4898                 val = [
4899                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4900                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4901                         ];
4902
4903             } else {
4904                 val = superclass.doMethod.call(this, attr, start, end);
4905             }
4906             return val;
4907         };
4908
4909         proto.getAttribute = function(attr) {
4910             var val = null;
4911             var el = this.getEl();
4912
4913             if (attr == 'scroll') {
4914                 val = [ el.scrollLeft, el.scrollTop ];
4915             } else {
4916                 val = superclass.getAttribute.call(this, attr);
4917             }
4918
4919             return val;
4920         };
4921
4922         proto.setAttribute = function(attr, val, unit) {
4923             var el = this.getEl();
4924
4925             if (attr == 'scroll') {
4926                 el.scrollLeft = val[0];
4927                 el.scrollTop = val[1];
4928             } else {
4929                 superclass.setAttribute.call(this, attr, val, unit);
4930             }
4931         };
4932     })();
4933 /**
4934  * Originally based of this code... - refactored for Roo...
4935  * https://github.com/aaalsaleh/undo-manager
4936  
4937  * undo-manager.js
4938  * @author  Abdulrahman Alsaleh 
4939  * @copyright 2015 Abdulrahman Alsaleh 
4940  * @license  MIT License (c) 
4941  *
4942  * Hackily modifyed by alan@roojs.com
4943  *
4944  *
4945  *  
4946  *
4947  *  TOTALLY UNTESTED...
4948  *
4949  *  Documentation to be done....
4950  */
4951  
4952
4953 /**
4954 * @class Roo.lib.UndoManager
4955 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4956 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4957
4958  * Usage:
4959  * <pre><code>
4960
4961
4962 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4963  
4964 </code></pre>
4965
4966 * For more information see this blog post with examples:
4967 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4968      - Create Elements using DOM, HTML fragments and Templates</a>. 
4969 * @constructor
4970 * @param {Number} limit how far back to go ... use 1000?
4971 * @param {Object} scope usually use document..
4972 */
4973
4974 Roo.lib.UndoManager = function (limit, undoScopeHost)
4975 {
4976     this.stack = [];
4977     this.limit = limit;
4978     this.scope = undoScopeHost;
4979     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4980     if (this.fireEvent) {
4981         this.bindEvents();
4982     }
4983     this.reset();
4984     
4985 };
4986         
4987 Roo.lib.UndoManager.prototype = {
4988     
4989     limit : false,
4990     stack : false,
4991     scope :  false,
4992     fireEvent : false,
4993     position : 0,
4994     length : 0,
4995     
4996     
4997      /**
4998      * To push and execute a transaction, the method undoManager.transact
4999      * must be called by passing a transaction object as the first argument, and a merge
5000      * flag as the second argument. A transaction object has the following properties:
5001      *
5002      * Usage:
5003 <pre><code>
5004 undoManager.transact({
5005     label: 'Typing',
5006     execute: function() { ... },
5007     undo: function() { ... },
5008     // redo same as execute
5009     redo: function() { this.execute(); }
5010 }, false);
5011
5012 // merge transaction
5013 undoManager.transact({
5014     label: 'Typing',
5015     execute: function() { ... },  // this will be run...
5016     undo: function() { ... }, // what to do when undo is run.
5017     // redo same as execute
5018     redo: function() { this.execute(); }
5019 }, true); 
5020 </code></pre> 
5021      *
5022      * 
5023      * @param {Object} transaction The transaction to add to the stack.
5024      * @return {String} The HTML fragment
5025      */
5026     
5027     
5028     transact : function (transaction, merge)
5029     {
5030         if (arguments.length < 2) {
5031             throw new TypeError('Not enough arguments to UndoManager.transact.');
5032         }
5033
5034         transaction.execute();
5035
5036         this.stack.splice(0, this.position);
5037         if (merge && this.length) {
5038             this.stack[0].push(transaction);
5039         } else {
5040             this.stack.unshift([transaction]);
5041         }
5042     
5043         this.position = 0;
5044
5045         if (this.limit && this.stack.length > this.limit) {
5046             this.length = this.stack.length = this.limit;
5047         } else {
5048             this.length = this.stack.length;
5049         }
5050
5051         if (this.fireEvent) {
5052             this.scope.dispatchEvent(
5053                 new CustomEvent('DOMTransaction', {
5054                     detail: {
5055                         transactions: this.stack[0].slice()
5056                     },
5057                     bubbles: true,
5058                     cancelable: false
5059                 })
5060             );
5061         }
5062         
5063         //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5064       
5065         
5066     },
5067
5068     undo : function ()
5069     {
5070         //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5071         
5072         if (this.position < this.length) {
5073             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5074                 this.stack[this.position][i].undo();
5075             }
5076             this.position++;
5077
5078             if (this.fireEvent) {
5079                 this.scope.dispatchEvent(
5080                     new CustomEvent('undo', {
5081                         detail: {
5082                             transactions: this.stack[this.position - 1].slice()
5083                         },
5084                         bubbles: true,
5085                         cancelable: false
5086                     })
5087                 );
5088             }
5089         }
5090     },
5091
5092     redo : function ()
5093     {
5094         if (this.position > 0) {
5095             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5096                 this.stack[this.position - 1][i].redo();
5097             }
5098             this.position--;
5099
5100             if (this.fireEvent) {
5101                 this.scope.dispatchEvent(
5102                     new CustomEvent('redo', {
5103                         detail: {
5104                             transactions: this.stack[this.position].slice()
5105                         },
5106                         bubbles: true,
5107                         cancelable: false
5108                     })
5109                 );
5110             }
5111         }
5112     },
5113
5114     item : function (index)
5115     {
5116         if (index >= 0 && index < this.length) {
5117             return this.stack[index].slice();
5118         }
5119         return null;
5120     },
5121
5122     clearUndo : function () {
5123         this.stack.length = this.length = this.position;
5124     },
5125
5126     clearRedo : function () {
5127         this.stack.splice(0, this.position);
5128         this.position = 0;
5129         this.length = this.stack.length;
5130     },
5131     /**
5132      * Reset the undo - probaly done on load to clear all history.
5133      */
5134     reset : function()
5135     {
5136         this.stack = [];
5137         this.position = 0;
5138         this.length = 0;
5139         this.current_html = this.scope.innerHTML;
5140         if (this.timer !== false) {
5141             clearTimeout(this.timer);
5142         }
5143         this.timer = false;
5144         this.merge = false;
5145         this.addEvent();
5146         
5147     },
5148     current_html : '',
5149     timer : false,
5150     merge : false,
5151     
5152     
5153     // this will handle the undo/redo on the element.?
5154     bindEvents : function()
5155     {
5156         var el  = this.scope;
5157         el.undoManager = this;
5158         
5159         
5160         this.scope.addEventListener('keydown', function(e) {
5161             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5162                 if (e.shiftKey) {
5163                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5164                 } else {
5165                     el.undoManager.undo(); // Ctrl/Command + Z
5166                 }
5167         
5168                 e.preventDefault();
5169             }
5170         });
5171         /// ignore keyup..
5172         this.scope.addEventListener('keyup', function(e) {
5173             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5174                 e.preventDefault();
5175             }
5176         });
5177         
5178         
5179         
5180         var t = this;
5181         
5182         el.addEventListener('input', function(e) {
5183             if(el.innerHTML == t.current_html) {
5184                 return;
5185             }
5186             // only record events every second.
5187             if (t.timer !== false) {
5188                clearTimeout(t.timer);
5189                t.timer = false;
5190             }
5191             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5192             
5193             t.addEvent(t.merge);
5194             t.merge = true; // ignore changes happening every second..
5195         });
5196         },
5197     /**
5198      * Manually add an event.
5199      * Normall called without arguements - and it will just get added to the stack.
5200      * 
5201      */
5202     
5203     addEvent : function(merge)
5204     {
5205         //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5206         // not sure if this should clear the timer 
5207         merge = typeof(merge) == 'undefined' ? false : merge; 
5208         
5209         this.scope.undoManager.transact({
5210             scope : this.scope,
5211             oldHTML: this.current_html,
5212             newHTML: this.scope.innerHTML,
5213             // nothing to execute (content already changed when input is fired)
5214             execute: function() { },
5215             undo: function() {
5216                 this.scope.innerHTML = this.current_html = this.oldHTML;
5217             },
5218             redo: function() {
5219                 this.scope.innerHTML = this.current_html = this.newHTML;
5220             }
5221         }, false); //merge);
5222         
5223         this.merge = merge;
5224         
5225         this.current_html = this.scope.innerHTML;
5226     }
5227     
5228     
5229      
5230     
5231     
5232     
5233 };
5234 /**
5235  * @class Roo.lib.Range
5236  * @constructor
5237  * This is a toolkit, normally used to copy features into a Dom Range element
5238  * Roo.lib.Range.wrap(x);
5239  *
5240  *
5241  *
5242  */
5243 Roo.lib.Range = function() { };
5244
5245 /**
5246  * Wrap a Dom Range object, to give it new features...
5247  * @static
5248  * @param {Range} the range to wrap
5249  */
5250 Roo.lib.Range.wrap = function(r) {
5251     return Roo.apply(r, Roo.lib.Range.prototype);
5252 };
5253 /**
5254  * find a parent node eg. LI / OL
5255  * @param {string|Array} node name or array of nodenames
5256  * @return {DomElement|false}
5257  */
5258 Roo.apply(Roo.lib.Range.prototype,
5259 {
5260     
5261     closest : function(str)
5262     {
5263         if (typeof(str) != 'string') {
5264             // assume it's a array.
5265             for(var i = 0;i < str.length;i++) {
5266                 var r = this.closest(str[i]);
5267                 if (r !== false) {
5268                     return r;
5269                 }
5270                 
5271             }
5272             return false;
5273         }
5274         str = str.toLowerCase();
5275         var n = this.commonAncestorContainer; // might not be a node
5276         while (n.nodeType != 1) {
5277             n = n.parentNode;
5278         }
5279         
5280         if (n.nodeName.toLowerCase() == str ) {
5281             return n;
5282         }
5283         if (n.nodeName.toLowerCase() == 'body') {
5284             return false;
5285         }
5286             
5287         return n.closest(str) || false;
5288         
5289     },
5290     cloneRange : function()
5291     {
5292         return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
5293     }
5294 });/**
5295  * @class Roo.lib.Selection
5296  * @constructor
5297  * This is a toolkit, normally used to copy features into a Dom Selection element
5298  * Roo.lib.Selection.wrap(x);
5299  *
5300  *
5301  *
5302  */
5303 Roo.lib.Selection = function() { };
5304
5305 /**
5306  * Wrap a Dom Range object, to give it new features...
5307  * @static
5308  * @param {Range} the range to wrap
5309  */
5310 Roo.lib.Selection.wrap = function(r, doc) {
5311     Roo.apply(r, Roo.lib.Selection.prototype);
5312     r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
5313     return r;
5314 };
5315 /**
5316  * find a parent node eg. LI / OL
5317  * @param {string|Array} node name or array of nodenames
5318  * @return {DomElement|false}
5319  */
5320 Roo.apply(Roo.lib.Selection.prototype,
5321 {
5322     /**
5323      * the owner document
5324      */
5325     ownerDocument : false,
5326     
5327     getRangeAt : function(n)
5328     {
5329         return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
5330     },
5331     
5332     /**
5333      * insert node at selection 
5334      * @param {DomElement|string} node
5335      * @param {string} cursor (after|in|none) where to place the cursor after inserting.
5336      */
5337     insertNode: function(node, cursor)
5338     {
5339         if (typeof(node) == 'string') {
5340             node = this.ownerDocument.createElement(node);
5341             if (cursor == 'in') {
5342                 node.innerHTML = '&nbsp;';
5343             }
5344         }
5345         
5346         var range = this.getRangeAt(0);
5347         
5348         if (this.type != 'Caret') {
5349             range.deleteContents();
5350         }
5351         var sn = node.childNodes[0]; // select the contents.
5352
5353         
5354         
5355         range.insertNode(node);
5356         if (cursor == 'after') {
5357             node.insertAdjacentHTML('afterend', '&nbsp;');
5358             sn = node.nextSibling;
5359         }
5360         
5361         if (cursor == 'none') {
5362             return;
5363         }
5364         
5365         this.cursorText(sn);
5366     },
5367     
5368     cursorText : function(n)
5369     {
5370        
5371         //var range = this.getRangeAt(0);
5372         range = Roo.lib.Range.wrap(new Range());
5373         //range.selectNode(n);
5374         
5375         var ix = Array.from(n.parentNode.childNodes).indexOf(n);
5376         range.setStart(n.parentNode,ix);
5377         range.setEnd(n.parentNode,ix+1);
5378         //range.collapse(false);
5379          
5380         this.removeAllRanges();
5381         this.addRange(range);
5382         
5383         Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
5384     },
5385     cursorAfter : function(n)
5386     {
5387         if (!n.nextSibling || n.nextSibling.nodeValue != '&nbsp;') {
5388             n.insertAdjacentHTML('afterend', '&nbsp;');
5389         }
5390         this.cursorText (n.nextSibling);
5391     }
5392         
5393     
5394 });/*
5395  * Based on:
5396  * Ext JS Library 1.1.1
5397  * Copyright(c) 2006-2007, Ext JS, LLC.
5398  *
5399  * Originally Released Under LGPL - original licence link has changed is not relivant.
5400  *
5401  * Fork - LGPL
5402  * <script type="text/javascript">
5403  */
5404
5405
5406 // nasty IE9 hack - what a pile of crap that is..
5407
5408  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5409     Range.prototype.createContextualFragment = function (html) {
5410         var doc = window.document;
5411         var container = doc.createElement("div");
5412         container.innerHTML = html;
5413         var frag = doc.createDocumentFragment(), n;
5414         while ((n = container.firstChild)) {
5415             frag.appendChild(n);
5416         }
5417         return frag;
5418     };
5419 }
5420
5421 /**
5422  * @class Roo.DomHelper
5423  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5424  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
5425  * @static
5426  */
5427 Roo.DomHelper = function(){
5428     var tempTableEl = null;
5429     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5430     var tableRe = /^table|tbody|tr|td$/i;
5431     var xmlns = {};
5432     // build as innerHTML where available
5433     /** @ignore */
5434     var createHtml = function(o){
5435         if(typeof o == 'string'){
5436             return o;
5437         }
5438         var b = "";
5439         if(!o.tag){
5440             o.tag = "div";
5441         }
5442         b += "<" + o.tag;
5443         for(var attr in o){
5444             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5445             if(attr == "style"){
5446                 var s = o["style"];
5447                 if(typeof s == "function"){
5448                     s = s.call();
5449                 }
5450                 if(typeof s == "string"){
5451                     b += ' style="' + s + '"';
5452                 }else if(typeof s == "object"){
5453                     b += ' style="';
5454                     for(var key in s){
5455                         if(typeof s[key] != "function"){
5456                             b += key + ":" + s[key] + ";";
5457                         }
5458                     }
5459                     b += '"';
5460                 }
5461             }else{
5462                 if(attr == "cls"){
5463                     b += ' class="' + o["cls"] + '"';
5464                 }else if(attr == "htmlFor"){
5465                     b += ' for="' + o["htmlFor"] + '"';
5466                 }else{
5467                     b += " " + attr + '="' + o[attr] + '"';
5468                 }
5469             }
5470         }
5471         if(emptyTags.test(o.tag)){
5472             b += "/>";
5473         }else{
5474             b += ">";
5475             var cn = o.children || o.cn;
5476             if(cn){
5477                 //http://bugs.kde.org/show_bug.cgi?id=71506
5478                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5479                     for(var i = 0, len = cn.length; i < len; i++) {
5480                         b += createHtml(cn[i], b);
5481                     }
5482                 }else{
5483                     b += createHtml(cn, b);
5484                 }
5485             }
5486             if(o.html){
5487                 b += o.html;
5488             }
5489             b += "</" + o.tag + ">";
5490         }
5491         return b;
5492     };
5493
5494     // build as dom
5495     /** @ignore */
5496     var createDom = function(o, parentNode){
5497          
5498         // defininition craeted..
5499         var ns = false;
5500         if (o.ns && o.ns != 'html') {
5501                
5502             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5503                 xmlns[o.ns] = o.xmlns;
5504                 ns = o.xmlns;
5505             }
5506             if (typeof(xmlns[o.ns]) == 'undefined') {
5507                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5508             }
5509             ns = xmlns[o.ns];
5510         }
5511         
5512         
5513         if (typeof(o) == 'string') {
5514             return parentNode.appendChild(document.createTextNode(o));
5515         }
5516         o.tag = o.tag || div;
5517         if (o.ns && Roo.isIE) {
5518             ns = false;
5519             o.tag = o.ns + ':' + o.tag;
5520             
5521         }
5522         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5523         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5524         for(var attr in o){
5525             
5526             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5527                     attr == "style" || typeof o[attr] == "function") { continue; }
5528                     
5529             if(attr=="cls" && Roo.isIE){
5530                 el.className = o["cls"];
5531             }else{
5532                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5533                 else { 
5534                     el[attr] = o[attr];
5535                 }
5536             }
5537         }
5538         Roo.DomHelper.applyStyles(el, o.style);
5539         var cn = o.children || o.cn;
5540         if(cn){
5541             //http://bugs.kde.org/show_bug.cgi?id=71506
5542              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5543                 for(var i = 0, len = cn.length; i < len; i++) {
5544                     createDom(cn[i], el);
5545                 }
5546             }else{
5547                 createDom(cn, el);
5548             }
5549         }
5550         if(o.html){
5551             el.innerHTML = o.html;
5552         }
5553         if(parentNode){
5554            parentNode.appendChild(el);
5555         }
5556         return el;
5557     };
5558
5559     var ieTable = function(depth, s, h, e){
5560         tempTableEl.innerHTML = [s, h, e].join('');
5561         var i = -1, el = tempTableEl;
5562         while(++i < depth && el.firstChild){
5563             el = el.firstChild;
5564         }
5565         return el;
5566     };
5567
5568     // kill repeat to save bytes
5569     var ts = '<table>',
5570         te = '</table>',
5571         tbs = ts+'<tbody>',
5572         tbe = '</tbody>'+te,
5573         trs = tbs + '<tr>',
5574         tre = '</tr>'+tbe;
5575
5576     /**
5577      * @ignore
5578      * Nasty code for IE's broken table implementation
5579      */
5580     var insertIntoTable = function(tag, where, el, html){
5581         if(!tempTableEl){
5582             tempTableEl = document.createElement('div');
5583         }
5584         var node;
5585         var before = null;
5586         if(tag == 'td'){
5587             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5588                 return;
5589             }
5590             if(where == 'beforebegin'){
5591                 before = el;
5592                 el = el.parentNode;
5593             } else{
5594                 before = el.nextSibling;
5595                 el = el.parentNode;
5596             }
5597             node = ieTable(4, trs, html, tre);
5598         }
5599         else if(tag == 'tr'){
5600             if(where == 'beforebegin'){
5601                 before = el;
5602                 el = el.parentNode;
5603                 node = ieTable(3, tbs, html, tbe);
5604             } else if(where == 'afterend'){
5605                 before = el.nextSibling;
5606                 el = el.parentNode;
5607                 node = ieTable(3, tbs, html, tbe);
5608             } else{ // INTO a TR
5609                 if(where == 'afterbegin'){
5610                     before = el.firstChild;
5611                 }
5612                 node = ieTable(4, trs, html, tre);
5613             }
5614         } else if(tag == 'tbody'){
5615             if(where == 'beforebegin'){
5616                 before = el;
5617                 el = el.parentNode;
5618                 node = ieTable(2, ts, html, te);
5619             } else if(where == 'afterend'){
5620                 before = el.nextSibling;
5621                 el = el.parentNode;
5622                 node = ieTable(2, ts, html, te);
5623             } else{
5624                 if(where == 'afterbegin'){
5625                     before = el.firstChild;
5626                 }
5627                 node = ieTable(3, tbs, html, tbe);
5628             }
5629         } else{ // TABLE
5630             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5631                 return;
5632             }
5633             if(where == 'afterbegin'){
5634                 before = el.firstChild;
5635             }
5636             node = ieTable(2, ts, html, te);
5637         }
5638         el.insertBefore(node, before);
5639         return node;
5640     };
5641     
5642     // this is a bit like the react update code...
5643     // 
5644     
5645     var updateNode = function(from, to)
5646     {
5647         // should we handle non-standard elements?
5648         Roo.log(["UpdateNode" , from, to]);
5649         if (from.nodeType != to.nodeType) {
5650             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5651             from.parentNode.replaceChild(to, from);
5652         }
5653         
5654         if (from.nodeType == 3) {
5655             // assume it's text?!
5656             if (from.data == to.data) {
5657                 return;
5658             }
5659             from.data = to.data;
5660             return;
5661         }
5662         if (!from.parentNode) {
5663             // not sure why this is happening?
5664             return;
5665         }
5666         // assume 'to' doesnt have '1/3 nodetypes!
5667         // not sure why, by from, parent node might not exist?
5668         if (from.nodeType !=1 || from.tagName != to.tagName) {
5669             Roo.log(["ReplaceChild" , from, to ]);
5670             
5671             from.parentNode.replaceChild(to, from);
5672             return;
5673         }
5674         // compare attributes
5675         var ar = Array.from(from.attributes);
5676         for(var i = 0; i< ar.length;i++) {
5677             if (to.hasAttribute(ar[i].name)) {
5678                 continue;
5679             }
5680             if (ar[i].name == 'id') { // always keep ids?
5681                continue;
5682             }
5683             //if (ar[i].name == 'style') {
5684             //   throw "style removed?";
5685             //}
5686             Roo.log("removeAttribute" + ar[i].name);
5687             from.removeAttribute(ar[i].name);
5688         }
5689         ar = to.attributes;
5690         for(var i = 0; i< ar.length;i++) {
5691             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5692                 Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
5693                 continue;
5694             }
5695             Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5696             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5697         }
5698         // children
5699         var far = Array.from(from.childNodes);
5700         var tar = Array.from(to.childNodes);
5701         // if the lengths are different.. then it's probably a editable content change, rather than
5702         // a change of the block definition..
5703         
5704         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5705          /*if (from.innerHTML == to.innerHTML) {
5706             return;
5707         }
5708         if (far.length != tar.length) {
5709             from.innerHTML = to.innerHTML;
5710             return;
5711         }
5712         */
5713         
5714         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5715             if (i >= far.length) {
5716                 from.appendChild(tar[i]);
5717                 Roo.log(["add", tar[i]]);
5718                 
5719             } else if ( i  >= tar.length) {
5720                 from.removeChild(far[i]);
5721                 Roo.log(["remove", far[i]]);
5722             } else {
5723                 
5724                 updateNode(far[i], tar[i]);
5725             }    
5726         }
5727         
5728         
5729         
5730         
5731     };
5732     
5733     
5734
5735     return {
5736         /** True to force the use of DOM instead of html fragments @type Boolean */
5737         useDom : false,
5738     
5739         /**
5740          * Returns the markup for the passed Element(s) config
5741          * @param {Object} o The Dom object spec (and children)
5742          * @return {String}
5743          */
5744         markup : function(o){
5745             return createHtml(o);
5746         },
5747     
5748         /**
5749          * Applies a style specification to an element
5750          * @param {String/HTMLElement} el The element to apply styles to
5751          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5752          * a function which returns such a specification.
5753          */
5754         applyStyles : function(el, styles){
5755             if(styles){
5756                el = Roo.fly(el);
5757                if(typeof styles == "string"){
5758                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5759                    var matches;
5760                    while ((matches = re.exec(styles)) != null){
5761                        el.setStyle(matches[1], matches[2]);
5762                    }
5763                }else if (typeof styles == "object"){
5764                    for (var style in styles){
5765                       el.setStyle(style, styles[style]);
5766                    }
5767                }else if (typeof styles == "function"){
5768                     Roo.DomHelper.applyStyles(el, styles.call());
5769                }
5770             }
5771         },
5772     
5773         /**
5774          * Inserts an HTML fragment into the Dom
5775          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5776          * @param {HTMLElement} el The context element
5777          * @param {String} html The HTML fragmenet
5778          * @return {HTMLElement} The new node
5779          */
5780         insertHtml : function(where, el, html){
5781             where = where.toLowerCase();
5782             if(el.insertAdjacentHTML){
5783                 if(tableRe.test(el.tagName)){
5784                     var rs;
5785                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5786                         return rs;
5787                     }
5788                 }
5789                 switch(where){
5790                     case "beforebegin":
5791                         el.insertAdjacentHTML('BeforeBegin', html);
5792                         return el.previousSibling;
5793                     case "afterbegin":
5794                         el.insertAdjacentHTML('AfterBegin', html);
5795                         return el.firstChild;
5796                     case "beforeend":
5797                         el.insertAdjacentHTML('BeforeEnd', html);
5798                         return el.lastChild;
5799                     case "afterend":
5800                         el.insertAdjacentHTML('AfterEnd', html);
5801                         return el.nextSibling;
5802                 }
5803                 throw 'Illegal insertion point -> "' + where + '"';
5804             }
5805             var range = el.ownerDocument.createRange();
5806             var frag;
5807             switch(where){
5808                  case "beforebegin":
5809                     range.setStartBefore(el);
5810                     frag = range.createContextualFragment(html);
5811                     el.parentNode.insertBefore(frag, el);
5812                     return el.previousSibling;
5813                  case "afterbegin":
5814                     if(el.firstChild){
5815                         range.setStartBefore(el.firstChild);
5816                         frag = range.createContextualFragment(html);
5817                         el.insertBefore(frag, el.firstChild);
5818                         return el.firstChild;
5819                     }else{
5820                         el.innerHTML = html;
5821                         return el.firstChild;
5822                     }
5823                 case "beforeend":
5824                     if(el.lastChild){
5825                         range.setStartAfter(el.lastChild);
5826                         frag = range.createContextualFragment(html);
5827                         el.appendChild(frag);
5828                         return el.lastChild;
5829                     }else{
5830                         el.innerHTML = html;
5831                         return el.lastChild;
5832                     }
5833                 case "afterend":
5834                     range.setStartAfter(el);
5835                     frag = range.createContextualFragment(html);
5836                     el.parentNode.insertBefore(frag, el.nextSibling);
5837                     return el.nextSibling;
5838                 }
5839                 throw 'Illegal insertion point -> "' + where + '"';
5840         },
5841     
5842         /**
5843          * Creates new Dom element(s) and inserts them before el
5844          * @param {String/HTMLElement/Element} el The context element
5845          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5846          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5847          * @return {HTMLElement/Roo.Element} The new node
5848          */
5849         insertBefore : function(el, o, returnElement){
5850             return this.doInsert(el, o, returnElement, "beforeBegin");
5851         },
5852     
5853         /**
5854          * Creates new Dom element(s) and inserts them after el
5855          * @param {String/HTMLElement/Element} el The context element
5856          * @param {Object} o The Dom object spec (and children)
5857          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5858          * @return {HTMLElement/Roo.Element} The new node
5859          */
5860         insertAfter : function(el, o, returnElement){
5861             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5862         },
5863     
5864         /**
5865          * Creates new Dom element(s) and inserts them as the first child of el
5866          * @param {String/HTMLElement/Element} el The context element
5867          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5868          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5869          * @return {HTMLElement/Roo.Element} The new node
5870          */
5871         insertFirst : function(el, o, returnElement){
5872             return this.doInsert(el, o, returnElement, "afterBegin");
5873         },
5874     
5875         // private
5876         doInsert : function(el, o, returnElement, pos, sibling){
5877             el = Roo.getDom(el);
5878             var newNode;
5879             if(this.useDom || o.ns){
5880                 newNode = createDom(o, null);
5881                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5882             }else{
5883                 var html = createHtml(o);
5884                 newNode = this.insertHtml(pos, el, html);
5885             }
5886             return returnElement ? Roo.get(newNode, true) : newNode;
5887         },
5888     
5889         /**
5890          * Creates new Dom element(s) and appends them to el
5891          * @param {String/HTMLElement/Element} el The context element
5892          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5893          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5894          * @return {HTMLElement/Roo.Element} The new node
5895          */
5896         append : function(el, o, returnElement){
5897             el = Roo.getDom(el);
5898             var newNode;
5899             if(this.useDom || o.ns){
5900                 newNode = createDom(o, null);
5901                 el.appendChild(newNode);
5902             }else{
5903                 var html = createHtml(o);
5904                 newNode = this.insertHtml("beforeEnd", el, html);
5905             }
5906             return returnElement ? Roo.get(newNode, true) : newNode;
5907         },
5908     
5909         /**
5910          * Creates new Dom element(s) and overwrites the contents of el with them
5911          * @param {String/HTMLElement/Element} el The context element
5912          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5913          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5914          * @return {HTMLElement/Roo.Element} The new node
5915          */
5916         overwrite : function(el, o, returnElement)
5917         {
5918             el = Roo.getDom(el);
5919             if (o.ns) {
5920               
5921                 while (el.childNodes.length) {
5922                     el.removeChild(el.firstChild);
5923                 }
5924                 createDom(o, el);
5925             } else {
5926                 el.innerHTML = createHtml(o);   
5927             }
5928             
5929             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5930         },
5931     
5932         /**
5933          * Creates a new Roo.DomHelper.Template from the Dom object spec
5934          * @param {Object} o The Dom object spec (and children)
5935          * @return {Roo.DomHelper.Template} The new template
5936          */
5937         createTemplate : function(o){
5938             var html = createHtml(o);
5939             return new Roo.Template(html);
5940         },
5941          /**
5942          * Updates the first element with the spec from the o (replacing if necessary)
5943          * This iterates through the children, and updates attributes / children etc..
5944          * @param {String/HTMLElement/Element} el The context element
5945          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5946          */
5947         
5948         update : function(el, o)
5949         {
5950             updateNode(Roo.getDom(el), createDom(o));
5951             
5952         }
5953         
5954         
5955     };
5956 }();
5957 /*
5958  * Based on:
5959  * Ext JS Library 1.1.1
5960  * Copyright(c) 2006-2007, Ext JS, LLC.
5961  *
5962  * Originally Released Under LGPL - original licence link has changed is not relivant.
5963  *
5964  * Fork - LGPL
5965  * <script type="text/javascript">
5966  */
5967  
5968 /**
5969 * @class Roo.Template
5970 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5971 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5972 * Usage:
5973 <pre><code>
5974 var t = new Roo.Template({
5975     html :  '&lt;div name="{id}"&gt;' + 
5976         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5977         '&lt;/div&gt;',
5978     myformat: function (value, allValues) {
5979         return 'XX' + value;
5980     }
5981 });
5982 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5983 </code></pre>
5984 * For more information see this blog post with examples:
5985 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5986      - Create Elements using DOM, HTML fragments and Templates</a>. 
5987 * @constructor
5988 * @param {Object} cfg - Configuration object.
5989 */
5990 Roo.Template = function(cfg){
5991     // BC!
5992     if(cfg instanceof Array){
5993         cfg = cfg.join("");
5994     }else if(arguments.length > 1){
5995         cfg = Array.prototype.join.call(arguments, "");
5996     }
5997     
5998     
5999     if (typeof(cfg) == 'object') {
6000         Roo.apply(this,cfg)
6001     } else {
6002         // bc
6003         this.html = cfg;
6004     }
6005     if (this.url) {
6006         this.load();
6007     }
6008     
6009 };
6010 Roo.Template.prototype = {
6011     
6012     /**
6013      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
6014      */
6015     onLoad : false,
6016     
6017     
6018     /**
6019      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
6020      *                    it should be fixed so that template is observable...
6021      */
6022     url : false,
6023     /**
6024      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
6025      */
6026     html : '',
6027     
6028     
6029     compiled : false,
6030     loaded : false,
6031     /**
6032      * Returns an HTML fragment of this template with the specified values applied.
6033      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6034      * @return {String} The HTML fragment
6035      */
6036     
6037    
6038     
6039     applyTemplate : function(values){
6040         //Roo.log(["applyTemplate", values]);
6041         try {
6042            
6043             if(this.compiled){
6044                 return this.compiled(values);
6045             }
6046             var useF = this.disableFormats !== true;
6047             var fm = Roo.util.Format, tpl = this;
6048             var fn = function(m, name, format, args){
6049                 if(format && useF){
6050                     if(format.substr(0, 5) == "this."){
6051                         return tpl.call(format.substr(5), values[name], values);
6052                     }else{
6053                         if(args){
6054                             // quoted values are required for strings in compiled templates, 
6055                             // but for non compiled we need to strip them
6056                             // quoted reversed for jsmin
6057                             var re = /^\s*['"](.*)["']\s*$/;
6058                             args = args.split(',');
6059                             for(var i = 0, len = args.length; i < len; i++){
6060                                 args[i] = args[i].replace(re, "$1");
6061                             }
6062                             args = [values[name]].concat(args);
6063                         }else{
6064                             args = [values[name]];
6065                         }
6066                         return fm[format].apply(fm, args);
6067                     }
6068                 }else{
6069                     return values[name] !== undefined ? values[name] : "";
6070                 }
6071             };
6072             return this.html.replace(this.re, fn);
6073         } catch (e) {
6074             Roo.log(e);
6075             throw e;
6076         }
6077          
6078     },
6079     
6080     loading : false,
6081       
6082     load : function ()
6083     {
6084          
6085         if (this.loading) {
6086             return;
6087         }
6088         var _t = this;
6089         
6090         this.loading = true;
6091         this.compiled = false;
6092         
6093         var cx = new Roo.data.Connection();
6094         cx.request({
6095             url : this.url,
6096             method : 'GET',
6097             success : function (response) {
6098                 _t.loading = false;
6099                 _t.url = false;
6100                 
6101                 _t.set(response.responseText,true);
6102                 _t.loaded = true;
6103                 if (_t.onLoad) {
6104                     _t.onLoad();
6105                 }
6106              },
6107             failure : function(response) {
6108                 Roo.log("Template failed to load from " + _t.url);
6109                 _t.loading = false;
6110             }
6111         });
6112     },
6113
6114     /**
6115      * Sets the HTML used as the template and optionally compiles it.
6116      * @param {String} html
6117      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
6118      * @return {Roo.Template} this
6119      */
6120     set : function(html, compile){
6121         this.html = html;
6122         this.compiled = false;
6123         if(compile){
6124             this.compile();
6125         }
6126         return this;
6127     },
6128     
6129     /**
6130      * True to disable format functions (defaults to false)
6131      * @type Boolean
6132      */
6133     disableFormats : false,
6134     
6135     /**
6136     * The regular expression used to match template variables 
6137     * @type RegExp
6138     * @property 
6139     */
6140     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
6141     
6142     /**
6143      * Compiles the template into an internal function, eliminating the RegEx overhead.
6144      * @return {Roo.Template} this
6145      */
6146     compile : function(){
6147         var fm = Roo.util.Format;
6148         var useF = this.disableFormats !== true;
6149         var sep = Roo.isGecko ? "+" : ",";
6150         var fn = function(m, name, format, args){
6151             if(format && useF){
6152                 args = args ? ',' + args : "";
6153                 if(format.substr(0, 5) != "this."){
6154                     format = "fm." + format + '(';
6155                 }else{
6156                     format = 'this.call("'+ format.substr(5) + '", ';
6157                     args = ", values";
6158                 }
6159             }else{
6160                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
6161             }
6162             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
6163         };
6164         var body;
6165         // branched to use + in gecko and [].join() in others
6166         if(Roo.isGecko){
6167             body = "this.compiled = function(values){ return '" +
6168                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
6169                     "';};";
6170         }else{
6171             body = ["this.compiled = function(values){ return ['"];
6172             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
6173             body.push("'].join('');};");
6174             body = body.join('');
6175         }
6176         /**
6177          * eval:var:values
6178          * eval:var:fm
6179          */
6180         eval(body);
6181         return this;
6182     },
6183     
6184     // private function used to call members
6185     call : function(fnName, value, allValues){
6186         return this[fnName](value, allValues);
6187     },
6188     
6189     /**
6190      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6191      * @param {String/HTMLElement/Roo.Element} el The context element
6192      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6193      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6194      * @return {HTMLElement/Roo.Element} The new node or Element
6195      */
6196     insertFirst: function(el, values, returnElement){
6197         return this.doInsert('afterBegin', el, values, returnElement);
6198     },
6199
6200     /**
6201      * Applies the supplied values to the template and inserts the new node(s) before el.
6202      * @param {String/HTMLElement/Roo.Element} el The context element
6203      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6204      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6205      * @return {HTMLElement/Roo.Element} The new node or Element
6206      */
6207     insertBefore: function(el, values, returnElement){
6208         return this.doInsert('beforeBegin', el, values, returnElement);
6209     },
6210
6211     /**
6212      * Applies the supplied values to the template and inserts the new node(s) after el.
6213      * @param {String/HTMLElement/Roo.Element} el The context element
6214      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6215      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6216      * @return {HTMLElement/Roo.Element} The new node or Element
6217      */
6218     insertAfter : function(el, values, returnElement){
6219         return this.doInsert('afterEnd', el, values, returnElement);
6220     },
6221     
6222     /**
6223      * Applies the supplied values to the template and appends the new node(s) to el.
6224      * @param {String/HTMLElement/Roo.Element} el The context element
6225      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6226      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6227      * @return {HTMLElement/Roo.Element} The new node or Element
6228      */
6229     append : function(el, values, returnElement){
6230         return this.doInsert('beforeEnd', el, values, returnElement);
6231     },
6232
6233     doInsert : function(where, el, values, returnEl){
6234         el = Roo.getDom(el);
6235         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6236         return returnEl ? Roo.get(newNode, true) : newNode;
6237     },
6238
6239     /**
6240      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6241      * @param {String/HTMLElement/Roo.Element} el The context element
6242      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
6243      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6244      * @return {HTMLElement/Roo.Element} The new node or Element
6245      */
6246     overwrite : function(el, values, returnElement){
6247         el = Roo.getDom(el);
6248         el.innerHTML = this.applyTemplate(values);
6249         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6250     }
6251 };
6252 /**
6253  * Alias for {@link #applyTemplate}
6254  * @method
6255  */
6256 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6257
6258 // backwards compat
6259 Roo.DomHelper.Template = Roo.Template;
6260
6261 /**
6262  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6263  * @param {String/HTMLElement} el A DOM element or its id
6264  * @returns {Roo.Template} The created template
6265  * @static
6266  */
6267 Roo.Template.from = function(el){
6268     el = Roo.getDom(el);
6269     return new Roo.Template(el.value || el.innerHTML);
6270 };/*
6271  * Based on:
6272  * Ext JS Library 1.1.1
6273  * Copyright(c) 2006-2007, Ext JS, LLC.
6274  *
6275  * Originally Released Under LGPL - original licence link has changed is not relivant.
6276  *
6277  * Fork - LGPL
6278  * <script type="text/javascript">
6279  */
6280  
6281
6282 /*
6283  * This is code is also distributed under MIT license for use
6284  * with jQuery and prototype JavaScript libraries.
6285  */
6286 /**
6287  * @class Roo.DomQuery
6288 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
6289 <p>
6290 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
6291
6292 <p>
6293 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
6294 </p>
6295 <h4>Element Selectors:</h4>
6296 <ul class="list">
6297     <li> <b>*</b> any element</li>
6298     <li> <b>E</b> an element with the tag E</li>
6299     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6300     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6301     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6302     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6303 </ul>
6304 <h4>Attribute Selectors:</h4>
6305 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6306 <ul class="list">
6307     <li> <b>E[foo]</b> has an attribute "foo"</li>
6308     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6309     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6310     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6311     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6312     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6313     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6314 </ul>
6315 <h4>Pseudo Classes:</h4>
6316 <ul class="list">
6317     <li> <b>E:first-child</b> E is the first child of its parent</li>
6318     <li> <b>E:last-child</b> E is the last child of its parent</li>
6319     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
6320     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6321     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6322     <li> <b>E:only-child</b> E is the only child of its parent</li>
6323     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
6324     <li> <b>E:first</b> the first E in the resultset</li>
6325     <li> <b>E:last</b> the last E in the resultset</li>
6326     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6327     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6328     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6329     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6330     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6331     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6332     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6333     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6334     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6335 </ul>
6336 <h4>CSS Value Selectors:</h4>
6337 <ul class="list">
6338     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6339     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6340     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6341     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6342     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6343     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6344 </ul>
6345  * @static
6346  */
6347 Roo.DomQuery = function(){
6348     var cache = {}, simpleCache = {}, valueCache = {};
6349     var nonSpace = /\S/;
6350     var trimRe = /^\s+|\s+$/g;
6351     var tplRe = /\{(\d+)\}/g;
6352     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6353     var tagTokenRe = /^(#)?([\w-\*]+)/;
6354     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6355
6356     function child(p, index){
6357         var i = 0;
6358         var n = p.firstChild;
6359         while(n){
6360             if(n.nodeType == 1){
6361                if(++i == index){
6362                    return n;
6363                }
6364             }
6365             n = n.nextSibling;
6366         }
6367         return null;
6368     };
6369
6370     function next(n){
6371         while((n = n.nextSibling) && n.nodeType != 1);
6372         return n;
6373     };
6374
6375     function prev(n){
6376         while((n = n.previousSibling) && n.nodeType != 1);
6377         return n;
6378     };
6379
6380     function children(d){
6381         var n = d.firstChild, ni = -1;
6382             while(n){
6383                 var nx = n.nextSibling;
6384                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6385                     d.removeChild(n);
6386                 }else{
6387                     n.nodeIndex = ++ni;
6388                 }
6389                 n = nx;
6390             }
6391             return this;
6392         };
6393
6394     function byClassName(c, a, v){
6395         if(!v){
6396             return c;
6397         }
6398         var r = [], ri = -1, cn;
6399         for(var i = 0, ci; ci = c[i]; i++){
6400             
6401             
6402             if((' '+
6403                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6404                  +' ').indexOf(v) != -1){
6405                 r[++ri] = ci;
6406             }
6407         }
6408         return r;
6409     };
6410
6411     function attrValue(n, attr){
6412         if(!n.tagName && typeof n.length != "undefined"){
6413             n = n[0];
6414         }
6415         if(!n){
6416             return null;
6417         }
6418         if(attr == "for"){
6419             return n.htmlFor;
6420         }
6421         if(attr == "class" || attr == "className"){
6422             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6423         }
6424         return n.getAttribute(attr) || n[attr];
6425
6426     };
6427
6428     function getNodes(ns, mode, tagName){
6429         var result = [], ri = -1, cs;
6430         if(!ns){
6431             return result;
6432         }
6433         tagName = tagName || "*";
6434         if(typeof ns.getElementsByTagName != "undefined"){
6435             ns = [ns];
6436         }
6437         if(!mode){
6438             for(var i = 0, ni; ni = ns[i]; i++){
6439                 cs = ni.getElementsByTagName(tagName);
6440                 for(var j = 0, ci; ci = cs[j]; j++){
6441                     result[++ri] = ci;
6442                 }
6443             }
6444         }else if(mode == "/" || mode == ">"){
6445             var utag = tagName.toUpperCase();
6446             for(var i = 0, ni, cn; ni = ns[i]; i++){
6447                 cn = ni.children || ni.childNodes;
6448                 for(var j = 0, cj; cj = cn[j]; j++){
6449                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6450                         result[++ri] = cj;
6451                     }
6452                 }
6453             }
6454         }else if(mode == "+"){
6455             var utag = tagName.toUpperCase();
6456             for(var i = 0, n; n = ns[i]; i++){
6457                 while((n = n.nextSibling) && n.nodeType != 1);
6458                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6459                     result[++ri] = n;
6460                 }
6461             }
6462         }else if(mode == "~"){
6463             for(var i = 0, n; n = ns[i]; i++){
6464                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6465                 if(n){
6466                     result[++ri] = n;
6467                 }
6468             }
6469         }
6470         return result;
6471     };
6472
6473     function concat(a, b){
6474         if(b.slice){
6475             return a.concat(b);
6476         }
6477         for(var i = 0, l = b.length; i < l; i++){
6478             a[a.length] = b[i];
6479         }
6480         return a;
6481     }
6482
6483     function byTag(cs, tagName){
6484         if(cs.tagName || cs == document){
6485             cs = [cs];
6486         }
6487         if(!tagName){
6488             return cs;
6489         }
6490         var r = [], ri = -1;
6491         tagName = tagName.toLowerCase();
6492         for(var i = 0, ci; ci = cs[i]; i++){
6493             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6494                 r[++ri] = ci;
6495             }
6496         }
6497         return r;
6498     };
6499
6500     function byId(cs, attr, id){
6501         if(cs.tagName || cs == document){
6502             cs = [cs];
6503         }
6504         if(!id){
6505             return cs;
6506         }
6507         var r = [], ri = -1;
6508         for(var i = 0,ci; ci = cs[i]; i++){
6509             if(ci && ci.id == id){
6510                 r[++ri] = ci;
6511                 return r;
6512             }
6513         }
6514         return r;
6515     };
6516
6517     function byAttribute(cs, attr, value, op, custom){
6518         var r = [], ri = -1, st = custom=="{";
6519         var f = Roo.DomQuery.operators[op];
6520         for(var i = 0, ci; ci = cs[i]; i++){
6521             var a;
6522             if(st){
6523                 a = Roo.DomQuery.getStyle(ci, attr);
6524             }
6525             else if(attr == "class" || attr == "className"){
6526                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6527             }else if(attr == "for"){
6528                 a = ci.htmlFor;
6529             }else if(attr == "href"){
6530                 a = ci.getAttribute("href", 2);
6531             }else{
6532                 a = ci.getAttribute(attr);
6533             }
6534             if((f && f(a, value)) || (!f && a)){
6535                 r[++ri] = ci;
6536             }
6537         }
6538         return r;
6539     };
6540
6541     function byPseudo(cs, name, value){
6542         return Roo.DomQuery.pseudos[name](cs, value);
6543     };
6544
6545     // This is for IE MSXML which does not support expandos.
6546     // IE runs the same speed using setAttribute, however FF slows way down
6547     // and Safari completely fails so they need to continue to use expandos.
6548     var isIE = window.ActiveXObject ? true : false;
6549
6550     // this eval is stop the compressor from
6551     // renaming the variable to something shorter
6552     
6553     /** eval:var:batch */
6554     var batch = 30803; 
6555
6556     var key = 30803;
6557
6558     function nodupIEXml(cs){
6559         var d = ++key;
6560         cs[0].setAttribute("_nodup", d);
6561         var r = [cs[0]];
6562         for(var i = 1, len = cs.length; i < len; i++){
6563             var c = cs[i];
6564             if(!c.getAttribute("_nodup") != d){
6565                 c.setAttribute("_nodup", d);
6566                 r[r.length] = c;
6567             }
6568         }
6569         for(var i = 0, len = cs.length; i < len; i++){
6570             cs[i].removeAttribute("_nodup");
6571         }
6572         return r;
6573     }
6574
6575     function nodup(cs){
6576         if(!cs){
6577             return [];
6578         }
6579         var len = cs.length, c, i, r = cs, cj, ri = -1;
6580         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6581             return cs;
6582         }
6583         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6584             return nodupIEXml(cs);
6585         }
6586         var d = ++key;
6587         cs[0]._nodup = d;
6588         for(i = 1; c = cs[i]; i++){
6589             if(c._nodup != d){
6590                 c._nodup = d;
6591             }else{
6592                 r = [];
6593                 for(var j = 0; j < i; j++){
6594                     r[++ri] = cs[j];
6595                 }
6596                 for(j = i+1; cj = cs[j]; j++){
6597                     if(cj._nodup != d){
6598                         cj._nodup = d;
6599                         r[++ri] = cj;
6600                     }
6601                 }
6602                 return r;
6603             }
6604         }
6605         return r;
6606     }
6607
6608     function quickDiffIEXml(c1, c2){
6609         var d = ++key;
6610         for(var i = 0, len = c1.length; i < len; i++){
6611             c1[i].setAttribute("_qdiff", d);
6612         }
6613         var r = [];
6614         for(var i = 0, len = c2.length; i < len; i++){
6615             if(c2[i].getAttribute("_qdiff") != d){
6616                 r[r.length] = c2[i];
6617             }
6618         }
6619         for(var i = 0, len = c1.length; i < len; i++){
6620            c1[i].removeAttribute("_qdiff");
6621         }
6622         return r;
6623     }
6624
6625     function quickDiff(c1, c2){
6626         var len1 = c1.length;
6627         if(!len1){
6628             return c2;
6629         }
6630         if(isIE && c1[0].selectSingleNode){
6631             return quickDiffIEXml(c1, c2);
6632         }
6633         var d = ++key;
6634         for(var i = 0; i < len1; i++){
6635             c1[i]._qdiff = d;
6636         }
6637         var r = [];
6638         for(var i = 0, len = c2.length; i < len; i++){
6639             if(c2[i]._qdiff != d){
6640                 r[r.length] = c2[i];
6641             }
6642         }
6643         return r;
6644     }
6645
6646     function quickId(ns, mode, root, id){
6647         if(ns == root){
6648            var d = root.ownerDocument || root;
6649            return d.getElementById(id);
6650         }
6651         ns = getNodes(ns, mode, "*");
6652         return byId(ns, null, id);
6653     }
6654
6655     return {
6656         getStyle : function(el, name){
6657             return Roo.fly(el).getStyle(name);
6658         },
6659         /**
6660          * Compiles a selector/xpath query into a reusable function. The returned function
6661          * takes one parameter "root" (optional), which is the context node from where the query should start.
6662          * @param {String} selector The selector/xpath query
6663          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6664          * @return {Function}
6665          */
6666         compile : function(path, type){
6667             type = type || "select";
6668             
6669             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6670             var q = path, mode, lq;
6671             var tk = Roo.DomQuery.matchers;
6672             var tklen = tk.length;
6673             var mm;
6674
6675             // accept leading mode switch
6676             var lmode = q.match(modeRe);
6677             if(lmode && lmode[1]){
6678                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6679                 q = q.replace(lmode[1], "");
6680             }
6681             // strip leading slashes
6682             while(path.substr(0, 1)=="/"){
6683                 path = path.substr(1);
6684             }
6685
6686             while(q && lq != q){
6687                 lq = q;
6688                 var tm = q.match(tagTokenRe);
6689                 if(type == "select"){
6690                     if(tm){
6691                         if(tm[1] == "#"){
6692                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6693                         }else{
6694                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6695                         }
6696                         q = q.replace(tm[0], "");
6697                     }else if(q.substr(0, 1) != '@'){
6698                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6699                     }
6700                 }else{
6701                     if(tm){
6702                         if(tm[1] == "#"){
6703                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6704                         }else{
6705                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6706                         }
6707                         q = q.replace(tm[0], "");
6708                     }
6709                 }
6710                 while(!(mm = q.match(modeRe))){
6711                     var matched = false;
6712                     for(var j = 0; j < tklen; j++){
6713                         var t = tk[j];
6714                         var m = q.match(t.re);
6715                         if(m){
6716                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6717                                                     return m[i];
6718                                                 });
6719                             q = q.replace(m[0], "");
6720                             matched = true;
6721                             break;
6722                         }
6723                     }
6724                     // prevent infinite loop on bad selector
6725                     if(!matched){
6726                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6727                     }
6728                 }
6729                 if(mm[1]){
6730                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6731                     q = q.replace(mm[1], "");
6732                 }
6733             }
6734             fn[fn.length] = "return nodup(n);\n}";
6735             
6736              /** 
6737               * list of variables that need from compression as they are used by eval.
6738              *  eval:var:batch 
6739              *  eval:var:nodup
6740              *  eval:var:byTag
6741              *  eval:var:ById
6742              *  eval:var:getNodes
6743              *  eval:var:quickId
6744              *  eval:var:mode
6745              *  eval:var:root
6746              *  eval:var:n
6747              *  eval:var:byClassName
6748              *  eval:var:byPseudo
6749              *  eval:var:byAttribute
6750              *  eval:var:attrValue
6751              * 
6752              **/ 
6753             eval(fn.join(""));
6754             return f;
6755         },
6756
6757         /**
6758          * Selects a group of elements.
6759          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6760          * @param {Node} root (optional) The start of the query (defaults to document).
6761          * @return {Array}
6762          */
6763         select : function(path, root, type){
6764             if(!root || root == document){
6765                 root = document;
6766             }
6767             if(typeof root == "string"){
6768                 root = document.getElementById(root);
6769             }
6770             var paths = path.split(",");
6771             var results = [];
6772             for(var i = 0, len = paths.length; i < len; i++){
6773                 var p = paths[i].replace(trimRe, "");
6774                 if(!cache[p]){
6775                     cache[p] = Roo.DomQuery.compile(p);
6776                     if(!cache[p]){
6777                         throw p + " is not a valid selector";
6778                     }
6779                 }
6780                 var result = cache[p](root);
6781                 if(result && result != document){
6782                     results = results.concat(result);
6783                 }
6784             }
6785             if(paths.length > 1){
6786                 return nodup(results);
6787             }
6788             return results;
6789         },
6790
6791         /**
6792          * Selects a single element.
6793          * @param {String} selector The selector/xpath query
6794          * @param {Node} root (optional) The start of the query (defaults to document).
6795          * @return {Element}
6796          */
6797         selectNode : function(path, root){
6798             return Roo.DomQuery.select(path, root)[0];
6799         },
6800
6801         /**
6802          * Selects the value of a node, optionally replacing null with the defaultValue.
6803          * @param {String} selector The selector/xpath query
6804          * @param {Node} root (optional) The start of the query (defaults to document).
6805          * @param {String} defaultValue
6806          */
6807         selectValue : function(path, root, defaultValue){
6808             path = path.replace(trimRe, "");
6809             if(!valueCache[path]){
6810                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6811             }
6812             var n = valueCache[path](root);
6813             n = n[0] ? n[0] : n;
6814             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6815             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6816         },
6817
6818         /**
6819          * Selects the value of a node, parsing integers and floats.
6820          * @param {String} selector The selector/xpath query
6821          * @param {Node} root (optional) The start of the query (defaults to document).
6822          * @param {Number} defaultValue
6823          * @return {Number}
6824          */
6825         selectNumber : function(path, root, defaultValue){
6826             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6827             return parseFloat(v);
6828         },
6829
6830         /**
6831          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6832          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6833          * @param {String} selector The simple selector to test
6834          * @return {Boolean}
6835          */
6836         is : function(el, ss){
6837             if(typeof el == "string"){
6838                 el = document.getElementById(el);
6839             }
6840             var isArray = (el instanceof Array);
6841             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6842             return isArray ? (result.length == el.length) : (result.length > 0);
6843         },
6844
6845         /**
6846          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6847          * @param {Array} el An array of elements to filter
6848          * @param {String} selector The simple selector to test
6849          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6850          * the selector instead of the ones that match
6851          * @return {Array}
6852          */
6853         filter : function(els, ss, nonMatches){
6854             ss = ss.replace(trimRe, "");
6855             if(!simpleCache[ss]){
6856                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6857             }
6858             var result = simpleCache[ss](els);
6859             return nonMatches ? quickDiff(result, els) : result;
6860         },
6861
6862         /**
6863          * Collection of matching regular expressions and code snippets.
6864          */
6865         matchers : [{
6866                 re: /^\.([\w-]+)/,
6867                 select: 'n = byClassName(n, null, " {1} ");'
6868             }, {
6869                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6870                 select: 'n = byPseudo(n, "{1}", "{2}");'
6871             },{
6872                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6873                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6874             }, {
6875                 re: /^#([\w-]+)/,
6876                 select: 'n = byId(n, null, "{1}");'
6877             },{
6878                 re: /^@([\w-]+)/,
6879                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6880             }
6881         ],
6882
6883         /**
6884          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6885          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
6886          */
6887         operators : {
6888             "=" : function(a, v){
6889                 return a == v;
6890             },
6891             "!=" : function(a, v){
6892                 return a != v;
6893             },
6894             "^=" : function(a, v){
6895                 return a && a.substr(0, v.length) == v;
6896             },
6897             "$=" : function(a, v){
6898                 return a && a.substr(a.length-v.length) == v;
6899             },
6900             "*=" : function(a, v){
6901                 return a && a.indexOf(v) !== -1;
6902             },
6903             "%=" : function(a, v){
6904                 return (a % v) == 0;
6905             },
6906             "|=" : function(a, v){
6907                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6908             },
6909             "~=" : function(a, v){
6910                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6911             }
6912         },
6913
6914         /**
6915          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6916          * and the argument (if any) supplied in the selector.
6917          */
6918         pseudos : {
6919             "first-child" : function(c){
6920                 var r = [], ri = -1, n;
6921                 for(var i = 0, ci; ci = n = c[i]; i++){
6922                     while((n = n.previousSibling) && n.nodeType != 1);
6923                     if(!n){
6924                         r[++ri] = ci;
6925                     }
6926                 }
6927                 return r;
6928             },
6929
6930             "last-child" : function(c){
6931                 var r = [], ri = -1, n;
6932                 for(var i = 0, ci; ci = n = c[i]; i++){
6933                     while((n = n.nextSibling) && n.nodeType != 1);
6934                     if(!n){
6935                         r[++ri] = ci;
6936                     }
6937                 }
6938                 return r;
6939             },
6940
6941             "nth-child" : function(c, a) {
6942                 var r = [], ri = -1;
6943                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6944                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6945                 for(var i = 0, n; n = c[i]; i++){
6946                     var pn = n.parentNode;
6947                     if (batch != pn._batch) {
6948                         var j = 0;
6949                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6950                             if(cn.nodeType == 1){
6951                                cn.nodeIndex = ++j;
6952                             }
6953                         }
6954                         pn._batch = batch;
6955                     }
6956                     if (f == 1) {
6957                         if (l == 0 || n.nodeIndex == l){
6958                             r[++ri] = n;
6959                         }
6960                     } else if ((n.nodeIndex + l) % f == 0){
6961                         r[++ri] = n;
6962                     }
6963                 }
6964
6965                 return r;
6966             },
6967
6968             "only-child" : function(c){
6969                 var r = [], ri = -1;;
6970                 for(var i = 0, ci; ci = c[i]; i++){
6971                     if(!prev(ci) && !next(ci)){
6972                         r[++ri] = ci;
6973                     }
6974                 }
6975                 return r;
6976             },
6977
6978             "empty" : function(c){
6979                 var r = [], ri = -1;
6980                 for(var i = 0, ci; ci = c[i]; i++){
6981                     var cns = ci.childNodes, j = 0, cn, empty = true;
6982                     while(cn = cns[j]){
6983                         ++j;
6984                         if(cn.nodeType == 1 || cn.nodeType == 3){
6985                             empty = false;
6986                             break;
6987                         }
6988                     }
6989                     if(empty){
6990                         r[++ri] = ci;
6991                     }
6992                 }
6993                 return r;
6994             },
6995
6996             "contains" : function(c, v){
6997                 var r = [], ri = -1;
6998                 for(var i = 0, ci; ci = c[i]; i++){
6999                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
7000                         r[++ri] = ci;
7001                     }
7002                 }
7003                 return r;
7004             },
7005
7006             "nodeValue" : function(c, v){
7007                 var r = [], ri = -1;
7008                 for(var i = 0, ci; ci = c[i]; i++){
7009                     if(ci.firstChild && ci.firstChild.nodeValue == v){
7010                         r[++ri] = ci;
7011                     }
7012                 }
7013                 return r;
7014             },
7015
7016             "checked" : function(c){
7017                 var r = [], ri = -1;
7018                 for(var i = 0, ci; ci = c[i]; i++){
7019                     if(ci.checked == true){
7020                         r[++ri] = ci;
7021                     }
7022                 }
7023                 return r;
7024             },
7025
7026             "not" : function(c, ss){
7027                 return Roo.DomQuery.filter(c, ss, true);
7028             },
7029
7030             "odd" : function(c){
7031                 return this["nth-child"](c, "odd");
7032             },
7033
7034             "even" : function(c){
7035                 return this["nth-child"](c, "even");
7036             },
7037
7038             "nth" : function(c, a){
7039                 return c[a-1] || [];
7040             },
7041
7042             "first" : function(c){
7043                 return c[0] || [];
7044             },
7045
7046             "last" : function(c){
7047                 return c[c.length-1] || [];
7048             },
7049
7050             "has" : function(c, ss){
7051                 var s = Roo.DomQuery.select;
7052                 var r = [], ri = -1;
7053                 for(var i = 0, ci; ci = c[i]; i++){
7054                     if(s(ss, ci).length > 0){
7055                         r[++ri] = ci;
7056                     }
7057                 }
7058                 return r;
7059             },
7060
7061             "next" : function(c, ss){
7062                 var is = Roo.DomQuery.is;
7063                 var r = [], ri = -1;
7064                 for(var i = 0, ci; ci = c[i]; i++){
7065                     var n = next(ci);
7066                     if(n && is(n, ss)){
7067                         r[++ri] = ci;
7068                     }
7069                 }
7070                 return r;
7071             },
7072
7073             "prev" : function(c, ss){
7074                 var is = Roo.DomQuery.is;
7075                 var r = [], ri = -1;
7076                 for(var i = 0, ci; ci = c[i]; i++){
7077                     var n = prev(ci);
7078                     if(n && is(n, ss)){
7079                         r[++ri] = ci;
7080                     }
7081                 }
7082                 return r;
7083             }
7084         }
7085     };
7086 }();
7087
7088 /**
7089  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
7090  * @param {String} path The selector/xpath query
7091  * @param {Node} root (optional) The start of the query (defaults to document).
7092  * @return {Array}
7093  * @member Roo
7094  * @method query
7095  */
7096 Roo.query = Roo.DomQuery.select;
7097 /*
7098  * Based on:
7099  * Ext JS Library 1.1.1
7100  * Copyright(c) 2006-2007, Ext JS, LLC.
7101  *
7102  * Originally Released Under LGPL - original licence link has changed is not relivant.
7103  *
7104  * Fork - LGPL
7105  * <script type="text/javascript">
7106  */
7107
7108 /**
7109  * @class Roo.util.Observable
7110  * Base class that provides a common interface for publishing events. Subclasses are expected to
7111  * to have a property "events" with all the events defined.<br>
7112  * For example:
7113  * <pre><code>
7114  Employee = function(name){
7115     this.name = name;
7116     this.addEvents({
7117         "fired" : true,
7118         "quit" : true
7119     });
7120  }
7121  Roo.extend(Employee, Roo.util.Observable);
7122 </code></pre>
7123  * @param {Object} config properties to use (incuding events / listeners)
7124  */
7125
7126 Roo.util.Observable = function(cfg){
7127     console.log("UTIL OBSERVABLE CONSTRUCTOR");
7128     
7129     cfg = cfg|| {};
7130     this.addEvents(cfg.events || {});
7131     if (cfg.events) {
7132         delete cfg.events; // make sure
7133     }
7134      
7135     Roo.apply(this, cfg);
7136     
7137     if(this.listeners){
7138         this.on(this.listeners);
7139         delete this.listeners;
7140     }
7141 };
7142 Roo.util.Observable.prototype = {
7143     /** 
7144  * @cfg {Object} listeners  list of events and functions to call for this object, 
7145  * For example :
7146  * <pre><code>
7147     listeners :  { 
7148        'click' : function(e) {
7149            ..... 
7150         } ,
7151         .... 
7152     } 
7153   </code></pre>
7154  */
7155     
7156     
7157     /**
7158      * Fires the specified event with the passed parameters (minus the event name).
7159      * @param {String} eventName
7160      * @param {Object...} args Variable number of parameters are passed to handlers
7161      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
7162      */
7163     fireEvent : function(){
7164         var ce = this.events[arguments[0].toLowerCase()];
7165         if(typeof ce == "object"){
7166             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
7167         }else{
7168             return true;
7169         }
7170     },
7171
7172     // private
7173     filterOptRe : /^(?:scope|delay|buffer|single)$/,
7174
7175     /**
7176      * Appends an event handler to this component
7177      * @param {String}   eventName The type of event to listen for
7178      * @param {Function} handler The method the event invokes
7179      * @param {Object}   scope (optional) The scope in which to execute the handler
7180      * function. The handler function's "this" context.
7181      * @param {Object}   options (optional) An object containing handler configuration
7182      * properties. This may contain any of the following properties:<ul>
7183      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7184      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7185      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7186      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7187      * by the specified number of milliseconds. If the event fires again within that time, the original
7188      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7189      * </ul><br>
7190      * <p>
7191      * <b>Combining Options</b><br>
7192      * Using the options argument, it is possible to combine different types of listeners:<br>
7193      * <br>
7194      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
7195                 <pre><code>
7196                 el.on('click', this.onClick, this, {
7197                         single: true,
7198                 delay: 100,
7199                 forumId: 4
7200                 });
7201                 </code></pre>
7202      * <p>
7203      * <b>Attaching multiple handlers in 1 call</b><br>
7204      * The method also allows for a single argument to be passed which is a config object containing properties
7205      * which specify multiple handlers.
7206      * <pre><code>
7207                 el.on({
7208                         'click': {
7209                         fn: this.onClick,
7210                         scope: this,
7211                         delay: 100
7212                 }, 
7213                 'mouseover': {
7214                         fn: this.onMouseOver,
7215                         scope: this
7216                 },
7217                 'mouseout': {
7218                         fn: this.onMouseOut,
7219                         scope: this
7220                 }
7221                 });
7222                 </code></pre>
7223      * <p>
7224      * Or a shorthand syntax which passes the same scope object to all handlers:
7225         <pre><code>
7226                 el.on({
7227                         'click': this.onClick,
7228                 'mouseover': this.onMouseOver,
7229                 'mouseout': this.onMouseOut,
7230                 scope: this
7231                 });
7232                 </code></pre>
7233      */
7234     addListener : function(eventName, fn, scope, o){
7235         if(typeof eventName == "object"){
7236             o = eventName;
7237             for(var e in o){
7238                 if(this.filterOptRe.test(e)){
7239                     continue;
7240                 }
7241                 if(typeof o[e] == "function"){
7242                     // shared options
7243                     this.addListener(e, o[e], o.scope,  o);
7244                 }else{
7245                     // individual options
7246                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7247                 }
7248             }
7249             return;
7250         }
7251         o = (!o || typeof o == "boolean") ? {} : o;
7252         eventName = eventName.toLowerCase();
7253         var ce = this.events[eventName] || true;
7254         if(typeof ce == "boolean"){
7255             ce = new Roo.util.Event(this, eventName);
7256             this.events[eventName] = ce;
7257         }
7258         ce.addListener(fn, scope, o);
7259     },
7260
7261     /**
7262      * Removes a listener
7263      * @param {String}   eventName     The type of event to listen for
7264      * @param {Function} handler        The handler to remove
7265      * @param {Object}   scope  (optional) The scope (this object) for the handler
7266      */
7267     removeListener : function(eventName, fn, scope){
7268         var ce = this.events[eventName.toLowerCase()];
7269         if(typeof ce == "object"){
7270             ce.removeListener(fn, scope);
7271         }
7272     },
7273
7274     /**
7275      * Removes all listeners for this object
7276      */
7277     purgeListeners : function(){
7278         for(var evt in this.events){
7279             if(typeof this.events[evt] == "object"){
7280                  this.events[evt].clearListeners();
7281             }
7282         }
7283     },
7284
7285     relayEvents : function(o, events){
7286         var createHandler = function(ename){
7287             return function(){
7288                  
7289                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7290             };
7291         };
7292         for(var i = 0, len = events.length; i < len; i++){
7293             var ename = events[i];
7294             if(!this.events[ename]){
7295                 this.events[ename] = true;
7296             };
7297             o.on(ename, createHandler(ename), this);
7298         }
7299     },
7300
7301     /**
7302      * Used to define events on this Observable
7303      * @param {Object} object The object with the events defined
7304      */
7305     addEvents : function(o){
7306         if(!this.events){
7307             this.events = {};
7308         }
7309         Roo.applyIf(this.events, o);
7310     },
7311
7312     /**
7313      * Checks to see if this object has any listeners for a specified event
7314      * @param {String} eventName The name of the event to check for
7315      * @return {Boolean} True if the event is being listened for, else false
7316      */
7317     hasListener : function(eventName){
7318         var e = this.events[eventName];
7319         return typeof e == "object" && e.listeners.length > 0;
7320     }
7321 };
7322 /**
7323  * Appends an event handler to this element (shorthand for addListener)
7324  * @param {String}   eventName     The type of event to listen for
7325  * @param {Function} handler        The method the event invokes
7326  * @param {Object}   scope (optional) The scope in which to execute the handler
7327  * function. The handler function's "this" context.
7328  * @param {Object}   options  (optional)
7329  * @method
7330  */
7331 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7332 /**
7333  * Removes a listener (shorthand for removeListener)
7334  * @param {String}   eventName     The type of event to listen for
7335  * @param {Function} handler        The handler to remove
7336  * @param {Object}   scope  (optional) The scope (this object) for the handler
7337  * @method
7338  */
7339 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7340
7341 /**
7342  * Starts capture on the specified Observable. All events will be passed
7343  * to the supplied function with the event name + standard signature of the event
7344  * <b>before</b> the event is fired. If the supplied function returns false,
7345  * the event will not fire.
7346  * @param {Observable} o The Observable to capture
7347  * @param {Function} fn The function to call
7348  * @param {Object} scope (optional) The scope (this object) for the fn
7349  * @static
7350  */
7351 Roo.util.Observable.capture = function(o, fn, scope){
7352     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7353 };
7354
7355 /**
7356  * Removes <b>all</b> added captures from the Observable.
7357  * @param {Observable} o The Observable to release
7358  * @static
7359  */
7360 Roo.util.Observable.releaseCapture = function(o){
7361     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7362 };
7363
7364 (function(){
7365
7366     var createBuffered = function(h, o, scope){
7367         var task = new Roo.util.DelayedTask();
7368         return function(){
7369             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7370         };
7371     };
7372
7373     var createSingle = function(h, e, fn, scope){
7374         return function(){
7375             e.removeListener(fn, scope);
7376             return h.apply(scope, arguments);
7377         };
7378     };
7379
7380     var createDelayed = function(h, o, scope){
7381         return function(){
7382             var args = Array.prototype.slice.call(arguments, 0);
7383             setTimeout(function(){
7384                 h.apply(scope, args);
7385             }, o.delay || 10);
7386         };
7387     };
7388
7389     Roo.util.Event = function(obj, name){
7390         this.name = name;
7391         this.obj = obj;
7392         this.listeners = [];
7393     };
7394
7395     Roo.util.Event.prototype = {
7396         addListener : function(fn, scope, options){
7397             var o = options || {};
7398             scope = scope || this.obj;
7399             if(!this.isListening(fn, scope)){
7400                 var l = {fn: fn, scope: scope, options: o};
7401                 var h = fn;
7402                 if(o.delay){
7403                     h = createDelayed(h, o, scope);
7404                 }
7405                 if(o.single){
7406                     h = createSingle(h, this, fn, scope);
7407                 }
7408                 if(o.buffer){
7409                     h = createBuffered(h, o, scope);
7410                 }
7411                 l.fireFn = h;
7412                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7413                     this.listeners.push(l);
7414                 }else{
7415                     this.listeners = this.listeners.slice(0);
7416                     this.listeners.push(l);
7417                 }
7418             }
7419         },
7420
7421         findListener : function(fn, scope){
7422             scope = scope || this.obj;
7423             var ls = this.listeners;
7424             for(var i = 0, len = ls.length; i < len; i++){
7425                 var l = ls[i];
7426                 if(l.fn == fn && l.scope == scope){
7427                     return i;
7428                 }
7429             }
7430             return -1;
7431         },
7432
7433         isListening : function(fn, scope){
7434             return this.findListener(fn, scope) != -1;
7435         },
7436
7437         removeListener : function(fn, scope){
7438             var index;
7439             if((index = this.findListener(fn, scope)) != -1){
7440                 if(!this.firing){
7441                     this.listeners.splice(index, 1);
7442                 }else{
7443                     this.listeners = this.listeners.slice(0);
7444                     this.listeners.splice(index, 1);
7445                 }
7446                 return true;
7447             }
7448             return false;
7449         },
7450
7451         clearListeners : function(){
7452             this.listeners = [];
7453         },
7454
7455         fire : function(){
7456             var ls = this.listeners, scope, len = ls.length;
7457             if(len > 0){
7458                 this.firing = true;
7459                 var args = Array.prototype.slice.call(arguments, 0);                
7460                 for(var i = 0; i < len; i++){
7461                     var l = ls[i];
7462                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7463                         this.firing = false;
7464                         return false;
7465                     }
7466                 }
7467                 this.firing = false;
7468             }
7469             return true;
7470         }
7471     };
7472 })();/*
7473  * RooJS Library 
7474  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7475  *
7476  * Licence LGPL 
7477  *
7478  */
7479  
7480 /**
7481  * @class Roo.Document
7482  * @extends Roo.util.Observable
7483  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7484  * 
7485  * @param {Object} config the methods and properties of the 'base' class for the application.
7486  * 
7487  *  Generic Page handler - implement this to start your app..
7488  * 
7489  * eg.
7490  *  MyProject = new Roo.Document({
7491         events : {
7492             'load' : true // your events..
7493         },
7494         listeners : {
7495             'ready' : function() {
7496                 // fired on Roo.onReady()
7497             }
7498         }
7499  * 
7500  */
7501 Roo.Document = function(cfg) {
7502      
7503     this.addEvents({ 
7504         'ready' : true
7505     });
7506     Roo.util.Observable.call(this,cfg);
7507     
7508     var _this = this;
7509     
7510     Roo.onReady(function() {
7511         _this.fireEvent('ready');
7512     },null,false);
7513     
7514     
7515 }
7516
7517 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7518  * Based on:
7519  * Ext JS Library 1.1.1
7520  * Copyright(c) 2006-2007, Ext JS, LLC.
7521  *
7522  * Originally Released Under LGPL - original licence link has changed is not relivant.
7523  *
7524  * Fork - LGPL
7525  * <script type="text/javascript">
7526  */
7527
7528 /**
7529  * @class Roo.EventManager
7530  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7531  * several useful events directly.
7532  * See {@link Roo.EventObject} for more details on normalized event objects.
7533  * @static
7534  */
7535 Roo.EventManager = function(){
7536     var docReadyEvent, docReadyProcId, docReadyState = false;
7537     var resizeEvent, resizeTask, textEvent, textSize;
7538     var E = Roo.lib.Event;
7539     var D = Roo.lib.Dom;
7540
7541     
7542     
7543
7544     var fireDocReady = function(){
7545         if(!docReadyState){
7546             docReadyState = true;
7547             Roo.isReady = true;
7548             if(docReadyProcId){
7549                 clearInterval(docReadyProcId);
7550             }
7551             if(Roo.isGecko || Roo.isOpera) {
7552                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7553             }
7554             if(Roo.isIE){
7555                 var defer = document.getElementById("ie-deferred-loader");
7556                 if(defer){
7557                     defer.onreadystatechange = null;
7558                     defer.parentNode.removeChild(defer);
7559                 }
7560             }
7561             if(docReadyEvent){
7562                 docReadyEvent.fire();
7563                 docReadyEvent.clearListeners();
7564             }
7565         }
7566     };
7567     
7568     var initDocReady = function(){
7569         docReadyEvent = new Roo.util.Event();
7570         if(Roo.isGecko || Roo.isOpera) {
7571             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7572         }else if(Roo.isIE){
7573             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7574             var defer = document.getElementById("ie-deferred-loader");
7575             defer.onreadystatechange = function(){
7576                 if(this.readyState == "complete"){
7577                     fireDocReady();
7578                 }
7579             };
7580         }else if(Roo.isSafari){ 
7581             docReadyProcId = setInterval(function(){
7582                 var rs = document.readyState;
7583                 if(rs == "complete") {
7584                     fireDocReady();     
7585                  }
7586             }, 10);
7587         }
7588         // no matter what, make sure it fires on load
7589         E.on(window, "load", fireDocReady);
7590     };
7591
7592     var createBuffered = function(h, o){
7593         var task = new Roo.util.DelayedTask(h);
7594         return function(e){
7595             // create new event object impl so new events don't wipe out properties
7596             e = new Roo.EventObjectImpl(e);
7597             task.delay(o.buffer, h, null, [e]);
7598         };
7599     };
7600
7601     var createSingle = function(h, el, ename, fn){
7602         return function(e){
7603             Roo.EventManager.removeListener(el, ename, fn);
7604             h(e);
7605         };
7606     };
7607
7608     var createDelayed = function(h, o){
7609         return function(e){
7610             // create new event object impl so new events don't wipe out properties
7611             e = new Roo.EventObjectImpl(e);
7612             setTimeout(function(){
7613                 h(e);
7614             }, o.delay || 10);
7615         };
7616     };
7617     var transitionEndVal = false;
7618     
7619     var transitionEnd = function()
7620     {
7621         if (transitionEndVal) {
7622             return transitionEndVal;
7623         }
7624         var el = document.createElement('div');
7625
7626         var transEndEventNames = {
7627             WebkitTransition : 'webkitTransitionEnd',
7628             MozTransition    : 'transitionend',
7629             OTransition      : 'oTransitionEnd otransitionend',
7630             transition       : 'transitionend'
7631         };
7632     
7633         for (var name in transEndEventNames) {
7634             if (el.style[name] !== undefined) {
7635                 transitionEndVal = transEndEventNames[name];
7636                 return  transitionEndVal ;
7637             }
7638         }
7639     }
7640     
7641   
7642
7643     var listen = function(element, ename, opt, fn, scope)
7644     {
7645         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7646         fn = fn || o.fn; scope = scope || o.scope;
7647         var el = Roo.getDom(element);
7648         
7649         
7650         if(!el){
7651             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7652         }
7653         
7654         if (ename == 'transitionend') {
7655             ename = transitionEnd();
7656         }
7657         var h = function(e){
7658             e = Roo.EventObject.setEvent(e);
7659             var t;
7660             if(o.delegate){
7661                 t = e.getTarget(o.delegate, el);
7662                 if(!t){
7663                     return;
7664                 }
7665             }else{
7666                 t = e.target;
7667             }
7668             if(o.stopEvent === true){
7669                 e.stopEvent();
7670             }
7671             if(o.preventDefault === true){
7672                e.preventDefault();
7673             }
7674             if(o.stopPropagation === true){
7675                 e.stopPropagation();
7676             }
7677
7678             if(o.normalized === false){
7679                 e = e.browserEvent;
7680             }
7681
7682             fn.call(scope || el, e, t, o);
7683         };
7684         if(o.delay){
7685             h = createDelayed(h, o);
7686         }
7687         if(o.single){
7688             h = createSingle(h, el, ename, fn);
7689         }
7690         if(o.buffer){
7691             h = createBuffered(h, o);
7692         }
7693         
7694         fn._handlers = fn._handlers || [];
7695         
7696         
7697         fn._handlers.push([Roo.id(el), ename, h]);
7698         
7699         
7700          
7701         E.on(el, ename, h); // this adds the actuall listener to the object..
7702         
7703         
7704         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7705             el.addEventListener("DOMMouseScroll", h, false);
7706             E.on(window, 'unload', function(){
7707                 el.removeEventListener("DOMMouseScroll", h, false);
7708             });
7709         }
7710         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7711             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7712         }
7713         return h;
7714     };
7715
7716     var stopListening = function(el, ename, fn){
7717         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7718         if(hds){
7719             for(var i = 0, len = hds.length; i < len; i++){
7720                 var h = hds[i];
7721                 if(h[0] == id && h[1] == ename){
7722                     hd = h[2];
7723                     hds.splice(i, 1);
7724                     break;
7725                 }
7726             }
7727         }
7728         E.un(el, ename, hd);
7729         el = Roo.getDom(el);
7730         if(ename == "mousewheel" && el.addEventListener){
7731             el.removeEventListener("DOMMouseScroll", hd, false);
7732         }
7733         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7734             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7735         }
7736     };
7737
7738     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7739     
7740     var pub = {
7741         
7742         
7743         /** 
7744          * Fix for doc tools
7745          * @scope Roo.EventManager
7746          */
7747         
7748         
7749         /** 
7750          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7751          * object with a Roo.EventObject
7752          * @param {Function} fn        The method the event invokes
7753          * @param {Object}   scope    An object that becomes the scope of the handler
7754          * @param {boolean}  override If true, the obj passed in becomes
7755          *                             the execution scope of the listener
7756          * @return {Function} The wrapped function
7757          * @deprecated
7758          */
7759         wrap : function(fn, scope, override){
7760             return function(e){
7761                 Roo.EventObject.setEvent(e);
7762                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7763             };
7764         },
7765         
7766         /**
7767      * Appends an event handler to an element (shorthand for addListener)
7768      * @param {String/HTMLElement}   element        The html element or id to assign the
7769      * @param {String}   eventName The type of event to listen for
7770      * @param {Function} handler The method the event invokes
7771      * @param {Object}   scope (optional) The scope in which to execute the handler
7772      * function. The handler function's "this" context.
7773      * @param {Object}   options (optional) An object containing handler configuration
7774      * properties. This may contain any of the following properties:<ul>
7775      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7776      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7777      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7778      * <li>preventDefault {Boolean} True to prevent the default action</li>
7779      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7780      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7781      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7782      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7783      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7784      * by the specified number of milliseconds. If the event fires again within that time, the original
7785      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7786      * </ul><br>
7787      * <p>
7788      * <b>Combining Options</b><br>
7789      * Using the options argument, it is possible to combine different types of listeners:<br>
7790      * <br>
7791      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7792      * Code:<pre><code>
7793 el.on('click', this.onClick, this, {
7794     single: true,
7795     delay: 100,
7796     stopEvent : true,
7797     forumId: 4
7798 });</code></pre>
7799      * <p>
7800      * <b>Attaching multiple handlers in 1 call</b><br>
7801       * The method also allows for a single argument to be passed which is a config object containing properties
7802      * which specify multiple handlers.
7803      * <p>
7804      * Code:<pre><code>
7805 el.on({
7806     'click' : {
7807         fn: this.onClick
7808         scope: this,
7809         delay: 100
7810     },
7811     'mouseover' : {
7812         fn: this.onMouseOver
7813         scope: this
7814     },
7815     'mouseout' : {
7816         fn: this.onMouseOut
7817         scope: this
7818     }
7819 });</code></pre>
7820      * <p>
7821      * Or a shorthand syntax:<br>
7822      * Code:<pre><code>
7823 el.on({
7824     'click' : this.onClick,
7825     'mouseover' : this.onMouseOver,
7826     'mouseout' : this.onMouseOut
7827     scope: this
7828 });</code></pre>
7829      */
7830         addListener : function(element, eventName, fn, scope, options){
7831             if(typeof eventName == "object"){
7832                 var o = eventName;
7833                 for(var e in o){
7834                     if(propRe.test(e)){
7835                         continue;
7836                     }
7837                     if(typeof o[e] == "function"){
7838                         // shared options
7839                         listen(element, e, o, o[e], o.scope);
7840                     }else{
7841                         // individual options
7842                         listen(element, e, o[e]);
7843                     }
7844                 }
7845                 return;
7846             }
7847             return listen(element, eventName, options, fn, scope);
7848         },
7849         
7850         /**
7851          * Removes an event handler
7852          *
7853          * @param {String/HTMLElement}   element        The id or html element to remove the 
7854          *                             event from
7855          * @param {String}   eventName     The type of event
7856          * @param {Function} fn
7857          * @return {Boolean} True if a listener was actually removed
7858          */
7859         removeListener : function(element, eventName, fn){
7860             return stopListening(element, eventName, fn);
7861         },
7862         
7863         /**
7864          * Fires when the document is ready (before onload and before images are loaded). Can be 
7865          * accessed shorthanded Roo.onReady().
7866          * @param {Function} fn        The method the event invokes
7867          * @param {Object}   scope    An  object that becomes the scope of the handler
7868          * @param {boolean}  options
7869          */
7870         onDocumentReady : function(fn, scope, options){
7871             if(docReadyState){ // if it already fired
7872                 docReadyEvent.addListener(fn, scope, options);
7873                 docReadyEvent.fire();
7874                 docReadyEvent.clearListeners();
7875                 return;
7876             }
7877             if(!docReadyEvent){
7878                 initDocReady();
7879             }
7880             docReadyEvent.addListener(fn, scope, options);
7881         },
7882         
7883         /**
7884          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7885          * @param {Function} fn        The method the event invokes
7886          * @param {Object}   scope    An object that becomes the scope of the handler
7887          * @param {boolean}  options
7888          */
7889         onWindowResize : function(fn, scope, options)
7890         {
7891             if(!resizeEvent){
7892                 resizeEvent = new Roo.util.Event();
7893                 resizeTask = new Roo.util.DelayedTask(function(){
7894                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7895                 });
7896                 E.on(window, "resize", function()
7897                 {
7898                     if (Roo.isIE) {
7899                         resizeTask.delay(50);
7900                     } else {
7901                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7902                     }
7903                 });
7904             }
7905             resizeEvent.addListener(fn, scope, options);
7906         },
7907
7908         /**
7909          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7910          * @param {Function} fn        The method the event invokes
7911          * @param {Object}   scope    An object that becomes the scope of the handler
7912          * @param {boolean}  options
7913          */
7914         onTextResize : function(fn, scope, options){
7915             if(!textEvent){
7916                 textEvent = new Roo.util.Event();
7917                 var textEl = new Roo.Element(document.createElement('div'));
7918                 textEl.dom.className = 'x-text-resize';
7919                 textEl.dom.innerHTML = 'X';
7920                 textEl.appendTo(document.body);
7921                 textSize = textEl.dom.offsetHeight;
7922                 setInterval(function(){
7923                     if(textEl.dom.offsetHeight != textSize){
7924                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7925                     }
7926                 }, this.textResizeInterval);
7927             }
7928             textEvent.addListener(fn, scope, options);
7929         },
7930
7931         /**
7932          * Removes the passed window resize listener.
7933          * @param {Function} fn        The method the event invokes
7934          * @param {Object}   scope    The scope of handler
7935          */
7936         removeResizeListener : function(fn, scope){
7937             if(resizeEvent){
7938                 resizeEvent.removeListener(fn, scope);
7939             }
7940         },
7941
7942         // private
7943         fireResize : function(){
7944             if(resizeEvent){
7945                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7946             }   
7947         },
7948         /**
7949          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7950          */
7951         ieDeferSrc : false,
7952         /**
7953          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7954          */
7955         textResizeInterval : 50
7956     };
7957     
7958     /**
7959      * Fix for doc tools
7960      * @scopeAlias pub=Roo.EventManager
7961      */
7962     
7963      /**
7964      * Appends an event handler to an element (shorthand for addListener)
7965      * @param {String/HTMLElement}   element        The html element or id to assign the
7966      * @param {String}   eventName The type of event to listen for
7967      * @param {Function} handler The method the event invokes
7968      * @param {Object}   scope (optional) The scope in which to execute the handler
7969      * function. The handler function's "this" context.
7970      * @param {Object}   options (optional) An object containing handler configuration
7971      * properties. This may contain any of the following properties:<ul>
7972      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7973      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7974      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7975      * <li>preventDefault {Boolean} True to prevent the default action</li>
7976      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7977      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7978      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7979      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7980      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7981      * by the specified number of milliseconds. If the event fires again within that time, the original
7982      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7983      * </ul><br>
7984      * <p>
7985      * <b>Combining Options</b><br>
7986      * Using the options argument, it is possible to combine different types of listeners:<br>
7987      * <br>
7988      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7989      * Code:<pre><code>
7990 el.on('click', this.onClick, this, {
7991     single: true,
7992     delay: 100,
7993     stopEvent : true,
7994     forumId: 4
7995 });</code></pre>
7996      * <p>
7997      * <b>Attaching multiple handlers in 1 call</b><br>
7998       * The method also allows for a single argument to be passed which is a config object containing properties
7999      * which specify multiple handlers.
8000      * <p>
8001      * Code:<pre><code>
8002 el.on({
8003     'click' : {
8004         fn: this.onClick
8005         scope: this,
8006         delay: 100
8007     },
8008     'mouseover' : {
8009         fn: this.onMouseOver
8010         scope: this
8011     },
8012     'mouseout' : {
8013         fn: this.onMouseOut
8014         scope: this
8015     }
8016 });</code></pre>
8017      * <p>
8018      * Or a shorthand syntax:<br>
8019      * Code:<pre><code>
8020 el.on({
8021     'click' : this.onClick,
8022     'mouseover' : this.onMouseOver,
8023     'mouseout' : this.onMouseOut
8024     scope: this
8025 });</code></pre>
8026      */
8027     pub.on = pub.addListener;
8028     pub.un = pub.removeListener;
8029
8030     pub.stoppedMouseDownEvent = new Roo.util.Event();
8031     return pub;
8032 }();
8033 /**
8034   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
8035   * @param {Function} fn        The method the event invokes
8036   * @param {Object}   scope    An  object that becomes the scope of the handler
8037   * @param {boolean}  override If true, the obj passed in becomes
8038   *                             the execution scope of the listener
8039   * @member Roo
8040   * @method onReady
8041  */
8042 Roo.onReady = Roo.EventManager.onDocumentReady;
8043
8044 Roo.onReady(function(){
8045     var bd = Roo.get(document.body);
8046     if(!bd){ return; }
8047
8048     var cls = [
8049             Roo.isIE ? "roo-ie"
8050             : Roo.isIE11 ? "roo-ie11"
8051             : Roo.isEdge ? "roo-edge"
8052             : Roo.isGecko ? "roo-gecko"
8053             : Roo.isOpera ? "roo-opera"
8054             : Roo.isSafari ? "roo-safari" : ""];
8055
8056     if(Roo.isMac){
8057         cls.push("roo-mac");
8058     }
8059     if(Roo.isLinux){
8060         cls.push("roo-linux");
8061     }
8062     if(Roo.isIOS){
8063         cls.push("roo-ios");
8064     }
8065     if(Roo.isTouch){
8066         cls.push("roo-touch");
8067     }
8068     if(Roo.isBorderBox){
8069         cls.push('roo-border-box');
8070     }
8071     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
8072         var p = bd.dom.parentNode;
8073         if(p){
8074             p.className += ' roo-strict';
8075         }
8076     }
8077     bd.addClass(cls.join(' '));
8078 });
8079
8080 /**
8081  * @class Roo.EventObject
8082  * EventObject exposes the Yahoo! UI Event functionality directly on the object
8083  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
8084  * Example:
8085  * <pre><code>
8086  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
8087     e.preventDefault();
8088     var target = e.getTarget();
8089     ...
8090  }
8091  var myDiv = Roo.get("myDiv");
8092  myDiv.on("click", handleClick);
8093  //or
8094  Roo.EventManager.on("myDiv", 'click', handleClick);
8095  Roo.EventManager.addListener("myDiv", 'click', handleClick);
8096  </code></pre>
8097  * @static
8098  */
8099 Roo.EventObject = function(){
8100     
8101     var E = Roo.lib.Event;
8102     
8103     // safari keypress events for special keys return bad keycodes
8104     var safariKeys = {
8105         63234 : 37, // left
8106         63235 : 39, // right
8107         63232 : 38, // up
8108         63233 : 40, // down
8109         63276 : 33, // page up
8110         63277 : 34, // page down
8111         63272 : 46, // delete
8112         63273 : 36, // home
8113         63275 : 35  // end
8114     };
8115
8116     // normalize button clicks
8117     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
8118                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
8119
8120     Roo.EventObjectImpl = function(e){
8121         if(e){
8122             this.setEvent(e.browserEvent || e);
8123         }
8124     };
8125     Roo.EventObjectImpl.prototype = {
8126         /**
8127          * Used to fix doc tools.
8128          * @scope Roo.EventObject.prototype
8129          */
8130             
8131
8132         
8133         
8134         /** The normal browser event */
8135         browserEvent : null,
8136         /** The button pressed in a mouse event */
8137         button : -1,
8138         /** True if the shift key was down during the event */
8139         shiftKey : false,
8140         /** True if the control key was down during the event */
8141         ctrlKey : false,
8142         /** True if the alt key was down during the event */
8143         altKey : false,
8144
8145         /** Key constant 
8146         * @type Number */
8147         BACKSPACE : 8,
8148         /** Key constant 
8149         * @type Number */
8150         TAB : 9,
8151         /** Key constant 
8152         * @type Number */
8153         RETURN : 13,
8154         /** Key constant 
8155         * @type Number */
8156         ENTER : 13,
8157         /** Key constant 
8158         * @type Number */
8159         SHIFT : 16,
8160         /** Key constant 
8161         * @type Number */
8162         CONTROL : 17,
8163         /** Key constant 
8164         * @type Number */
8165         ESC : 27,
8166         /** Key constant 
8167         * @type Number */
8168         SPACE : 32,
8169         /** Key constant 
8170         * @type Number */
8171         PAGEUP : 33,
8172         /** Key constant 
8173         * @type Number */
8174         PAGEDOWN : 34,
8175         /** Key constant 
8176         * @type Number */
8177         END : 35,
8178         /** Key constant 
8179         * @type Number */
8180         HOME : 36,
8181         /** Key constant 
8182         * @type Number */
8183         LEFT : 37,
8184         /** Key constant 
8185         * @type Number */
8186         UP : 38,
8187         /** Key constant 
8188         * @type Number */
8189         RIGHT : 39,
8190         /** Key constant 
8191         * @type Number */
8192         DOWN : 40,
8193         /** Key constant 
8194         * @type Number */
8195         DELETE : 46,
8196         /** Key constant 
8197         * @type Number */
8198         F5 : 116,
8199
8200            /** @private */
8201         setEvent : function(e){
8202             if(e == this || (e && e.browserEvent)){ // already wrapped
8203                 return e;
8204             }
8205             this.browserEvent = e;
8206             if(e){
8207                 // normalize buttons
8208                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8209                 if(e.type == 'click' && this.button == -1){
8210                     this.button = 0;
8211                 }
8212                 this.type = e.type;
8213                 this.shiftKey = e.shiftKey;
8214                 // mac metaKey behaves like ctrlKey
8215                 this.ctrlKey = e.ctrlKey || e.metaKey;
8216                 this.altKey = e.altKey;
8217                 // in getKey these will be normalized for the mac
8218                 this.keyCode = e.keyCode;
8219                 // keyup warnings on firefox.
8220                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8221                 // cache the target for the delayed and or buffered events
8222                 this.target = E.getTarget(e);
8223                 // same for XY
8224                 this.xy = E.getXY(e);
8225             }else{
8226                 this.button = -1;
8227                 this.shiftKey = false;
8228                 this.ctrlKey = false;
8229                 this.altKey = false;
8230                 this.keyCode = 0;
8231                 this.charCode =0;
8232                 this.target = null;
8233                 this.xy = [0, 0];
8234             }
8235             return this;
8236         },
8237
8238         /**
8239          * Stop the event (preventDefault and stopPropagation)
8240          */
8241         stopEvent : function(){
8242             if(this.browserEvent){
8243                 if(this.browserEvent.type == 'mousedown'){
8244                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8245                 }
8246                 E.stopEvent(this.browserEvent);
8247             }
8248         },
8249
8250         /**
8251          * Prevents the browsers default handling of the event.
8252          */
8253         preventDefault : function(){
8254             if(this.browserEvent){
8255                 E.preventDefault(this.browserEvent);
8256             }
8257         },
8258
8259         /** @private */
8260         isNavKeyPress : function(){
8261             var k = this.keyCode;
8262             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8263             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8264         },
8265
8266         isSpecialKey : function(){
8267             var k = this.keyCode;
8268             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8269             (k == 16) || (k == 17) ||
8270             (k >= 18 && k <= 20) ||
8271             (k >= 33 && k <= 35) ||
8272             (k >= 36 && k <= 39) ||
8273             (k >= 44 && k <= 45);
8274         },
8275         /**
8276          * Cancels bubbling of the event.
8277          */
8278         stopPropagation : function(){
8279             if(this.browserEvent){
8280                 if(this.type == 'mousedown'){
8281                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8282                 }
8283                 E.stopPropagation(this.browserEvent);
8284             }
8285         },
8286
8287         /**
8288          * Gets the key code for the event.
8289          * @return {Number}
8290          */
8291         getCharCode : function(){
8292             return this.charCode || this.keyCode;
8293         },
8294
8295         /**
8296          * Returns a normalized keyCode for the event.
8297          * @return {Number} The key code
8298          */
8299         getKey : function(){
8300             var k = this.keyCode || this.charCode;
8301             return Roo.isSafari ? (safariKeys[k] || k) : k;
8302         },
8303
8304         /**
8305          * Gets the x coordinate of the event.
8306          * @return {Number}
8307          */
8308         getPageX : function(){
8309             return this.xy[0];
8310         },
8311
8312         /**
8313          * Gets the y coordinate of the event.
8314          * @return {Number}
8315          */
8316         getPageY : function(){
8317             return this.xy[1];
8318         },
8319
8320         /**
8321          * Gets the time of the event.
8322          * @return {Number}
8323          */
8324         getTime : function(){
8325             if(this.browserEvent){
8326                 return E.getTime(this.browserEvent);
8327             }
8328             return null;
8329         },
8330
8331         /**
8332          * Gets the page coordinates of the event.
8333          * @return {Array} The xy values like [x, y]
8334          */
8335         getXY : function(){
8336             return this.xy;
8337         },
8338
8339         /**
8340          * Gets the target for the event.
8341          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8342          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8343                 search as a number or element (defaults to 10 || document.body)
8344          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8345          * @return {HTMLelement}
8346          */
8347         getTarget : function(selector, maxDepth, returnEl){
8348             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8349         },
8350         /**
8351          * Gets the related target.
8352          * @return {HTMLElement}
8353          */
8354         getRelatedTarget : function(){
8355             if(this.browserEvent){
8356                 return E.getRelatedTarget(this.browserEvent);
8357             }
8358             return null;
8359         },
8360
8361         /**
8362          * Normalizes mouse wheel delta across browsers
8363          * @return {Number} The delta
8364          */
8365         getWheelDelta : function(){
8366             var e = this.browserEvent;
8367             var delta = 0;
8368             if(e.wheelDelta){ /* IE/Opera. */
8369                 delta = e.wheelDelta/120;
8370             }else if(e.detail){ /* Mozilla case. */
8371                 delta = -e.detail/3;
8372             }
8373             return delta;
8374         },
8375
8376         /**
8377          * Returns true if the control, meta, shift or alt key was pressed during this event.
8378          * @return {Boolean}
8379          */
8380         hasModifier : function(){
8381             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8382         },
8383
8384         /**
8385          * Returns true if the target of this event equals el or is a child of el
8386          * @param {String/HTMLElement/Element} el
8387          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8388          * @return {Boolean}
8389          */
8390         within : function(el, related){
8391             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8392             return t && Roo.fly(el).contains(t);
8393         },
8394
8395         getPoint : function(){
8396             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8397         }
8398     };
8399
8400     return new Roo.EventObjectImpl();
8401 }();
8402             
8403     /*
8404  * Based on:
8405  * Ext JS Library 1.1.1
8406  * Copyright(c) 2006-2007, Ext JS, LLC.
8407  *
8408  * Originally Released Under LGPL - original licence link has changed is not relivant.
8409  *
8410  * Fork - LGPL
8411  * <script type="text/javascript">
8412  */
8413
8414  
8415 // was in Composite Element!??!?!
8416  
8417 (function(){
8418     var D = Roo.lib.Dom;
8419     var E = Roo.lib.Event;
8420     var A = Roo.lib.Anim;
8421
8422     // local style camelizing for speed
8423     var propCache = {};
8424     var camelRe = /(-[a-z])/gi;
8425     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8426     var view = document.defaultView;
8427
8428 /**
8429  * @class Roo.Element
8430  * Represents an Element in the DOM.<br><br>
8431  * Usage:<br>
8432 <pre><code>
8433 var el = Roo.get("my-div");
8434
8435 // or with getEl
8436 var el = getEl("my-div");
8437
8438 // or with a DOM element
8439 var el = Roo.get(myDivElement);
8440 </code></pre>
8441  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8442  * each call instead of constructing a new one.<br><br>
8443  * <b>Animations</b><br />
8444  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8445  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8446 <pre>
8447 Option    Default   Description
8448 --------- --------  ---------------------------------------------
8449 duration  .35       The duration of the animation in seconds
8450 easing    easeOut   The YUI easing method
8451 callback  none      A function to execute when the anim completes
8452 scope     this      The scope (this) of the callback function
8453 </pre>
8454 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8455 * manipulate the animation. Here's an example:
8456 <pre><code>
8457 var el = Roo.get("my-div");
8458
8459 // no animation
8460 el.setWidth(100);
8461
8462 // default animation
8463 el.setWidth(100, true);
8464
8465 // animation with some options set
8466 el.setWidth(100, {
8467     duration: 1,
8468     callback: this.foo,
8469     scope: this
8470 });
8471
8472 // using the "anim" property to get the Anim object
8473 var opt = {
8474     duration: 1,
8475     callback: this.foo,
8476     scope: this
8477 };
8478 el.setWidth(100, opt);
8479 ...
8480 if(opt.anim.isAnimated()){
8481     opt.anim.stop();
8482 }
8483 </code></pre>
8484 * <b> Composite (Collections of) Elements</b><br />
8485  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8486  * @constructor Create a new Element directly.
8487  * @param {String/HTMLElement} element
8488  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
8489  */
8490     Roo.Element = function(element, forceNew)
8491     {
8492         var dom = typeof element == "string" ?
8493                 document.getElementById(element) : element;
8494         
8495         this.listeners = {};
8496         
8497         if(!dom){ // invalid id/element
8498             return null;
8499         }
8500         var id = dom.id;
8501         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8502             return Roo.Element.cache[id];
8503         }
8504
8505         /**
8506          * The DOM element
8507          * @type HTMLElement
8508          */
8509         this.dom = dom;
8510
8511         /**
8512          * The DOM element ID
8513          * @type String
8514          */
8515         this.id = id || Roo.id(dom);
8516         
8517         return this; // assumed for cctor?
8518     };
8519
8520     var El = Roo.Element;
8521
8522     El.prototype = {
8523         /**
8524          * The element's default display mode  (defaults to "") 
8525          * @type String
8526          */
8527         originalDisplay : "",
8528
8529         
8530         // note this is overridden in BS version..
8531         visibilityMode : 1, 
8532         /**
8533          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8534          * @type String
8535          */
8536         defaultUnit : "px",
8537         
8538         /**
8539          * Sets the element's visibility mode. When setVisible() is called it
8540          * will use this to determine whether to set the visibility or the display property.
8541          * @param visMode Element.VISIBILITY or Element.DISPLAY
8542          * @return {Roo.Element} this
8543          */
8544         setVisibilityMode : function(visMode){
8545             this.visibilityMode = visMode;
8546             return this;
8547         },
8548         /**
8549          * Convenience method for setVisibilityMode(Element.DISPLAY)
8550          * @param {String} display (optional) What to set display to when visible
8551          * @return {Roo.Element} this
8552          */
8553         enableDisplayMode : function(display){
8554             this.setVisibilityMode(El.DISPLAY);
8555             if(typeof display != "undefined") { this.originalDisplay = display; }
8556             return this;
8557         },
8558
8559         /**
8560          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8561          * @param {String} selector The simple selector to test
8562          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8563                 search as a number or element (defaults to 10 || document.body)
8564          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8565          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8566          */
8567         findParent : function(simpleSelector, maxDepth, returnEl){
8568             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8569             maxDepth = maxDepth || 50;
8570             if(typeof maxDepth != "number"){
8571                 stopEl = Roo.getDom(maxDepth);
8572                 maxDepth = 10;
8573             }
8574             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8575                 if(dq.is(p, simpleSelector)){
8576                     return returnEl ? Roo.get(p) : p;
8577                 }
8578                 depth++;
8579                 p = p.parentNode;
8580             }
8581             return null;
8582         },
8583
8584
8585         /**
8586          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8587          * @param {String} selector The simple selector to test
8588          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8589                 search as a number or element (defaults to 10 || document.body)
8590          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8591          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8592          */
8593         findParentNode : function(simpleSelector, maxDepth, returnEl){
8594             var p = Roo.fly(this.dom.parentNode, '_internal');
8595             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8596         },
8597         
8598         /**
8599          * Looks at  the scrollable parent element
8600          */
8601         findScrollableParent : function()
8602         {
8603             var overflowRegex = /(auto|scroll)/;
8604             
8605             if(this.getStyle('position') === 'fixed'){
8606                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8607             }
8608             
8609             var excludeStaticParent = this.getStyle('position') === "absolute";
8610             
8611             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8612                 
8613                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8614                     continue;
8615                 }
8616                 
8617                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8618                     return parent;
8619                 }
8620                 
8621                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8622                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8623                 }
8624             }
8625             
8626             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8627         },
8628
8629         /**
8630          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8631          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8632          * @param {String} selector The simple selector to test
8633          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8634                 search as a number or element (defaults to 10 || document.body)
8635          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8636          */
8637         up : function(simpleSelector, maxDepth){
8638             return this.findParentNode(simpleSelector, maxDepth, true);
8639         },
8640
8641
8642
8643         /**
8644          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8645          * @param {String} selector The simple selector to test
8646          * @return {Boolean} True if this element matches the selector, else false
8647          */
8648         is : function(simpleSelector){
8649             return Roo.DomQuery.is(this.dom, simpleSelector);
8650         },
8651
8652         /**
8653          * Perform animation on this element.
8654          * @param {Object} args The YUI animation control args
8655          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8656          * @param {Function} onComplete (optional) Function to call when animation completes
8657          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8658          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8659          * @return {Roo.Element} this
8660          */
8661         animate : function(args, duration, onComplete, easing, animType){
8662             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8663             return this;
8664         },
8665
8666         /*
8667          * @private Internal animation call
8668          */
8669         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8670             animType = animType || 'run';
8671             opt = opt || {};
8672             var anim = Roo.lib.Anim[animType](
8673                 this.dom, args,
8674                 (opt.duration || defaultDur) || .35,
8675                 (opt.easing || defaultEase) || 'easeOut',
8676                 function(){
8677                     Roo.callback(cb, this);
8678                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8679                 },
8680                 this
8681             );
8682             opt.anim = anim;
8683             return anim;
8684         },
8685
8686         // private legacy anim prep
8687         preanim : function(a, i){
8688             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8689         },
8690
8691         /**
8692          * Removes worthless text nodes
8693          * @param {Boolean} forceReclean (optional) By default the element
8694          * keeps track if it has been cleaned already so
8695          * you can call this over and over. However, if you update the element and
8696          * need to force a reclean, you can pass true.
8697          */
8698         clean : function(forceReclean){
8699             if(this.isCleaned && forceReclean !== true){
8700                 return this;
8701             }
8702             var ns = /\S/;
8703             var d = this.dom, n = d.firstChild, ni = -1;
8704             while(n){
8705                 var nx = n.nextSibling;
8706                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8707                     d.removeChild(n);
8708                 }else{
8709                     n.nodeIndex = ++ni;
8710                 }
8711                 n = nx;
8712             }
8713             this.isCleaned = true;
8714             return this;
8715         },
8716
8717         // private
8718         calcOffsetsTo : function(el){
8719             el = Roo.get(el);
8720             var d = el.dom;
8721             var restorePos = false;
8722             if(el.getStyle('position') == 'static'){
8723                 el.position('relative');
8724                 restorePos = true;
8725             }
8726             var x = 0, y =0;
8727             var op = this.dom;
8728             while(op && op != d && op.tagName != 'HTML'){
8729                 x+= op.offsetLeft;
8730                 y+= op.offsetTop;
8731                 op = op.offsetParent;
8732             }
8733             if(restorePos){
8734                 el.position('static');
8735             }
8736             return [x, y];
8737         },
8738
8739         /**
8740          * Scrolls this element into view within the passed container.
8741          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8742          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8743          * @return {Roo.Element} this
8744          */
8745         scrollIntoView : function(container, hscroll){
8746             var c = Roo.getDom(container) || document.body;
8747             var el = this.dom;
8748
8749             var o = this.calcOffsetsTo(c),
8750                 l = o[0],
8751                 t = o[1],
8752                 b = t+el.offsetHeight,
8753                 r = l+el.offsetWidth;
8754
8755             var ch = c.clientHeight;
8756             var ct = parseInt(c.scrollTop, 10);
8757             var cl = parseInt(c.scrollLeft, 10);
8758             var cb = ct + ch;
8759             var cr = cl + c.clientWidth;
8760
8761             if(t < ct){
8762                 c.scrollTop = t;
8763             }else if(b > cb){
8764                 c.scrollTop = b-ch;
8765             }
8766
8767             if(hscroll !== false){
8768                 if(l < cl){
8769                     c.scrollLeft = l;
8770                 }else if(r > cr){
8771                     c.scrollLeft = r-c.clientWidth;
8772                 }
8773             }
8774             return this;
8775         },
8776
8777         // private
8778         scrollChildIntoView : function(child, hscroll){
8779             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8780         },
8781
8782         /**
8783          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8784          * the new height may not be available immediately.
8785          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8786          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8787          * @param {Function} onComplete (optional) Function to call when animation completes
8788          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8789          * @return {Roo.Element} this
8790          */
8791         autoHeight : function(animate, duration, onComplete, easing){
8792             var oldHeight = this.getHeight();
8793             this.clip();
8794             this.setHeight(1); // force clipping
8795             setTimeout(function(){
8796                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8797                 if(!animate){
8798                     this.setHeight(height);
8799                     this.unclip();
8800                     if(typeof onComplete == "function"){
8801                         onComplete();
8802                     }
8803                 }else{
8804                     this.setHeight(oldHeight); // restore original height
8805                     this.setHeight(height, animate, duration, function(){
8806                         this.unclip();
8807                         if(typeof onComplete == "function") { onComplete(); }
8808                     }.createDelegate(this), easing);
8809                 }
8810             }.createDelegate(this), 0);
8811             return this;
8812         },
8813
8814         /**
8815          * Returns true if this element is an ancestor of the passed element
8816          * @param {HTMLElement/String} el The element to check
8817          * @return {Boolean} True if this element is an ancestor of el, else false
8818          */
8819         contains : function(el){
8820             if(!el){return false;}
8821             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8822         },
8823
8824         /**
8825          * Checks whether the element is currently visible using both visibility and display properties.
8826          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8827          * @return {Boolean} True if the element is currently visible, else false
8828          */
8829         isVisible : function(deep) {
8830             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8831             if(deep !== true || !vis){
8832                 return vis;
8833             }
8834             var p = this.dom.parentNode;
8835             while(p && p.tagName.toLowerCase() != "body"){
8836                 if(!Roo.fly(p, '_isVisible').isVisible()){
8837                     return false;
8838                 }
8839                 p = p.parentNode;
8840             }
8841             return true;
8842         },
8843
8844         /**
8845          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8846          * @param {String} selector The CSS selector
8847          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8848          * @return {CompositeElement/CompositeElementLite} The composite element
8849          */
8850         select : function(selector, unique){
8851             return El.select(selector, unique, this.dom);
8852         },
8853
8854         /**
8855          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8856          * @param {String} selector The CSS selector
8857          * @return {Array} An array of the matched nodes
8858          */
8859         query : function(selector, unique){
8860             return Roo.DomQuery.select(selector, this.dom);
8861         },
8862
8863         /**
8864          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8865          * @param {String} selector The CSS selector
8866          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8867          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8868          */
8869         child : function(selector, returnDom){
8870             var n = Roo.DomQuery.selectNode(selector, this.dom);
8871             return returnDom ? n : Roo.get(n);
8872         },
8873
8874         /**
8875          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8876          * @param {String} selector The CSS selector
8877          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8878          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8879          */
8880         down : function(selector, returnDom){
8881             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8882             return returnDom ? n : Roo.get(n);
8883         },
8884
8885         /**
8886          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8887          * @param {String} group The group the DD object is member of
8888          * @param {Object} config The DD config object
8889          * @param {Object} overrides An object containing methods to override/implement on the DD object
8890          * @return {Roo.dd.DD} The DD object
8891          */
8892         initDD : function(group, config, overrides){
8893             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8894             return Roo.apply(dd, overrides);
8895         },
8896
8897         /**
8898          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8899          * @param {String} group The group the DDProxy object is member of
8900          * @param {Object} config The DDProxy config object
8901          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8902          * @return {Roo.dd.DDProxy} The DDProxy object
8903          */
8904         initDDProxy : function(group, config, overrides){
8905             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8906             return Roo.apply(dd, overrides);
8907         },
8908
8909         /**
8910          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8911          * @param {String} group The group the DDTarget object is member of
8912          * @param {Object} config The DDTarget config object
8913          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8914          * @return {Roo.dd.DDTarget} The DDTarget object
8915          */
8916         initDDTarget : function(group, config, overrides){
8917             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8918             return Roo.apply(dd, overrides);
8919         },
8920
8921         /**
8922          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8923          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8924          * @param {Boolean} visible Whether the element is visible
8925          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8926          * @return {Roo.Element} this
8927          */
8928          setVisible : function(visible, animate){
8929             if(!animate || !A){
8930                 if(this.visibilityMode == El.DISPLAY){
8931                     this.setDisplayed(visible);
8932                 }else{
8933                     this.fixDisplay();
8934                     this.dom.style.visibility = visible ? "visible" : "hidden";
8935                 }
8936             }else{
8937                 // closure for composites
8938                 var dom = this.dom;
8939                 var visMode = this.visibilityMode;
8940                 if(visible){
8941                     this.setOpacity(.01);
8942                     this.setVisible(true);
8943                 }
8944                 this.anim({opacity: { to: (visible?1:0) }},
8945                       this.preanim(arguments, 1),
8946                       null, .35, 'easeIn', function(){
8947                          if(!visible){
8948                              if(visMode == El.DISPLAY){
8949                                  dom.style.display = "none";
8950                              }else{
8951                                  dom.style.visibility = "hidden";
8952                              }
8953                              Roo.get(dom).setOpacity(1);
8954                          }
8955                      });
8956             }
8957             return this;
8958         },
8959
8960         /**
8961          * Returns true if display is not "none"
8962          * @return {Boolean}
8963          */
8964         isDisplayed : function() {
8965             return this.getStyle("display") != "none";
8966         },
8967
8968         /**
8969          * Toggles the element's visibility or display, depending on visibility mode.
8970          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8971          * @return {Roo.Element} this
8972          */
8973         toggle : function(animate){
8974             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8975             return this;
8976         },
8977
8978         /**
8979          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8980          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8981          * @return {Roo.Element} this
8982          */
8983         setDisplayed : function(value) {
8984             if(typeof value == "boolean"){
8985                value = value ? this.originalDisplay : "none";
8986             }
8987             this.setStyle("display", value);
8988             return this;
8989         },
8990
8991         /**
8992          * Tries to focus the element. Any exceptions are caught and ignored.
8993          * @return {Roo.Element} this
8994          */
8995         focus : function() {
8996             try{
8997                 this.dom.focus();
8998             }catch(e){}
8999             return this;
9000         },
9001
9002         /**
9003          * Tries to blur the element. Any exceptions are caught and ignored.
9004          * @return {Roo.Element} this
9005          */
9006         blur : function() {
9007             try{
9008                 this.dom.blur();
9009             }catch(e){}
9010             return this;
9011         },
9012
9013         /**
9014          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
9015          * @param {String/Array} className The CSS class to add, or an array of classes
9016          * @return {Roo.Element} this
9017          */
9018         addClass : function(className){
9019             if(className instanceof Array){
9020                 for(var i = 0, len = className.length; i < len; i++) {
9021                     this.addClass(className[i]);
9022                 }
9023             }else{
9024                 if(className && !this.hasClass(className)){
9025                     if (this.dom instanceof SVGElement) {
9026                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
9027                     } else {
9028                         this.dom.className = this.dom.className + " " + className;
9029                     }
9030                 }
9031             }
9032             return this;
9033         },
9034
9035         /**
9036          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
9037          * @param {String/Array} className The CSS class to add, or an array of classes
9038          * @return {Roo.Element} this
9039          */
9040         radioClass : function(className){
9041             var siblings = this.dom.parentNode.childNodes;
9042             for(var i = 0; i < siblings.length; i++) {
9043                 var s = siblings[i];
9044                 if(s.nodeType == 1){
9045                     Roo.get(s).removeClass(className);
9046                 }
9047             }
9048             this.addClass(className);
9049             return this;
9050         },
9051
9052         /**
9053          * Removes one or more CSS classes from the element.
9054          * @param {String/Array} className The CSS class to remove, or an array of classes
9055          * @return {Roo.Element} this
9056          */
9057         removeClass : function(className){
9058             
9059             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
9060             if(!className || !cn){
9061                 return this;
9062             }
9063             if(className instanceof Array){
9064                 for(var i = 0, len = className.length; i < len; i++) {
9065                     this.removeClass(className[i]);
9066                 }
9067             }else{
9068                 if(this.hasClass(className)){
9069                     var re = this.classReCache[className];
9070                     if (!re) {
9071                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
9072                        this.classReCache[className] = re;
9073                     }
9074                     if (this.dom instanceof SVGElement) {
9075                         this.dom.className.baseVal = cn.replace(re, " ");
9076                     } else {
9077                         this.dom.className = cn.replace(re, " ");
9078                     }
9079                 }
9080             }
9081             return this;
9082         },
9083
9084         // private
9085         classReCache: {},
9086
9087         /**
9088          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
9089          * @param {String} className The CSS class to toggle
9090          * @return {Roo.Element} this
9091          */
9092         toggleClass : function(className){
9093             if(this.hasClass(className)){
9094                 this.removeClass(className);
9095             }else{
9096                 this.addClass(className);
9097             }
9098             return this;
9099         },
9100
9101         /**
9102          * Checks if the specified CSS class exists on this element's DOM node.
9103          * @param {String} className The CSS class to check for
9104          * @return {Boolean} True if the class exists, else false
9105          */
9106         hasClass : function(className){
9107             if (this.dom instanceof SVGElement) {
9108                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
9109             } 
9110             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
9111         },
9112
9113         /**
9114          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
9115          * @param {String} oldClassName The CSS class to replace
9116          * @param {String} newClassName The replacement CSS class
9117          * @return {Roo.Element} this
9118          */
9119         replaceClass : function(oldClassName, newClassName){
9120             this.removeClass(oldClassName);
9121             this.addClass(newClassName);
9122             return this;
9123         },
9124
9125         /**
9126          * Returns an object with properties matching the styles requested.
9127          * For example, el.getStyles('color', 'font-size', 'width') might return
9128          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
9129          * @param {String} style1 A style name
9130          * @param {String} style2 A style name
9131          * @param {String} etc.
9132          * @return {Object} The style object
9133          */
9134         getStyles : function(){
9135             var a = arguments, len = a.length, r = {};
9136             for(var i = 0; i < len; i++){
9137                 r[a[i]] = this.getStyle(a[i]);
9138             }
9139             return r;
9140         },
9141
9142         /**
9143          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
9144          * @param {String} property The style property whose value is returned.
9145          * @return {String} The current value of the style property for this element.
9146          */
9147         getStyle : function(){
9148             return view && view.getComputedStyle ?
9149                 function(prop){
9150                     var el = this.dom, v, cs, camel;
9151                     if(prop == 'float'){
9152                         prop = "cssFloat";
9153                     }
9154                     if(el.style && (v = el.style[prop])){
9155                         return v;
9156                     }
9157                     if(cs = view.getComputedStyle(el, "")){
9158                         if(!(camel = propCache[prop])){
9159                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
9160                         }
9161                         return cs[camel];
9162                     }
9163                     return null;
9164                 } :
9165                 function(prop){
9166                     var el = this.dom, v, cs, camel;
9167                     if(prop == 'opacity'){
9168                         if(typeof el.style.filter == 'string'){
9169                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
9170                             if(m){
9171                                 var fv = parseFloat(m[1]);
9172                                 if(!isNaN(fv)){
9173                                     return fv ? fv / 100 : 0;
9174                                 }
9175                             }
9176                         }
9177                         return 1;
9178                     }else if(prop == 'float'){
9179                         prop = "styleFloat";
9180                     }
9181                     if(!(camel = propCache[prop])){
9182                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
9183                     }
9184                     if(v = el.style[camel]){
9185                         return v;
9186                     }
9187                     if(cs = el.currentStyle){
9188                         return cs[camel];
9189                     }
9190                     return null;
9191                 };
9192         }(),
9193
9194         /**
9195          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
9196          * @param {String/Object} property The style property to be set, or an object of multiple styles.
9197          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
9198          * @return {Roo.Element} this
9199          */
9200         setStyle : function(prop, value){
9201             if(typeof prop == "string"){
9202                 
9203                 if (prop == 'float') {
9204                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
9205                     return this;
9206                 }
9207                 
9208                 var camel;
9209                 if(!(camel = propCache[prop])){
9210                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
9211                 }
9212                 
9213                 if(camel == 'opacity') {
9214                     this.setOpacity(value);
9215                 }else{
9216                     this.dom.style[camel] = value;
9217                 }
9218             }else{
9219                 for(var style in prop){
9220                     if(typeof prop[style] != "function"){
9221                        this.setStyle(style, prop[style]);
9222                     }
9223                 }
9224             }
9225             return this;
9226         },
9227
9228         /**
9229          * More flexible version of {@link #setStyle} for setting style properties.
9230          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9231          * a function which returns such a specification.
9232          * @return {Roo.Element} this
9233          */
9234         applyStyles : function(style){
9235             Roo.DomHelper.applyStyles(this.dom, style);
9236             return this;
9237         },
9238
9239         /**
9240           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9241           * @return {Number} The X position of the element
9242           */
9243         getX : function(){
9244             return D.getX(this.dom);
9245         },
9246
9247         /**
9248           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9249           * @return {Number} The Y position of the element
9250           */
9251         getY : function(){
9252             return D.getY(this.dom);
9253         },
9254
9255         /**
9256           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9257           * @return {Array} The XY position of the element
9258           */
9259         getXY : function(){
9260             return D.getXY(this.dom);
9261         },
9262
9263         /**
9264          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9265          * @param {Number} The X position of the element
9266          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9267          * @return {Roo.Element} this
9268          */
9269         setX : function(x, animate){
9270             if(!animate || !A){
9271                 D.setX(this.dom, x);
9272             }else{
9273                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9274             }
9275             return this;
9276         },
9277
9278         /**
9279          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9280          * @param {Number} The Y position of the element
9281          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9282          * @return {Roo.Element} this
9283          */
9284         setY : function(y, animate){
9285             if(!animate || !A){
9286                 D.setY(this.dom, y);
9287             }else{
9288                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9289             }
9290             return this;
9291         },
9292
9293         /**
9294          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9295          * @param {String} left The left CSS property value
9296          * @return {Roo.Element} this
9297          */
9298         setLeft : function(left){
9299             this.setStyle("left", this.addUnits(left));
9300             return this;
9301         },
9302
9303         /**
9304          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9305          * @param {String} top The top CSS property value
9306          * @return {Roo.Element} this
9307          */
9308         setTop : function(top){
9309             this.setStyle("top", this.addUnits(top));
9310             return this;
9311         },
9312
9313         /**
9314          * Sets the element's CSS right style.
9315          * @param {String} right The right CSS property value
9316          * @return {Roo.Element} this
9317          */
9318         setRight : function(right){
9319             this.setStyle("right", this.addUnits(right));
9320             return this;
9321         },
9322
9323         /**
9324          * Sets the element's CSS bottom style.
9325          * @param {String} bottom The bottom CSS property value
9326          * @return {Roo.Element} this
9327          */
9328         setBottom : function(bottom){
9329             this.setStyle("bottom", this.addUnits(bottom));
9330             return this;
9331         },
9332
9333         /**
9334          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9335          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9336          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9337          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9338          * @return {Roo.Element} this
9339          */
9340         setXY : function(pos, animate){
9341             if(!animate || !A){
9342                 D.setXY(this.dom, pos);
9343             }else{
9344                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9345             }
9346             return this;
9347         },
9348
9349         /**
9350          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9351          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9352          * @param {Number} x X value for new position (coordinates are page-based)
9353          * @param {Number} y Y value for new position (coordinates are page-based)
9354          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9355          * @return {Roo.Element} this
9356          */
9357         setLocation : function(x, y, animate){
9358             this.setXY([x, y], this.preanim(arguments, 2));
9359             return this;
9360         },
9361
9362         /**
9363          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9364          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9365          * @param {Number} x X value for new position (coordinates are page-based)
9366          * @param {Number} y Y value for new position (coordinates are page-based)
9367          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9368          * @return {Roo.Element} this
9369          */
9370         moveTo : function(x, y, animate){
9371             this.setXY([x, y], this.preanim(arguments, 2));
9372             return this;
9373         },
9374
9375         /**
9376          * Returns the region of the given element.
9377          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9378          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9379          */
9380         getRegion : function(){
9381             return D.getRegion(this.dom);
9382         },
9383
9384         /**
9385          * Returns the offset height of the element
9386          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9387          * @return {Number} The element's height
9388          */
9389         getHeight : function(contentHeight){
9390             var h = this.dom.offsetHeight || 0;
9391             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9392         },
9393
9394         /**
9395          * Returns the offset width of the element
9396          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9397          * @return {Number} The element's width
9398          */
9399         getWidth : function(contentWidth){
9400             var w = this.dom.offsetWidth || 0;
9401             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9402         },
9403
9404         /**
9405          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9406          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9407          * if a height has not been set using CSS.
9408          * @return {Number}
9409          */
9410         getComputedHeight : function(){
9411             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9412             if(!h){
9413                 h = parseInt(this.getStyle('height'), 10) || 0;
9414                 if(!this.isBorderBox()){
9415                     h += this.getFrameWidth('tb');
9416                 }
9417             }
9418             return h;
9419         },
9420
9421         /**
9422          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9423          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9424          * if a width has not been set using CSS.
9425          * @return {Number}
9426          */
9427         getComputedWidth : function(){
9428             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9429             if(!w){
9430                 w = parseInt(this.getStyle('width'), 10) || 0;
9431                 if(!this.isBorderBox()){
9432                     w += this.getFrameWidth('lr');
9433                 }
9434             }
9435             return w;
9436         },
9437
9438         /**
9439          * Returns the size of the element.
9440          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9441          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9442          */
9443         getSize : function(contentSize){
9444             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9445         },
9446
9447         /**
9448          * Returns the width and height of the viewport.
9449          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9450          */
9451         getViewSize : function(){
9452             var d = this.dom, doc = document, aw = 0, ah = 0;
9453             if(d == doc || d == doc.body){
9454                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9455             }else{
9456                 return {
9457                     width : d.clientWidth,
9458                     height: d.clientHeight
9459                 };
9460             }
9461         },
9462
9463         /**
9464          * Returns the value of the "value" attribute
9465          * @param {Boolean} asNumber true to parse the value as a number
9466          * @return {String/Number}
9467          */
9468         getValue : function(asNumber){
9469             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9470         },
9471
9472         // private
9473         adjustWidth : function(width){
9474             if(typeof width == "number"){
9475                 if(this.autoBoxAdjust && !this.isBorderBox()){
9476                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9477                 }
9478                 if(width < 0){
9479                     width = 0;
9480                 }
9481             }
9482             return width;
9483         },
9484
9485         // private
9486         adjustHeight : function(height){
9487             if(typeof height == "number"){
9488                if(this.autoBoxAdjust && !this.isBorderBox()){
9489                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9490                }
9491                if(height < 0){
9492                    height = 0;
9493                }
9494             }
9495             return height;
9496         },
9497
9498         /**
9499          * Set the width of the element
9500          * @param {Number} width The new width
9501          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9502          * @return {Roo.Element} this
9503          */
9504         setWidth : function(width, animate){
9505             width = this.adjustWidth(width);
9506             if(!animate || !A){
9507                 this.dom.style.width = this.addUnits(width);
9508             }else{
9509                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9510             }
9511             return this;
9512         },
9513
9514         /**
9515          * Set the height of the element
9516          * @param {Number} height The new height
9517          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9518          * @return {Roo.Element} this
9519          */
9520          setHeight : function(height, animate){
9521             height = this.adjustHeight(height);
9522             if(!animate || !A){
9523                 this.dom.style.height = this.addUnits(height);
9524             }else{
9525                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9526             }
9527             return this;
9528         },
9529
9530         /**
9531          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9532          * @param {Number} width The new width
9533          * @param {Number} height The new height
9534          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9535          * @return {Roo.Element} this
9536          */
9537          setSize : function(width, height, animate){
9538             if(typeof width == "object"){ // in case of object from getSize()
9539                 height = width.height; width = width.width;
9540             }
9541             width = this.adjustWidth(width); height = this.adjustHeight(height);
9542             if(!animate || !A){
9543                 this.dom.style.width = this.addUnits(width);
9544                 this.dom.style.height = this.addUnits(height);
9545             }else{
9546                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9547             }
9548             return this;
9549         },
9550
9551         /**
9552          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9553          * @param {Number} x X value for new position (coordinates are page-based)
9554          * @param {Number} y Y value for new position (coordinates are page-based)
9555          * @param {Number} width The new width
9556          * @param {Number} height The new height
9557          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9558          * @return {Roo.Element} this
9559          */
9560         setBounds : function(x, y, width, height, animate){
9561             if(!animate || !A){
9562                 this.setSize(width, height);
9563                 this.setLocation(x, y);
9564             }else{
9565                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9566                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9567                               this.preanim(arguments, 4), 'motion');
9568             }
9569             return this;
9570         },
9571
9572         /**
9573          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
9574          * @param {Roo.lib.Region} region The region to fill
9575          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9576          * @return {Roo.Element} this
9577          */
9578         setRegion : function(region, animate){
9579             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9580             return this;
9581         },
9582
9583         /**
9584          * Appends an event handler
9585          *
9586          * @param {String}   eventName     The type of event to append
9587          * @param {Function} fn        The method the event invokes
9588          * @param {Object} scope       (optional) The scope (this object) of the fn
9589          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9590          */
9591         addListener : function(eventName, fn, scope, options)
9592         {
9593             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9594                 this.addListener('touchstart', this.onTapHandler, this);
9595             }
9596             
9597             // we need to handle a special case where dom element is a svg element.
9598             // in this case we do not actua
9599             if (!this.dom) {
9600                 return;
9601             }
9602             
9603             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9604                 if (typeof(this.listeners[eventName]) == 'undefined') {
9605                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9606                 }
9607                 this.listeners[eventName].addListener(fn, scope, options);
9608                 return;
9609             }
9610             
9611                 
9612             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9613             
9614             
9615         },
9616         tapedTwice : false,
9617         onTapHandler : function(event)
9618         {
9619             if(!this.tapedTwice) {
9620                 this.tapedTwice = true;
9621                 var s = this;
9622                 setTimeout( function() {
9623                     s.tapedTwice = false;
9624                 }, 300 );
9625                 return;
9626             }
9627             event.preventDefault();
9628             var revent = new MouseEvent('dblclick',  {
9629                 view: window,
9630                 bubbles: true,
9631                 cancelable: true
9632             });
9633              
9634             this.dom.dispatchEvent(revent);
9635             //action on double tap goes below
9636              
9637         }, 
9638  
9639         /**
9640          * Removes an event handler from this element
9641          * @param {String} eventName the type of event to remove
9642          * @param {Function} fn the method the event invokes
9643          * @param {Function} scope (needed for svg fake listeners)
9644          * @return {Roo.Element} this
9645          */
9646         removeListener : function(eventName, fn, scope){
9647             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9648             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9649                 return this;
9650             }
9651             this.listeners[eventName].removeListener(fn, scope);
9652             return this;
9653         },
9654
9655         /**
9656          * Removes all previous added listeners from this element
9657          * @return {Roo.Element} this
9658          */
9659         removeAllListeners : function(){
9660             E.purgeElement(this.dom);
9661             this.listeners = {};
9662             return this;
9663         },
9664
9665         relayEvent : function(eventName, observable){
9666             this.on(eventName, function(e){
9667                 observable.fireEvent(eventName, e);
9668             });
9669         },
9670
9671         
9672         /**
9673          * Set the opacity of the element
9674          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9675          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9676          * @return {Roo.Element} this
9677          */
9678          setOpacity : function(opacity, animate){
9679             if(!animate || !A){
9680                 var s = this.dom.style;
9681                 if(Roo.isIE){
9682                     s.zoom = 1;
9683                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9684                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9685                 }else{
9686                     s.opacity = opacity;
9687                 }
9688             }else{
9689                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9690             }
9691             return this;
9692         },
9693
9694         /**
9695          * Gets the left X coordinate
9696          * @param {Boolean} local True to get the local css position instead of page coordinate
9697          * @return {Number}
9698          */
9699         getLeft : function(local){
9700             if(!local){
9701                 return this.getX();
9702             }else{
9703                 return parseInt(this.getStyle("left"), 10) || 0;
9704             }
9705         },
9706
9707         /**
9708          * Gets the right X coordinate of the element (element X position + element width)
9709          * @param {Boolean} local True to get the local css position instead of page coordinate
9710          * @return {Number}
9711          */
9712         getRight : function(local){
9713             if(!local){
9714                 return this.getX() + this.getWidth();
9715             }else{
9716                 return (this.getLeft(true) + this.getWidth()) || 0;
9717             }
9718         },
9719
9720         /**
9721          * Gets the top Y coordinate
9722          * @param {Boolean} local True to get the local css position instead of page coordinate
9723          * @return {Number}
9724          */
9725         getTop : function(local) {
9726             if(!local){
9727                 return this.getY();
9728             }else{
9729                 return parseInt(this.getStyle("top"), 10) || 0;
9730             }
9731         },
9732
9733         /**
9734          * Gets the bottom Y coordinate of the element (element Y position + element height)
9735          * @param {Boolean} local True to get the local css position instead of page coordinate
9736          * @return {Number}
9737          */
9738         getBottom : function(local){
9739             if(!local){
9740                 return this.getY() + this.getHeight();
9741             }else{
9742                 return (this.getTop(true) + this.getHeight()) || 0;
9743             }
9744         },
9745
9746         /**
9747         * Initializes positioning on this element. If a desired position is not passed, it will make the
9748         * the element positioned relative IF it is not already positioned.
9749         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9750         * @param {Number} zIndex (optional) The zIndex to apply
9751         * @param {Number} x (optional) Set the page X position
9752         * @param {Number} y (optional) Set the page Y position
9753         */
9754         position : function(pos, zIndex, x, y){
9755             if(!pos){
9756                if(this.getStyle('position') == 'static'){
9757                    this.setStyle('position', 'relative');
9758                }
9759             }else{
9760                 this.setStyle("position", pos);
9761             }
9762             if(zIndex){
9763                 this.setStyle("z-index", zIndex);
9764             }
9765             if(x !== undefined && y !== undefined){
9766                 this.setXY([x, y]);
9767             }else if(x !== undefined){
9768                 this.setX(x);
9769             }else if(y !== undefined){
9770                 this.setY(y);
9771             }
9772         },
9773
9774         /**
9775         * Clear positioning back to the default when the document was loaded
9776         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9777         * @return {Roo.Element} this
9778          */
9779         clearPositioning : function(value){
9780             value = value ||'';
9781             this.setStyle({
9782                 "left": value,
9783                 "right": value,
9784                 "top": value,
9785                 "bottom": value,
9786                 "z-index": "",
9787                 "position" : "static"
9788             });
9789             return this;
9790         },
9791
9792         /**
9793         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9794         * snapshot before performing an update and then restoring the element.
9795         * @return {Object}
9796         */
9797         getPositioning : function(){
9798             var l = this.getStyle("left");
9799             var t = this.getStyle("top");
9800             return {
9801                 "position" : this.getStyle("position"),
9802                 "left" : l,
9803                 "right" : l ? "" : this.getStyle("right"),
9804                 "top" : t,
9805                 "bottom" : t ? "" : this.getStyle("bottom"),
9806                 "z-index" : this.getStyle("z-index")
9807             };
9808         },
9809
9810         /**
9811          * Gets the width of the border(s) for the specified side(s)
9812          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9813          * passing lr would get the border (l)eft width + the border (r)ight width.
9814          * @return {Number} The width of the sides passed added together
9815          */
9816         getBorderWidth : function(side){
9817             return this.addStyles(side, El.borders);
9818         },
9819
9820         /**
9821          * Gets the width of the padding(s) for the specified side(s)
9822          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9823          * passing lr would get the padding (l)eft + the padding (r)ight.
9824          * @return {Number} The padding of the sides passed added together
9825          */
9826         getPadding : function(side){
9827             return this.addStyles(side, El.paddings);
9828         },
9829
9830         /**
9831         * Set positioning with an object returned by getPositioning().
9832         * @param {Object} posCfg
9833         * @return {Roo.Element} this
9834          */
9835         setPositioning : function(pc){
9836             this.applyStyles(pc);
9837             if(pc.right == "auto"){
9838                 this.dom.style.right = "";
9839             }
9840             if(pc.bottom == "auto"){
9841                 this.dom.style.bottom = "";
9842             }
9843             return this;
9844         },
9845
9846         // private
9847         fixDisplay : function(){
9848             if(this.getStyle("display") == "none"){
9849                 this.setStyle("visibility", "hidden");
9850                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9851                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9852                     this.setStyle("display", "block");
9853                 }
9854             }
9855         },
9856
9857         /**
9858          * Quick set left and top adding default units
9859          * @param {String} left The left CSS property value
9860          * @param {String} top The top CSS property value
9861          * @return {Roo.Element} this
9862          */
9863          setLeftTop : function(left, top){
9864             this.dom.style.left = this.addUnits(left);
9865             this.dom.style.top = this.addUnits(top);
9866             return this;
9867         },
9868
9869         /**
9870          * Move this element relative to its current position.
9871          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9872          * @param {Number} distance How far to move the element in pixels
9873          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9874          * @return {Roo.Element} this
9875          */
9876          move : function(direction, distance, animate){
9877             var xy = this.getXY();
9878             direction = direction.toLowerCase();
9879             switch(direction){
9880                 case "l":
9881                 case "left":
9882                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9883                     break;
9884                case "r":
9885                case "right":
9886                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9887                     break;
9888                case "t":
9889                case "top":
9890                case "up":
9891                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9892                     break;
9893                case "b":
9894                case "bottom":
9895                case "down":
9896                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9897                     break;
9898             }
9899             return this;
9900         },
9901
9902         /**
9903          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9904          * @return {Roo.Element} this
9905          */
9906         clip : function(){
9907             if(!this.isClipped){
9908                this.isClipped = true;
9909                this.originalClip = {
9910                    "o": this.getStyle("overflow"),
9911                    "x": this.getStyle("overflow-x"),
9912                    "y": this.getStyle("overflow-y")
9913                };
9914                this.setStyle("overflow", "hidden");
9915                this.setStyle("overflow-x", "hidden");
9916                this.setStyle("overflow-y", "hidden");
9917             }
9918             return this;
9919         },
9920
9921         /**
9922          *  Return clipping (overflow) to original clipping before clip() was called
9923          * @return {Roo.Element} this
9924          */
9925         unclip : function(){
9926             if(this.isClipped){
9927                 this.isClipped = false;
9928                 var o = this.originalClip;
9929                 if(o.o){this.setStyle("overflow", o.o);}
9930                 if(o.x){this.setStyle("overflow-x", o.x);}
9931                 if(o.y){this.setStyle("overflow-y", o.y);}
9932             }
9933             return this;
9934         },
9935
9936
9937         /**
9938          * Gets the x,y coordinates specified by the anchor position on the element.
9939          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9940          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9941          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9942          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9943          * @return {Array} [x, y] An array containing the element's x and y coordinates
9944          */
9945         getAnchorXY : function(anchor, local, s){
9946             //Passing a different size is useful for pre-calculating anchors,
9947             //especially for anchored animations that change the el size.
9948
9949             var w, h, vp = false;
9950             if(!s){
9951                 var d = this.dom;
9952                 if(d == document.body || d == document){
9953                     vp = true;
9954                     w = D.getViewWidth(); h = D.getViewHeight();
9955                 }else{
9956                     w = this.getWidth(); h = this.getHeight();
9957                 }
9958             }else{
9959                 w = s.width;  h = s.height;
9960             }
9961             var x = 0, y = 0, r = Math.round;
9962             switch((anchor || "tl").toLowerCase()){
9963                 case "c":
9964                     x = r(w*.5);
9965                     y = r(h*.5);
9966                 break;
9967                 case "t":
9968                     x = r(w*.5);
9969                     y = 0;
9970                 break;
9971                 case "l":
9972                     x = 0;
9973                     y = r(h*.5);
9974                 break;
9975                 case "r":
9976                     x = w;
9977                     y = r(h*.5);
9978                 break;
9979                 case "b":
9980                     x = r(w*.5);
9981                     y = h;
9982                 break;
9983                 case "tl":
9984                     x = 0;
9985                     y = 0;
9986                 break;
9987                 case "bl":
9988                     x = 0;
9989                     y = h;
9990                 break;
9991                 case "br":
9992                     x = w;
9993                     y = h;
9994                 break;
9995                 case "tr":
9996                     x = w;
9997                     y = 0;
9998                 break;
9999             }
10000             if(local === true){
10001                 return [x, y];
10002             }
10003             if(vp){
10004                 var sc = this.getScroll();
10005                 return [x + sc.left, y + sc.top];
10006             }
10007             //Add the element's offset xy
10008             var o = this.getXY();
10009             return [x+o[0], y+o[1]];
10010         },
10011
10012         /**
10013          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
10014          * supported position values.
10015          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10016          * @param {String} position The position to align to.
10017          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10018          * @return {Array} [x, y]
10019          */
10020         getAlignToXY : function(el, p, o)
10021         {
10022             el = Roo.get(el);
10023             var d = this.dom;
10024             if(!el.dom){
10025                 throw "Element.alignTo with an element that doesn't exist";
10026             }
10027             var c = false; //constrain to viewport
10028             var p1 = "", p2 = "";
10029             o = o || [0,0];
10030
10031             if(!p){
10032                 p = "tl-bl";
10033             }else if(p == "?"){
10034                 p = "tl-bl?";
10035             }else if(p.indexOf("-") == -1){
10036                 p = "tl-" + p;
10037             }
10038             p = p.toLowerCase();
10039             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
10040             if(!m){
10041                throw "Element.alignTo with an invalid alignment " + p;
10042             }
10043             p1 = m[1]; p2 = m[2]; c = !!m[3];
10044
10045             //Subtract the aligned el's internal xy from the target's offset xy
10046             //plus custom offset to get the aligned el's new offset xy
10047             var a1 = this.getAnchorXY(p1, true);
10048             var a2 = el.getAnchorXY(p2, false);
10049             var x = a2[0] - a1[0] + o[0];
10050             var y = a2[1] - a1[1] + o[1];
10051             if(c){
10052                 //constrain the aligned el to viewport if necessary
10053                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
10054                 // 5px of margin for ie
10055                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
10056
10057                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
10058                 //perpendicular to the vp border, allow the aligned el to slide on that border,
10059                 //otherwise swap the aligned el to the opposite border of the target.
10060                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
10061                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
10062                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
10063                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
10064
10065                var doc = document;
10066                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
10067                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
10068
10069                if((x+w) > dw + scrollX){
10070                     x = swapX ? r.left-w : dw+scrollX-w;
10071                 }
10072                if(x < scrollX){
10073                    x = swapX ? r.right : scrollX;
10074                }
10075                if((y+h) > dh + scrollY){
10076                     y = swapY ? r.top-h : dh+scrollY-h;
10077                 }
10078                if (y < scrollY){
10079                    y = swapY ? r.bottom : scrollY;
10080                }
10081             }
10082             return [x,y];
10083         },
10084
10085         // private
10086         getConstrainToXY : function(){
10087             var os = {top:0, left:0, bottom:0, right: 0};
10088
10089             return function(el, local, offsets, proposedXY){
10090                 el = Roo.get(el);
10091                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
10092
10093                 var vw, vh, vx = 0, vy = 0;
10094                 if(el.dom == document.body || el.dom == document){
10095                     vw = Roo.lib.Dom.getViewWidth();
10096                     vh = Roo.lib.Dom.getViewHeight();
10097                 }else{
10098                     vw = el.dom.clientWidth;
10099                     vh = el.dom.clientHeight;
10100                     if(!local){
10101                         var vxy = el.getXY();
10102                         vx = vxy[0];
10103                         vy = vxy[1];
10104                     }
10105                 }
10106
10107                 var s = el.getScroll();
10108
10109                 vx += offsets.left + s.left;
10110                 vy += offsets.top + s.top;
10111
10112                 vw -= offsets.right;
10113                 vh -= offsets.bottom;
10114
10115                 var vr = vx+vw;
10116                 var vb = vy+vh;
10117
10118                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
10119                 var x = xy[0], y = xy[1];
10120                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
10121
10122                 // only move it if it needs it
10123                 var moved = false;
10124
10125                 // first validate right/bottom
10126                 if((x + w) > vr){
10127                     x = vr - w;
10128                     moved = true;
10129                 }
10130                 if((y + h) > vb){
10131                     y = vb - h;
10132                     moved = true;
10133                 }
10134                 // then make sure top/left isn't negative
10135                 if(x < vx){
10136                     x = vx;
10137                     moved = true;
10138                 }
10139                 if(y < vy){
10140                     y = vy;
10141                     moved = true;
10142                 }
10143                 return moved ? [x, y] : false;
10144             };
10145         }(),
10146
10147         // private
10148         adjustForConstraints : function(xy, parent, offsets){
10149             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
10150         },
10151
10152         /**
10153          * Aligns this element with another element relative to the specified anchor points. If the other element is the
10154          * document it aligns it to the viewport.
10155          * The position parameter is optional, and can be specified in any one of the following formats:
10156          * <ul>
10157          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
10158          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
10159          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
10160          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
10161          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
10162          *       element's anchor point, and the second value is used as the target's anchor point.</li>
10163          * </ul>
10164          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
10165          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
10166          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
10167          * that specified in order to enforce the viewport constraints.
10168          * Following are all of the supported anchor positions:
10169     <pre>
10170     Value  Description
10171     -----  -----------------------------
10172     tl     The top left corner (default)
10173     t      The center of the top edge
10174     tr     The top right corner
10175     l      The center of the left edge
10176     c      In the center of the element
10177     r      The center of the right edge
10178     bl     The bottom left corner
10179     b      The center of the bottom edge
10180     br     The bottom right corner
10181     </pre>
10182     Example Usage:
10183     <pre><code>
10184     // align el to other-el using the default positioning ("tl-bl", non-constrained)
10185     el.alignTo("other-el");
10186
10187     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
10188     el.alignTo("other-el", "tr?");
10189
10190     // align the bottom right corner of el with the center left edge of other-el
10191     el.alignTo("other-el", "br-l?");
10192
10193     // align the center of el with the bottom left corner of other-el and
10194     // adjust the x position by -6 pixels (and the y position by 0)
10195     el.alignTo("other-el", "c-bl", [-6, 0]);
10196     </code></pre>
10197          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10198          * @param {String} position The position to align to.
10199          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10200          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10201          * @return {Roo.Element} this
10202          */
10203         alignTo : function(element, position, offsets, animate){
10204             var xy = this.getAlignToXY(element, position, offsets);
10205             this.setXY(xy, this.preanim(arguments, 3));
10206             return this;
10207         },
10208
10209         /**
10210          * Anchors an element to another element and realigns it when the window is resized.
10211          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10212          * @param {String} position The position to align to.
10213          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10214          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10215          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10216          * is a number, it is used as the buffer delay (defaults to 50ms).
10217          * @param {Function} callback The function to call after the animation finishes
10218          * @return {Roo.Element} this
10219          */
10220         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10221             var action = function(){
10222                 this.alignTo(el, alignment, offsets, animate);
10223                 Roo.callback(callback, this);
10224             };
10225             Roo.EventManager.onWindowResize(action, this);
10226             var tm = typeof monitorScroll;
10227             if(tm != 'undefined'){
10228                 Roo.EventManager.on(window, 'scroll', action, this,
10229                     {buffer: tm == 'number' ? monitorScroll : 50});
10230             }
10231             action.call(this); // align immediately
10232             return this;
10233         },
10234         /**
10235          * Clears any opacity settings from this element. Required in some cases for IE.
10236          * @return {Roo.Element} this
10237          */
10238         clearOpacity : function(){
10239             if (window.ActiveXObject) {
10240                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10241                     this.dom.style.filter = "";
10242                 }
10243             } else {
10244                 this.dom.style.opacity = "";
10245                 this.dom.style["-moz-opacity"] = "";
10246                 this.dom.style["-khtml-opacity"] = "";
10247             }
10248             return this;
10249         },
10250
10251         /**
10252          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10253          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10254          * @return {Roo.Element} this
10255          */
10256         hide : function(animate){
10257             this.setVisible(false, this.preanim(arguments, 0));
10258             return this;
10259         },
10260
10261         /**
10262         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10263         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10264          * @return {Roo.Element} this
10265          */
10266         show : function(animate){
10267             this.setVisible(true, this.preanim(arguments, 0));
10268             return this;
10269         },
10270
10271         /**
10272          * @private Test if size has a unit, otherwise appends the default
10273          */
10274         addUnits : function(size){
10275             return Roo.Element.addUnits(size, this.defaultUnit);
10276         },
10277
10278         /**
10279          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10280          * @return {Roo.Element} this
10281          */
10282         beginMeasure : function(){
10283             var el = this.dom;
10284             if(el.offsetWidth || el.offsetHeight){
10285                 return this; // offsets work already
10286             }
10287             var changed = [];
10288             var p = this.dom, b = document.body; // start with this element
10289             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10290                 var pe = Roo.get(p);
10291                 if(pe.getStyle('display') == 'none'){
10292                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10293                     p.style.visibility = "hidden";
10294                     p.style.display = "block";
10295                 }
10296                 p = p.parentNode;
10297             }
10298             this._measureChanged = changed;
10299             return this;
10300
10301         },
10302
10303         /**
10304          * Restores displays to before beginMeasure was called
10305          * @return {Roo.Element} this
10306          */
10307         endMeasure : function(){
10308             var changed = this._measureChanged;
10309             if(changed){
10310                 for(var i = 0, len = changed.length; i < len; i++) {
10311                     var r = changed[i];
10312                     r.el.style.visibility = r.visibility;
10313                     r.el.style.display = "none";
10314                 }
10315                 this._measureChanged = null;
10316             }
10317             return this;
10318         },
10319
10320         /**
10321         * Update the innerHTML of this element, optionally searching for and processing scripts
10322         * @param {String} html The new HTML
10323         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10324         * @param {Function} callback For async script loading you can be noticed when the update completes
10325         * @return {Roo.Element} this
10326          */
10327         update : function(html, loadScripts, callback){
10328             if(typeof html == "undefined"){
10329                 html = "";
10330             }
10331             if(loadScripts !== true){
10332                 this.dom.innerHTML = html;
10333                 if(typeof callback == "function"){
10334                     callback();
10335                 }
10336                 return this;
10337             }
10338             var id = Roo.id();
10339             var dom = this.dom;
10340
10341             html += '<span id="' + id + '"></span>';
10342
10343             E.onAvailable(id, function(){
10344                 var hd = document.getElementsByTagName("head")[0];
10345                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10346                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10347                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10348
10349                 var match;
10350                 while(match = re.exec(html)){
10351                     var attrs = match[1];
10352                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10353                     if(srcMatch && srcMatch[2]){
10354                        var s = document.createElement("script");
10355                        s.src = srcMatch[2];
10356                        var typeMatch = attrs.match(typeRe);
10357                        if(typeMatch && typeMatch[2]){
10358                            s.type = typeMatch[2];
10359                        }
10360                        hd.appendChild(s);
10361                     }else if(match[2] && match[2].length > 0){
10362                         if(window.execScript) {
10363                            window.execScript(match[2]);
10364                         } else {
10365                             /**
10366                              * eval:var:id
10367                              * eval:var:dom
10368                              * eval:var:html
10369                              * 
10370                              */
10371                            window.eval(match[2]);
10372                         }
10373                     }
10374                 }
10375                 var el = document.getElementById(id);
10376                 if(el){el.parentNode.removeChild(el);}
10377                 if(typeof callback == "function"){
10378                     callback();
10379                 }
10380             });
10381             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10382             return this;
10383         },
10384
10385         /**
10386          * Direct access to the UpdateManager update() method (takes the same parameters).
10387          * @param {String/Function} url The url for this request or a function to call to get the url
10388          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
10389          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10390          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
10391          * @return {Roo.Element} this
10392          */
10393         load : function(){
10394             var um = this.getUpdateManager();
10395             um.update.apply(um, arguments);
10396             return this;
10397         },
10398
10399         /**
10400         * Gets this element's UpdateManager
10401         * @return {Roo.UpdateManager} The UpdateManager
10402         */
10403         getUpdateManager : function(){
10404             if(!this.updateManager){
10405                 this.updateManager = new Roo.UpdateManager(this);
10406             }
10407             return this.updateManager;
10408         },
10409
10410         /**
10411          * Disables text selection for this element (normalized across browsers)
10412          * @return {Roo.Element} this
10413          */
10414         unselectable : function(){
10415             this.dom.unselectable = "on";
10416             this.swallowEvent("selectstart", true);
10417             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10418             this.addClass("x-unselectable");
10419             return this;
10420         },
10421
10422         /**
10423         * Calculates the x, y to center this element on the screen
10424         * @return {Array} The x, y values [x, y]
10425         */
10426         getCenterXY : function(){
10427             return this.getAlignToXY(document, 'c-c');
10428         },
10429
10430         /**
10431         * Centers the Element in either the viewport, or another Element.
10432         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10433         */
10434         center : function(centerIn){
10435             this.alignTo(centerIn || document, 'c-c');
10436             return this;
10437         },
10438
10439         /**
10440          * Tests various css rules/browsers to determine if this element uses a border box
10441          * @return {Boolean}
10442          */
10443         isBorderBox : function(){
10444             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10445         },
10446
10447         /**
10448          * Return a box {x, y, width, height} that can be used to set another elements
10449          * size/location to match this element.
10450          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10451          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10452          * @return {Object} box An object in the format {x, y, width, height}
10453          */
10454         getBox : function(contentBox, local){
10455             var xy;
10456             if(!local){
10457                 xy = this.getXY();
10458             }else{
10459                 var left = parseInt(this.getStyle("left"), 10) || 0;
10460                 var top = parseInt(this.getStyle("top"), 10) || 0;
10461                 xy = [left, top];
10462             }
10463             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10464             if(!contentBox){
10465                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10466             }else{
10467                 var l = this.getBorderWidth("l")+this.getPadding("l");
10468                 var r = this.getBorderWidth("r")+this.getPadding("r");
10469                 var t = this.getBorderWidth("t")+this.getPadding("t");
10470                 var b = this.getBorderWidth("b")+this.getPadding("b");
10471                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
10472             }
10473             bx.right = bx.x + bx.width;
10474             bx.bottom = bx.y + bx.height;
10475             return bx;
10476         },
10477
10478         /**
10479          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10480          for more information about the sides.
10481          * @param {String} sides
10482          * @return {Number}
10483          */
10484         getFrameWidth : function(sides, onlyContentBox){
10485             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10486         },
10487
10488         /**
10489          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
10490          * @param {Object} box The box to fill {x, y, width, height}
10491          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10492          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10493          * @return {Roo.Element} this
10494          */
10495         setBox : function(box, adjust, animate){
10496             var w = box.width, h = box.height;
10497             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10498                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10499                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10500             }
10501             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10502             return this;
10503         },
10504
10505         /**
10506          * Forces the browser to repaint this element
10507          * @return {Roo.Element} this
10508          */
10509          repaint : function(){
10510             var dom = this.dom;
10511             this.addClass("x-repaint");
10512             setTimeout(function(){
10513                 Roo.get(dom).removeClass("x-repaint");
10514             }, 1);
10515             return this;
10516         },
10517
10518         /**
10519          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10520          * then it returns the calculated width of the sides (see getPadding)
10521          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10522          * @return {Object/Number}
10523          */
10524         getMargins : function(side){
10525             if(!side){
10526                 return {
10527                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10528                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10529                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10530                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10531                 };
10532             }else{
10533                 return this.addStyles(side, El.margins);
10534              }
10535         },
10536
10537         // private
10538         addStyles : function(sides, styles){
10539             var val = 0, v, w;
10540             for(var i = 0, len = sides.length; i < len; i++){
10541                 v = this.getStyle(styles[sides.charAt(i)]);
10542                 if(v){
10543                      w = parseInt(v, 10);
10544                      if(w){ val += w; }
10545                 }
10546             }
10547             return val;
10548         },
10549
10550         /**
10551          * Creates a proxy element of this element
10552          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10553          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10554          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10555          * @return {Roo.Element} The new proxy element
10556          */
10557         createProxy : function(config, renderTo, matchBox){
10558             if(renderTo){
10559                 renderTo = Roo.getDom(renderTo);
10560             }else{
10561                 renderTo = document.body;
10562             }
10563             config = typeof config == "object" ?
10564                 config : {tag : "div", cls: config};
10565             var proxy = Roo.DomHelper.append(renderTo, config, true);
10566             if(matchBox){
10567                proxy.setBox(this.getBox());
10568             }
10569             return proxy;
10570         },
10571
10572         /**
10573          * Puts a mask over this element to disable user interaction. Requires core.css.
10574          * This method can only be applied to elements which accept child nodes.
10575          * @param {String} msg (optional) A message to display in the mask
10576          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10577          * @return {Element} The mask  element
10578          */
10579         mask : function(msg, msgCls)
10580         {
10581             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10582                 this.setStyle("position", "relative");
10583             }
10584             if(!this._mask){
10585                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10586             }
10587             
10588             this.addClass("x-masked");
10589             this._mask.setDisplayed(true);
10590             
10591             // we wander
10592             var z = 0;
10593             var dom = this.dom;
10594             while (dom && dom.style) {
10595                 if (!isNaN(parseInt(dom.style.zIndex))) {
10596                     z = Math.max(z, parseInt(dom.style.zIndex));
10597                 }
10598                 dom = dom.parentNode;
10599             }
10600             // if we are masking the body - then it hides everything..
10601             if (this.dom == document.body) {
10602                 z = 1000000;
10603                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10604                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10605             }
10606            
10607             if(typeof msg == 'string'){
10608                 if(!this._maskMsg){
10609                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10610                         cls: "roo-el-mask-msg", 
10611                         cn: [
10612                             {
10613                                 tag: 'i',
10614                                 cls: 'fa fa-spinner fa-spin'
10615                             },
10616                             {
10617                                 tag: 'div'
10618                             }   
10619                         ]
10620                     }, true);
10621                 }
10622                 var mm = this._maskMsg;
10623                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10624                 if (mm.dom.lastChild) { // weird IE issue?
10625                     mm.dom.lastChild.innerHTML = msg;
10626                 }
10627                 mm.setDisplayed(true);
10628                 mm.center(this);
10629                 mm.setStyle('z-index', z + 102);
10630             }
10631             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10632                 this._mask.setHeight(this.getHeight());
10633             }
10634             this._mask.setStyle('z-index', z + 100);
10635             
10636             return this._mask;
10637         },
10638
10639         /**
10640          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10641          * it is cached for reuse.
10642          */
10643         unmask : function(removeEl){
10644             if(this._mask){
10645                 if(removeEl === true){
10646                     this._mask.remove();
10647                     delete this._mask;
10648                     if(this._maskMsg){
10649                         this._maskMsg.remove();
10650                         delete this._maskMsg;
10651                     }
10652                 }else{
10653                     this._mask.setDisplayed(false);
10654                     if(this._maskMsg){
10655                         this._maskMsg.setDisplayed(false);
10656                     }
10657                 }
10658             }
10659             this.removeClass("x-masked");
10660         },
10661
10662         /**
10663          * Returns true if this element is masked
10664          * @return {Boolean}
10665          */
10666         isMasked : function(){
10667             return this._mask && this._mask.isVisible();
10668         },
10669
10670         /**
10671          * Creates an iframe shim for this element to keep selects and other windowed objects from
10672          * showing through.
10673          * @return {Roo.Element} The new shim element
10674          */
10675         createShim : function(){
10676             var el = document.createElement('iframe');
10677             el.frameBorder = 'no';
10678             el.className = 'roo-shim';
10679             if(Roo.isIE && Roo.isSecure){
10680                 el.src = Roo.SSL_SECURE_URL;
10681             }
10682             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10683             shim.autoBoxAdjust = false;
10684             return shim;
10685         },
10686
10687         /**
10688          * Removes this element from the DOM and deletes it from the cache
10689          */
10690         remove : function(){
10691             if(this.dom.parentNode){
10692                 this.dom.parentNode.removeChild(this.dom);
10693             }
10694             delete El.cache[this.dom.id];
10695         },
10696
10697         /**
10698          * Sets up event handlers to add and remove a css class when the mouse is over this element
10699          * @param {String} className
10700          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10701          * mouseout events for children elements
10702          * @return {Roo.Element} this
10703          */
10704         addClassOnOver : function(className, preventFlicker){
10705             this.on("mouseover", function(){
10706                 Roo.fly(this, '_internal').addClass(className);
10707             }, this.dom);
10708             var removeFn = function(e){
10709                 if(preventFlicker !== true || !e.within(this, true)){
10710                     Roo.fly(this, '_internal').removeClass(className);
10711                 }
10712             };
10713             this.on("mouseout", removeFn, this.dom);
10714             return this;
10715         },
10716
10717         /**
10718          * Sets up event handlers to add and remove a css class when this element has the focus
10719          * @param {String} className
10720          * @return {Roo.Element} this
10721          */
10722         addClassOnFocus : function(className){
10723             this.on("focus", function(){
10724                 Roo.fly(this, '_internal').addClass(className);
10725             }, this.dom);
10726             this.on("blur", function(){
10727                 Roo.fly(this, '_internal').removeClass(className);
10728             }, this.dom);
10729             return this;
10730         },
10731         /**
10732          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
10733          * @param {String} className
10734          * @return {Roo.Element} this
10735          */
10736         addClassOnClick : function(className){
10737             var dom = this.dom;
10738             this.on("mousedown", function(){
10739                 Roo.fly(dom, '_internal').addClass(className);
10740                 var d = Roo.get(document);
10741                 var fn = function(){
10742                     Roo.fly(dom, '_internal').removeClass(className);
10743                     d.removeListener("mouseup", fn);
10744                 };
10745                 d.on("mouseup", fn);
10746             });
10747             return this;
10748         },
10749
10750         /**
10751          * Stops the specified event from bubbling and optionally prevents the default action
10752          * @param {String} eventName
10753          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10754          * @return {Roo.Element} this
10755          */
10756         swallowEvent : function(eventName, preventDefault){
10757             var fn = function(e){
10758                 e.stopPropagation();
10759                 if(preventDefault){
10760                     e.preventDefault();
10761                 }
10762             };
10763             if(eventName instanceof Array){
10764                 for(var i = 0, len = eventName.length; i < len; i++){
10765                      this.on(eventName[i], fn);
10766                 }
10767                 return this;
10768             }
10769             this.on(eventName, fn);
10770             return this;
10771         },
10772
10773         /**
10774          * @private
10775          */
10776         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10777
10778         /**
10779          * Sizes this element to its parent element's dimensions performing
10780          * neccessary box adjustments.
10781          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10782          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10783          * @return {Roo.Element} this
10784          */
10785         fitToParent : function(monitorResize, targetParent) {
10786           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10787           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10788           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10789             return this;
10790           }
10791           var p = Roo.get(targetParent || this.dom.parentNode);
10792           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10793           if (monitorResize === true) {
10794             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10795             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10796           }
10797           return this;
10798         },
10799
10800         /**
10801          * Gets the next sibling, skipping text nodes
10802          * @return {HTMLElement} The next sibling or null
10803          */
10804         getNextSibling : function(){
10805             var n = this.dom.nextSibling;
10806             while(n && n.nodeType != 1){
10807                 n = n.nextSibling;
10808             }
10809             return n;
10810         },
10811
10812         /**
10813          * Gets the previous sibling, skipping text nodes
10814          * @return {HTMLElement} The previous sibling or null
10815          */
10816         getPrevSibling : function(){
10817             var n = this.dom.previousSibling;
10818             while(n && n.nodeType != 1){
10819                 n = n.previousSibling;
10820             }
10821             return n;
10822         },
10823
10824
10825         /**
10826          * Appends the passed element(s) to this element
10827          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10828          * @return {Roo.Element} this
10829          */
10830         appendChild: function(el){
10831             el = Roo.get(el);
10832             el.appendTo(this);
10833             return this;
10834         },
10835
10836         /**
10837          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10838          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10839          * automatically generated with the specified attributes.
10840          * @param {HTMLElement} insertBefore (optional) a child element of this element
10841          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10842          * @return {Roo.Element} The new child element
10843          */
10844         createChild: function(config, insertBefore, returnDom){
10845             config = config || {tag:'div'};
10846             if(insertBefore){
10847                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10848             }
10849             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10850         },
10851
10852         /**
10853          * Appends this element to the passed element
10854          * @param {String/HTMLElement/Element} el The new parent element
10855          * @return {Roo.Element} this
10856          */
10857         appendTo: function(el){
10858             el = Roo.getDom(el);
10859             el.appendChild(this.dom);
10860             return this;
10861         },
10862
10863         /**
10864          * Inserts this element before the passed element in the DOM
10865          * @param {String/HTMLElement/Element} el The element to insert before
10866          * @return {Roo.Element} this
10867          */
10868         insertBefore: function(el){
10869             el = Roo.getDom(el);
10870             el.parentNode.insertBefore(this.dom, el);
10871             return this;
10872         },
10873
10874         /**
10875          * Inserts this element after the passed element in the DOM
10876          * @param {String/HTMLElement/Element} el The element to insert after
10877          * @return {Roo.Element} this
10878          */
10879         insertAfter: function(el){
10880             el = Roo.getDom(el);
10881             el.parentNode.insertBefore(this.dom, el.nextSibling);
10882             return this;
10883         },
10884
10885         /**
10886          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10887          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10888          * @return {Roo.Element} The new child
10889          */
10890         insertFirst: function(el, returnDom){
10891             el = el || {};
10892             if(typeof el == 'object' && !el.nodeType){ // dh config
10893                 return this.createChild(el, this.dom.firstChild, returnDom);
10894             }else{
10895                 el = Roo.getDom(el);
10896                 this.dom.insertBefore(el, this.dom.firstChild);
10897                 return !returnDom ? Roo.get(el) : el;
10898             }
10899         },
10900
10901         /**
10902          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10903          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10904          * @param {String} where (optional) 'before' or 'after' defaults to before
10905          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10906          * @return {Roo.Element} the inserted Element
10907          */
10908         insertSibling: function(el, where, returnDom){
10909             where = where ? where.toLowerCase() : 'before';
10910             el = el || {};
10911             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10912
10913             if(typeof el == 'object' && !el.nodeType){ // dh config
10914                 if(where == 'after' && !this.dom.nextSibling){
10915                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10916                 }else{
10917                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10918                 }
10919
10920             }else{
10921                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10922                             where == 'before' ? this.dom : this.dom.nextSibling);
10923                 if(!returnDom){
10924                     rt = Roo.get(rt);
10925                 }
10926             }
10927             return rt;
10928         },
10929
10930         /**
10931          * Creates and wraps this element with another element
10932          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10933          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10934          * @return {HTMLElement/Element} The newly created wrapper element
10935          */
10936         wrap: function(config, returnDom){
10937             if(!config){
10938                 config = {tag: "div"};
10939             }
10940             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10941             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10942             return newEl;
10943         },
10944
10945         /**
10946          * Replaces the passed element with this element
10947          * @param {String/HTMLElement/Element} el The element to replace
10948          * @return {Roo.Element} this
10949          */
10950         replace: function(el){
10951             el = Roo.get(el);
10952             this.insertBefore(el);
10953             el.remove();
10954             return this;
10955         },
10956
10957         /**
10958          * Inserts an html fragment into this element
10959          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10960          * @param {String} html The HTML fragment
10961          * @param {Boolean} returnEl True to return an Roo.Element
10962          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10963          */
10964         insertHtml : function(where, html, returnEl){
10965             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10966             return returnEl ? Roo.get(el) : el;
10967         },
10968
10969         /**
10970          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10971          * @param {Object} o The object with the attributes
10972          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10973          * @return {Roo.Element} this
10974          */
10975         set : function(o, useSet){
10976             var el = this.dom;
10977             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10978             for(var attr in o){
10979                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10980                 if(attr=="cls"){
10981                     el.className = o["cls"];
10982                 }else{
10983                     if(useSet) {
10984                         el.setAttribute(attr, o[attr]);
10985                     } else {
10986                         el[attr] = o[attr];
10987                     }
10988                 }
10989             }
10990             if(o.style){
10991                 Roo.DomHelper.applyStyles(el, o.style);
10992             }
10993             return this;
10994         },
10995
10996         /**
10997          * Convenience method for constructing a KeyMap
10998          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
10999          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
11000          * @param {Function} fn The function to call
11001          * @param {Object} scope (optional) The scope of the function
11002          * @return {Roo.KeyMap} The KeyMap created
11003          */
11004         addKeyListener : function(key, fn, scope){
11005             var config;
11006             if(typeof key != "object" || key instanceof Array){
11007                 config = {
11008                     key: key,
11009                     fn: fn,
11010                     scope: scope
11011                 };
11012             }else{
11013                 config = {
11014                     key : key.key,
11015                     shift : key.shift,
11016                     ctrl : key.ctrl,
11017                     alt : key.alt,
11018                     fn: fn,
11019                     scope: scope
11020                 };
11021             }
11022             return new Roo.KeyMap(this, config);
11023         },
11024
11025         /**
11026          * Creates a KeyMap for this element
11027          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
11028          * @return {Roo.KeyMap} The KeyMap created
11029          */
11030         addKeyMap : function(config){
11031             return new Roo.KeyMap(this, config);
11032         },
11033
11034         /**
11035          * Returns true if this element is scrollable.
11036          * @return {Boolean}
11037          */
11038          isScrollable : function(){
11039             var dom = this.dom;
11040             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
11041         },
11042
11043         /**
11044          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
11045          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
11046          * @param {Number} value The new scroll value
11047          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11048          * @return {Element} this
11049          */
11050
11051         scrollTo : function(side, value, animate){
11052             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
11053             if(!animate || !A){
11054                 this.dom[prop] = value;
11055             }else{
11056                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
11057                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
11058             }
11059             return this;
11060         },
11061
11062         /**
11063          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
11064          * within this element's scrollable range.
11065          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
11066          * @param {Number} distance How far to scroll the element in pixels
11067          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11068          * @return {Boolean} Returns true if a scroll was triggered or false if the element
11069          * was scrolled as far as it could go.
11070          */
11071          scroll : function(direction, distance, animate){
11072              if(!this.isScrollable()){
11073                  return;
11074              }
11075              var el = this.dom;
11076              var l = el.scrollLeft, t = el.scrollTop;
11077              var w = el.scrollWidth, h = el.scrollHeight;
11078              var cw = el.clientWidth, ch = el.clientHeight;
11079              direction = direction.toLowerCase();
11080              var scrolled = false;
11081              var a = this.preanim(arguments, 2);
11082              switch(direction){
11083                  case "l":
11084                  case "left":
11085                      if(w - l > cw){
11086                          var v = Math.min(l + distance, w-cw);
11087                          this.scrollTo("left", v, a);
11088                          scrolled = true;
11089                      }
11090                      break;
11091                 case "r":
11092                 case "right":
11093                      if(l > 0){
11094                          var v = Math.max(l - distance, 0);
11095                          this.scrollTo("left", v, a);
11096                          scrolled = true;
11097                      }
11098                      break;
11099                 case "t":
11100                 case "top":
11101                 case "up":
11102                      if(t > 0){
11103                          var v = Math.max(t - distance, 0);
11104                          this.scrollTo("top", v, a);
11105                          scrolled = true;
11106                      }
11107                      break;
11108                 case "b":
11109                 case "bottom":
11110                 case "down":
11111                      if(h - t > ch){
11112                          var v = Math.min(t + distance, h-ch);
11113                          this.scrollTo("top", v, a);
11114                          scrolled = true;
11115                      }
11116                      break;
11117              }
11118              return scrolled;
11119         },
11120
11121         /**
11122          * Translates the passed page coordinates into left/top css values for this element
11123          * @param {Number/Array} x The page x or an array containing [x, y]
11124          * @param {Number} y The page y
11125          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
11126          */
11127         translatePoints : function(x, y){
11128             if(typeof x == 'object' || x instanceof Array){
11129                 y = x[1]; x = x[0];
11130             }
11131             var p = this.getStyle('position');
11132             var o = this.getXY();
11133
11134             var l = parseInt(this.getStyle('left'), 10);
11135             var t = parseInt(this.getStyle('top'), 10);
11136
11137             if(isNaN(l)){
11138                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
11139             }
11140             if(isNaN(t)){
11141                 t = (p == "relative") ? 0 : this.dom.offsetTop;
11142             }
11143
11144             return {left: (x - o[0] + l), top: (y - o[1] + t)};
11145         },
11146
11147         /**
11148          * Returns the current scroll position of the element.
11149          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
11150          */
11151         getScroll : function(){
11152             var d = this.dom, doc = document;
11153             if(d == doc || d == doc.body){
11154                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
11155                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
11156                 return {left: l, top: t};
11157             }else{
11158                 return {left: d.scrollLeft, top: d.scrollTop};
11159             }
11160         },
11161
11162         /**
11163          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
11164          * are convert to standard 6 digit hex color.
11165          * @param {String} attr The css attribute
11166          * @param {String} defaultValue The default value to use when a valid color isn't found
11167          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
11168          * YUI color anims.
11169          */
11170         getColor : function(attr, defaultValue, prefix){
11171             var v = this.getStyle(attr);
11172             if(!v || v == "transparent" || v == "inherit") {
11173                 return defaultValue;
11174             }
11175             var color = typeof prefix == "undefined" ? "#" : prefix;
11176             if(v.substr(0, 4) == "rgb("){
11177                 var rvs = v.slice(4, v.length -1).split(",");
11178                 for(var i = 0; i < 3; i++){
11179                     var h = parseInt(rvs[i]).toString(16);
11180                     if(h < 16){
11181                         h = "0" + h;
11182                     }
11183                     color += h;
11184                 }
11185             } else {
11186                 if(v.substr(0, 1) == "#"){
11187                     if(v.length == 4) {
11188                         for(var i = 1; i < 4; i++){
11189                             var c = v.charAt(i);
11190                             color +=  c + c;
11191                         }
11192                     }else if(v.length == 7){
11193                         color += v.substr(1);
11194                     }
11195                 }
11196             }
11197             return(color.length > 5 ? color.toLowerCase() : defaultValue);
11198         },
11199
11200         /**
11201          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11202          * gradient background, rounded corners and a 4-way shadow.
11203          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11204          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11205          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11206          * @return {Roo.Element} this
11207          */
11208         boxWrap : function(cls){
11209             cls = cls || 'x-box';
11210             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11211             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11212             return el;
11213         },
11214
11215         /**
11216          * Returns the value of a namespaced attribute from the element's underlying DOM node.
11217          * @param {String} namespace The namespace in which to look for the attribute
11218          * @param {String} name The attribute name
11219          * @return {String} The attribute value
11220          */
11221         getAttributeNS : Roo.isIE ? function(ns, name){
11222             var d = this.dom;
11223             var type = typeof d[ns+":"+name];
11224             if(type != 'undefined' && type != 'unknown'){
11225                 return d[ns+":"+name];
11226             }
11227             return d[name];
11228         } : function(ns, name){
11229             var d = this.dom;
11230             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11231         },
11232         
11233         
11234         /**
11235          * Sets or Returns the value the dom attribute value
11236          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11237          * @param {String} value (optional) The value to set the attribute to
11238          * @return {String} The attribute value
11239          */
11240         attr : function(name){
11241             if (arguments.length > 1) {
11242                 this.dom.setAttribute(name, arguments[1]);
11243                 return arguments[1];
11244             }
11245             if (typeof(name) == 'object') {
11246                 for(var i in name) {
11247                     this.attr(i, name[i]);
11248                 }
11249                 return name;
11250             }
11251             
11252             
11253             if (!this.dom.hasAttribute(name)) {
11254                 return undefined;
11255             }
11256             return this.dom.getAttribute(name);
11257         }
11258         
11259         
11260         
11261     };
11262
11263     var ep = El.prototype;
11264
11265     /**
11266      * Appends an event handler (Shorthand for addListener)
11267      * @param {String}   eventName     The type of event to append
11268      * @param {Function} fn        The method the event invokes
11269      * @param {Object} scope       (optional) The scope (this object) of the fn
11270      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11271      * @method
11272      */
11273     ep.on = ep.addListener;
11274         // backwards compat
11275     ep.mon = ep.addListener;
11276
11277     /**
11278      * Removes an event handler from this element (shorthand for removeListener)
11279      * @param {String} eventName the type of event to remove
11280      * @param {Function} fn the method the event invokes
11281      * @return {Roo.Element} this
11282      * @method
11283      */
11284     ep.un = ep.removeListener;
11285
11286     /**
11287      * true to automatically adjust width and height settings for box-model issues (default to true)
11288      */
11289     ep.autoBoxAdjust = true;
11290
11291     // private
11292     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11293
11294     // private
11295     El.addUnits = function(v, defaultUnit){
11296         if(v === "" || v == "auto"){
11297             return v;
11298         }
11299         if(v === undefined){
11300             return '';
11301         }
11302         if(typeof v == "number" || !El.unitPattern.test(v)){
11303             return v + (defaultUnit || 'px');
11304         }
11305         return v;
11306     };
11307
11308     // special markup used throughout Roo when box wrapping elements
11309     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
11310     /**
11311      * Visibility mode constant - Use visibility to hide element
11312      * @static
11313      * @type Number
11314      */
11315     El.VISIBILITY = 1;
11316     /**
11317      * Visibility mode constant - Use display to hide element
11318      * @static
11319      * @type Number
11320      */
11321     El.DISPLAY = 2;
11322
11323     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11324     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11325     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11326
11327
11328
11329     /**
11330      * @private
11331      */
11332     El.cache = {};
11333
11334     var docEl;
11335
11336     /**
11337      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11338      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11339      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11340      * @return {Element} The Element object
11341      * @static
11342      */
11343     El.get = function(el){
11344         var ex, elm, id;
11345         if(!el){ return null; }
11346         if(typeof el == "string"){ // element id
11347             if(!(elm = document.getElementById(el))){
11348                 return null;
11349             }
11350             if(ex = El.cache[el]){
11351                 ex.dom = elm;
11352             }else{
11353                 ex = El.cache[el] = new El(elm);
11354             }
11355             return ex;
11356         }else if(el.tagName){ // dom element
11357             if(!(id = el.id)){
11358                 id = Roo.id(el);
11359             }
11360             if(ex = El.cache[id]){
11361                 ex.dom = el;
11362             }else{
11363                 ex = El.cache[id] = new El(el);
11364             }
11365             return ex;
11366         }else if(el instanceof El){
11367             if(el != docEl){
11368                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11369                                                               // catch case where it hasn't been appended
11370                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11371             }
11372             return el;
11373         }else if(el.isComposite){
11374             return el;
11375         }else if(el instanceof Array){
11376             return El.select(el);
11377         }else if(el == document){
11378             // create a bogus element object representing the document object
11379             if(!docEl){
11380                 var f = function(){};
11381                 f.prototype = El.prototype;
11382                 docEl = new f();
11383                 docEl.dom = document;
11384             }
11385             return docEl;
11386         }
11387         return null;
11388     };
11389
11390     // private
11391     El.uncache = function(el){
11392         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11393             if(a[i]){
11394                 delete El.cache[a[i].id || a[i]];
11395             }
11396         }
11397     };
11398
11399     // private
11400     // Garbage collection - uncache elements/purge listeners on orphaned elements
11401     // so we don't hold a reference and cause the browser to retain them
11402     El.garbageCollect = function(){
11403         if(!Roo.enableGarbageCollector){
11404             clearInterval(El.collectorThread);
11405             return;
11406         }
11407         for(var eid in El.cache){
11408             var el = El.cache[eid], d = el.dom;
11409             // -------------------------------------------------------
11410             // Determining what is garbage:
11411             // -------------------------------------------------------
11412             // !d
11413             // dom node is null, definitely garbage
11414             // -------------------------------------------------------
11415             // !d.parentNode
11416             // no parentNode == direct orphan, definitely garbage
11417             // -------------------------------------------------------
11418             // !d.offsetParent && !document.getElementById(eid)
11419             // display none elements have no offsetParent so we will
11420             // also try to look it up by it's id. However, check
11421             // offsetParent first so we don't do unneeded lookups.
11422             // This enables collection of elements that are not orphans
11423             // directly, but somewhere up the line they have an orphan
11424             // parent.
11425             // -------------------------------------------------------
11426             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11427                 delete El.cache[eid];
11428                 if(d && Roo.enableListenerCollection){
11429                     E.purgeElement(d);
11430                 }
11431             }
11432         }
11433     }
11434     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11435
11436
11437     // dom is optional
11438     El.Flyweight = function(dom){
11439         this.dom = dom;
11440     };
11441     El.Flyweight.prototype = El.prototype;
11442
11443     El._flyweights = {};
11444     /**
11445      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11446      * the dom node can be overwritten by other code.
11447      * @param {String/HTMLElement} el The dom node or id
11448      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11449      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11450      * @static
11451      * @return {Element} The shared Element object
11452      */
11453     El.fly = function(el, named){
11454         named = named || '_global';
11455         el = Roo.getDom(el);
11456         if(!el){
11457             return null;
11458         }
11459         if(!El._flyweights[named]){
11460             El._flyweights[named] = new El.Flyweight();
11461         }
11462         El._flyweights[named].dom = el;
11463         return El._flyweights[named];
11464     };
11465
11466     /**
11467      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11468      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11469      * Shorthand of {@link Roo.Element#get}
11470      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11471      * @return {Element} The Element object
11472      * @member Roo
11473      * @method get
11474      */
11475     Roo.get = El.get;
11476     /**
11477      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11478      * the dom node can be overwritten by other code.
11479      * Shorthand of {@link Roo.Element#fly}
11480      * @param {String/HTMLElement} el The dom node or id
11481      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11482      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11483      * @static
11484      * @return {Element} The shared Element object
11485      * @member Roo
11486      * @method fly
11487      */
11488     Roo.fly = El.fly;
11489
11490     // speedy lookup for elements never to box adjust
11491     var noBoxAdjust = Roo.isStrict ? {
11492         select:1
11493     } : {
11494         input:1, select:1, textarea:1
11495     };
11496     if(Roo.isIE || Roo.isGecko){
11497         noBoxAdjust['button'] = 1;
11498     }
11499
11500
11501     Roo.EventManager.on(window, 'unload', function(){
11502         delete El.cache;
11503         delete El._flyweights;
11504     });
11505 })();
11506
11507
11508
11509
11510 if(Roo.DomQuery){
11511     Roo.Element.selectorFunction = Roo.DomQuery.select;
11512 }
11513
11514 Roo.Element.select = function(selector, unique, root){
11515     var els;
11516     if(typeof selector == "string"){
11517         els = Roo.Element.selectorFunction(selector, root);
11518     }else if(selector.length !== undefined){
11519         els = selector;
11520     }else{
11521         throw "Invalid selector";
11522     }
11523     if(unique === true){
11524         return new Roo.CompositeElement(els);
11525     }else{
11526         return new Roo.CompositeElementLite(els);
11527     }
11528 };
11529 /**
11530  * Selects elements based on the passed CSS selector to enable working on them as 1.
11531  * @param {String/Array} selector The CSS selector or an array of elements
11532  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11533  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11534  * @return {CompositeElementLite/CompositeElement}
11535  * @member Roo
11536  * @method select
11537  */
11538 Roo.select = Roo.Element.select;
11539
11540
11541
11542
11543
11544
11545
11546
11547
11548
11549
11550
11551
11552
11553 /*
11554  * Based on:
11555  * Ext JS Library 1.1.1
11556  * Copyright(c) 2006-2007, Ext JS, LLC.
11557  *
11558  * Originally Released Under LGPL - original licence link has changed is not relivant.
11559  *
11560  * Fork - LGPL
11561  * <script type="text/javascript">
11562  */
11563
11564
11565
11566 //Notifies Element that fx methods are available
11567 Roo.enableFx = true;
11568
11569 /**
11570  * @class Roo.Fx
11571  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11572  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11573  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11574  * Element effects to work.</p><br/>
11575  *
11576  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11577  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11578  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11579  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11580  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11581  * expected results and should be done with care.</p><br/>
11582  *
11583  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11584  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11585 <pre>
11586 Value  Description
11587 -----  -----------------------------
11588 tl     The top left corner
11589 t      The center of the top edge
11590 tr     The top right corner
11591 l      The center of the left edge
11592 r      The center of the right edge
11593 bl     The bottom left corner
11594 b      The center of the bottom edge
11595 br     The bottom right corner
11596 </pre>
11597  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11598  * below are common options that can be passed to any Fx method.</b>
11599  * @cfg {Function} callback A function called when the effect is finished
11600  * @cfg {Object} scope The scope of the effect function
11601  * @cfg {String} easing A valid Easing value for the effect
11602  * @cfg {String} afterCls A css class to apply after the effect
11603  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11604  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11605  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11606  * effects that end with the element being visually hidden, ignored otherwise)
11607  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11608  * a function which returns such a specification that will be applied to the Element after the effect finishes
11609  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11610  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
11611  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11612  */
11613 Roo.Fx = {
11614         /**
11615          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11616          * origin for the slide effect.  This function automatically handles wrapping the element with
11617          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11618          * Usage:
11619          *<pre><code>
11620 // default: slide the element in from the top
11621 el.slideIn();
11622
11623 // custom: slide the element in from the right with a 2-second duration
11624 el.slideIn('r', { duration: 2 });
11625
11626 // common config options shown with default values
11627 el.slideIn('t', {
11628     easing: 'easeOut',
11629     duration: .5
11630 });
11631 </code></pre>
11632          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11633          * @param {Object} options (optional) Object literal with any of the Fx config options
11634          * @return {Roo.Element} The Element
11635          */
11636     slideIn : function(anchor, o){
11637         var el = this.getFxEl();
11638         o = o || {};
11639
11640         el.queueFx(o, function(){
11641
11642             anchor = anchor || "t";
11643
11644             // fix display to visibility
11645             this.fixDisplay();
11646
11647             // restore values after effect
11648             var r = this.getFxRestore();
11649             var b = this.getBox();
11650             // fixed size for slide
11651             this.setSize(b);
11652
11653             // wrap if needed
11654             var wrap = this.fxWrap(r.pos, o, "hidden");
11655
11656             var st = this.dom.style;
11657             st.visibility = "visible";
11658             st.position = "absolute";
11659
11660             // clear out temp styles after slide and unwrap
11661             var after = function(){
11662                 el.fxUnwrap(wrap, r.pos, o);
11663                 st.width = r.width;
11664                 st.height = r.height;
11665                 el.afterFx(o);
11666             };
11667             // time to calc the positions
11668             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11669
11670             switch(anchor.toLowerCase()){
11671                 case "t":
11672                     wrap.setSize(b.width, 0);
11673                     st.left = st.bottom = "0";
11674                     a = {height: bh};
11675                 break;
11676                 case "l":
11677                     wrap.setSize(0, b.height);
11678                     st.right = st.top = "0";
11679                     a = {width: bw};
11680                 break;
11681                 case "r":
11682                     wrap.setSize(0, b.height);
11683                     wrap.setX(b.right);
11684                     st.left = st.top = "0";
11685                     a = {width: bw, points: pt};
11686                 break;
11687                 case "b":
11688                     wrap.setSize(b.width, 0);
11689                     wrap.setY(b.bottom);
11690                     st.left = st.top = "0";
11691                     a = {height: bh, points: pt};
11692                 break;
11693                 case "tl":
11694                     wrap.setSize(0, 0);
11695                     st.right = st.bottom = "0";
11696                     a = {width: bw, height: bh};
11697                 break;
11698                 case "bl":
11699                     wrap.setSize(0, 0);
11700                     wrap.setY(b.y+b.height);
11701                     st.right = st.top = "0";
11702                     a = {width: bw, height: bh, points: pt};
11703                 break;
11704                 case "br":
11705                     wrap.setSize(0, 0);
11706                     wrap.setXY([b.right, b.bottom]);
11707                     st.left = st.top = "0";
11708                     a = {width: bw, height: bh, points: pt};
11709                 break;
11710                 case "tr":
11711                     wrap.setSize(0, 0);
11712                     wrap.setX(b.x+b.width);
11713                     st.left = st.bottom = "0";
11714                     a = {width: bw, height: bh, points: pt};
11715                 break;
11716             }
11717             this.dom.style.visibility = "visible";
11718             wrap.show();
11719
11720             arguments.callee.anim = wrap.fxanim(a,
11721                 o,
11722                 'motion',
11723                 .5,
11724                 'easeOut', after);
11725         });
11726         return this;
11727     },
11728     
11729         /**
11730          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11731          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11732          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11733          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11734          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11735          * Usage:
11736          *<pre><code>
11737 // default: slide the element out to the top
11738 el.slideOut();
11739
11740 // custom: slide the element out to the right with a 2-second duration
11741 el.slideOut('r', { duration: 2 });
11742
11743 // common config options shown with default values
11744 el.slideOut('t', {
11745     easing: 'easeOut',
11746     duration: .5,
11747     remove: false,
11748     useDisplay: false
11749 });
11750 </code></pre>
11751          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11752          * @param {Object} options (optional) Object literal with any of the Fx config options
11753          * @return {Roo.Element} The Element
11754          */
11755     slideOut : function(anchor, o){
11756         var el = this.getFxEl();
11757         o = o || {};
11758
11759         el.queueFx(o, function(){
11760
11761             anchor = anchor || "t";
11762
11763             // restore values after effect
11764             var r = this.getFxRestore();
11765             
11766             var b = this.getBox();
11767             // fixed size for slide
11768             this.setSize(b);
11769
11770             // wrap if needed
11771             var wrap = this.fxWrap(r.pos, o, "visible");
11772
11773             var st = this.dom.style;
11774             st.visibility = "visible";
11775             st.position = "absolute";
11776
11777             wrap.setSize(b);
11778
11779             var after = function(){
11780                 if(o.useDisplay){
11781                     el.setDisplayed(false);
11782                 }else{
11783                     el.hide();
11784                 }
11785
11786                 el.fxUnwrap(wrap, r.pos, o);
11787
11788                 st.width = r.width;
11789                 st.height = r.height;
11790
11791                 el.afterFx(o);
11792             };
11793
11794             var a, zero = {to: 0};
11795             switch(anchor.toLowerCase()){
11796                 case "t":
11797                     st.left = st.bottom = "0";
11798                     a = {height: zero};
11799                 break;
11800                 case "l":
11801                     st.right = st.top = "0";
11802                     a = {width: zero};
11803                 break;
11804                 case "r":
11805                     st.left = st.top = "0";
11806                     a = {width: zero, points: {to:[b.right, b.y]}};
11807                 break;
11808                 case "b":
11809                     st.left = st.top = "0";
11810                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11811                 break;
11812                 case "tl":
11813                     st.right = st.bottom = "0";
11814                     a = {width: zero, height: zero};
11815                 break;
11816                 case "bl":
11817                     st.right = st.top = "0";
11818                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11819                 break;
11820                 case "br":
11821                     st.left = st.top = "0";
11822                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11823                 break;
11824                 case "tr":
11825                     st.left = st.bottom = "0";
11826                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11827                 break;
11828             }
11829
11830             arguments.callee.anim = wrap.fxanim(a,
11831                 o,
11832                 'motion',
11833                 .5,
11834                 "easeOut", after);
11835         });
11836         return this;
11837     },
11838
11839         /**
11840          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11841          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11842          * The element must be removed from the DOM using the 'remove' config option if desired.
11843          * Usage:
11844          *<pre><code>
11845 // default
11846 el.puff();
11847
11848 // common config options shown with default values
11849 el.puff({
11850     easing: 'easeOut',
11851     duration: .5,
11852     remove: false,
11853     useDisplay: false
11854 });
11855 </code></pre>
11856          * @param {Object} options (optional) Object literal with any of the Fx config options
11857          * @return {Roo.Element} The Element
11858          */
11859     puff : function(o){
11860         var el = this.getFxEl();
11861         o = o || {};
11862
11863         el.queueFx(o, function(){
11864             this.clearOpacity();
11865             this.show();
11866
11867             // restore values after effect
11868             var r = this.getFxRestore();
11869             var st = this.dom.style;
11870
11871             var after = function(){
11872                 if(o.useDisplay){
11873                     el.setDisplayed(false);
11874                 }else{
11875                     el.hide();
11876                 }
11877
11878                 el.clearOpacity();
11879
11880                 el.setPositioning(r.pos);
11881                 st.width = r.width;
11882                 st.height = r.height;
11883                 st.fontSize = '';
11884                 el.afterFx(o);
11885             };
11886
11887             var width = this.getWidth();
11888             var height = this.getHeight();
11889
11890             arguments.callee.anim = this.fxanim({
11891                     width : {to: this.adjustWidth(width * 2)},
11892                     height : {to: this.adjustHeight(height * 2)},
11893                     points : {by: [-(width * .5), -(height * .5)]},
11894                     opacity : {to: 0},
11895                     fontSize: {to:200, unit: "%"}
11896                 },
11897                 o,
11898                 'motion',
11899                 .5,
11900                 "easeOut", after);
11901         });
11902         return this;
11903     },
11904
11905         /**
11906          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11907          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11908          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11909          * Usage:
11910          *<pre><code>
11911 // default
11912 el.switchOff();
11913
11914 // all config options shown with default values
11915 el.switchOff({
11916     easing: 'easeIn',
11917     duration: .3,
11918     remove: false,
11919     useDisplay: false
11920 });
11921 </code></pre>
11922          * @param {Object} options (optional) Object literal with any of the Fx config options
11923          * @return {Roo.Element} The Element
11924          */
11925     switchOff : function(o){
11926         var el = this.getFxEl();
11927         o = o || {};
11928
11929         el.queueFx(o, function(){
11930             this.clearOpacity();
11931             this.clip();
11932
11933             // restore values after effect
11934             var r = this.getFxRestore();
11935             var st = this.dom.style;
11936
11937             var after = function(){
11938                 if(o.useDisplay){
11939                     el.setDisplayed(false);
11940                 }else{
11941                     el.hide();
11942                 }
11943
11944                 el.clearOpacity();
11945                 el.setPositioning(r.pos);
11946                 st.width = r.width;
11947                 st.height = r.height;
11948
11949                 el.afterFx(o);
11950             };
11951
11952             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11953                 this.clearOpacity();
11954                 (function(){
11955                     this.fxanim({
11956                         height:{to:1},
11957                         points:{by:[0, this.getHeight() * .5]}
11958                     }, o, 'motion', 0.3, 'easeIn', after);
11959                 }).defer(100, this);
11960             });
11961         });
11962         return this;
11963     },
11964
11965     /**
11966      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11967      * changed using the "attr" config option) and then fading back to the original color. If no original
11968      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11969      * Usage:
11970 <pre><code>
11971 // default: highlight background to yellow
11972 el.highlight();
11973
11974 // custom: highlight foreground text to blue for 2 seconds
11975 el.highlight("0000ff", { attr: 'color', duration: 2 });
11976
11977 // common config options shown with default values
11978 el.highlight("ffff9c", {
11979     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11980     endColor: (current color) or "ffffff",
11981     easing: 'easeIn',
11982     duration: 1
11983 });
11984 </code></pre>
11985      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11986      * @param {Object} options (optional) Object literal with any of the Fx config options
11987      * @return {Roo.Element} The Element
11988      */ 
11989     highlight : function(color, o){
11990         var el = this.getFxEl();
11991         o = o || {};
11992
11993         el.queueFx(o, function(){
11994             color = color || "ffff9c";
11995             attr = o.attr || "backgroundColor";
11996
11997             this.clearOpacity();
11998             this.show();
11999
12000             var origColor = this.getColor(attr);
12001             var restoreColor = this.dom.style[attr];
12002             endColor = (o.endColor || origColor) || "ffffff";
12003
12004             var after = function(){
12005                 el.dom.style[attr] = restoreColor;
12006                 el.afterFx(o);
12007             };
12008
12009             var a = {};
12010             a[attr] = {from: color, to: endColor};
12011             arguments.callee.anim = this.fxanim(a,
12012                 o,
12013                 'color',
12014                 1,
12015                 'easeIn', after);
12016         });
12017         return this;
12018     },
12019
12020    /**
12021     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
12022     * Usage:
12023 <pre><code>
12024 // default: a single light blue ripple
12025 el.frame();
12026
12027 // custom: 3 red ripples lasting 3 seconds total
12028 el.frame("ff0000", 3, { duration: 3 });
12029
12030 // common config options shown with default values
12031 el.frame("C3DAF9", 1, {
12032     duration: 1 //duration of entire animation (not each individual ripple)
12033     // Note: Easing is not configurable and will be ignored if included
12034 });
12035 </code></pre>
12036     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
12037     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
12038     * @param {Object} options (optional) Object literal with any of the Fx config options
12039     * @return {Roo.Element} The Element
12040     */
12041     frame : function(color, count, o){
12042         var el = this.getFxEl();
12043         o = o || {};
12044
12045         el.queueFx(o, function(){
12046             color = color || "#C3DAF9";
12047             if(color.length == 6){
12048                 color = "#" + color;
12049             }
12050             count = count || 1;
12051             duration = o.duration || 1;
12052             this.show();
12053
12054             var b = this.getBox();
12055             var animFn = function(){
12056                 var proxy = this.createProxy({
12057
12058                      style:{
12059                         visbility:"hidden",
12060                         position:"absolute",
12061                         "z-index":"35000", // yee haw
12062                         border:"0px solid " + color
12063                      }
12064                   });
12065                 var scale = Roo.isBorderBox ? 2 : 1;
12066                 proxy.animate({
12067                     top:{from:b.y, to:b.y - 20},
12068                     left:{from:b.x, to:b.x - 20},
12069                     borderWidth:{from:0, to:10},
12070                     opacity:{from:1, to:0},
12071                     height:{from:b.height, to:(b.height + (20*scale))},
12072                     width:{from:b.width, to:(b.width + (20*scale))}
12073                 }, duration, function(){
12074                     proxy.remove();
12075                 });
12076                 if(--count > 0){
12077                      animFn.defer((duration/2)*1000, this);
12078                 }else{
12079                     el.afterFx(o);
12080                 }
12081             };
12082             animFn.call(this);
12083         });
12084         return this;
12085     },
12086
12087    /**
12088     * Creates a pause before any subsequent queued effects begin.  If there are
12089     * no effects queued after the pause it will have no effect.
12090     * Usage:
12091 <pre><code>
12092 el.pause(1);
12093 </code></pre>
12094     * @param {Number} seconds The length of time to pause (in seconds)
12095     * @return {Roo.Element} The Element
12096     */
12097     pause : function(seconds){
12098         var el = this.getFxEl();
12099         var o = {};
12100
12101         el.queueFx(o, function(){
12102             setTimeout(function(){
12103                 el.afterFx(o);
12104             }, seconds * 1000);
12105         });
12106         return this;
12107     },
12108
12109    /**
12110     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
12111     * using the "endOpacity" config option.
12112     * Usage:
12113 <pre><code>
12114 // default: fade in from opacity 0 to 100%
12115 el.fadeIn();
12116
12117 // custom: fade in from opacity 0 to 75% over 2 seconds
12118 el.fadeIn({ endOpacity: .75, duration: 2});
12119
12120 // common config options shown with default values
12121 el.fadeIn({
12122     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
12123     easing: 'easeOut',
12124     duration: .5
12125 });
12126 </code></pre>
12127     * @param {Object} options (optional) Object literal with any of the Fx config options
12128     * @return {Roo.Element} The Element
12129     */
12130     fadeIn : function(o){
12131         var el = this.getFxEl();
12132         o = o || {};
12133         el.queueFx(o, function(){
12134             this.setOpacity(0);
12135             this.fixDisplay();
12136             this.dom.style.visibility = 'visible';
12137             var to = o.endOpacity || 1;
12138             arguments.callee.anim = this.fxanim({opacity:{to:to}},
12139                 o, null, .5, "easeOut", function(){
12140                 if(to == 1){
12141                     this.clearOpacity();
12142                 }
12143                 el.afterFx(o);
12144             });
12145         });
12146         return this;
12147     },
12148
12149    /**
12150     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
12151     * using the "endOpacity" config option.
12152     * Usage:
12153 <pre><code>
12154 // default: fade out from the element's current opacity to 0
12155 el.fadeOut();
12156
12157 // custom: fade out from the element's current opacity to 25% over 2 seconds
12158 el.fadeOut({ endOpacity: .25, duration: 2});
12159
12160 // common config options shown with default values
12161 el.fadeOut({
12162     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
12163     easing: 'easeOut',
12164     duration: .5
12165     remove: false,
12166     useDisplay: false
12167 });
12168 </code></pre>
12169     * @param {Object} options (optional) Object literal with any of the Fx config options
12170     * @return {Roo.Element} The Element
12171     */
12172     fadeOut : function(o){
12173         var el = this.getFxEl();
12174         o = o || {};
12175         el.queueFx(o, function(){
12176             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
12177                 o, null, .5, "easeOut", function(){
12178                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
12179                      this.dom.style.display = "none";
12180                 }else{
12181                      this.dom.style.visibility = "hidden";
12182                 }
12183                 this.clearOpacity();
12184                 el.afterFx(o);
12185             });
12186         });
12187         return this;
12188     },
12189
12190    /**
12191     * Animates the transition of an element's dimensions from a starting height/width
12192     * to an ending height/width.
12193     * Usage:
12194 <pre><code>
12195 // change height and width to 100x100 pixels
12196 el.scale(100, 100);
12197
12198 // common config options shown with default values.  The height and width will default to
12199 // the element's existing values if passed as null.
12200 el.scale(
12201     [element's width],
12202     [element's height], {
12203     easing: 'easeOut',
12204     duration: .35
12205 });
12206 </code></pre>
12207     * @param {Number} width  The new width (pass undefined to keep the original width)
12208     * @param {Number} height  The new height (pass undefined to keep the original height)
12209     * @param {Object} options (optional) Object literal with any of the Fx config options
12210     * @return {Roo.Element} The Element
12211     */
12212     scale : function(w, h, o){
12213         this.shift(Roo.apply({}, o, {
12214             width: w,
12215             height: h
12216         }));
12217         return this;
12218     },
12219
12220    /**
12221     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12222     * Any of these properties not specified in the config object will not be changed.  This effect 
12223     * requires that at least one new dimension, position or opacity setting must be passed in on
12224     * the config object in order for the function to have any effect.
12225     * Usage:
12226 <pre><code>
12227 // slide the element horizontally to x position 200 while changing the height and opacity
12228 el.shift({ x: 200, height: 50, opacity: .8 });
12229
12230 // common config options shown with default values.
12231 el.shift({
12232     width: [element's width],
12233     height: [element's height],
12234     x: [element's x position],
12235     y: [element's y position],
12236     opacity: [element's opacity],
12237     easing: 'easeOut',
12238     duration: .35
12239 });
12240 </code></pre>
12241     * @param {Object} options  Object literal with any of the Fx config options
12242     * @return {Roo.Element} The Element
12243     */
12244     shift : function(o){
12245         var el = this.getFxEl();
12246         o = o || {};
12247         el.queueFx(o, function(){
12248             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12249             if(w !== undefined){
12250                 a.width = {to: this.adjustWidth(w)};
12251             }
12252             if(h !== undefined){
12253                 a.height = {to: this.adjustHeight(h)};
12254             }
12255             if(x !== undefined || y !== undefined){
12256                 a.points = {to: [
12257                     x !== undefined ? x : this.getX(),
12258                     y !== undefined ? y : this.getY()
12259                 ]};
12260             }
12261             if(op !== undefined){
12262                 a.opacity = {to: op};
12263             }
12264             if(o.xy !== undefined){
12265                 a.points = {to: o.xy};
12266             }
12267             arguments.callee.anim = this.fxanim(a,
12268                 o, 'motion', .35, "easeOut", function(){
12269                 el.afterFx(o);
12270             });
12271         });
12272         return this;
12273     },
12274
12275         /**
12276          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12277          * ending point of the effect.
12278          * Usage:
12279          *<pre><code>
12280 // default: slide the element downward while fading out
12281 el.ghost();
12282
12283 // custom: slide the element out to the right with a 2-second duration
12284 el.ghost('r', { duration: 2 });
12285
12286 // common config options shown with default values
12287 el.ghost('b', {
12288     easing: 'easeOut',
12289     duration: .5
12290     remove: false,
12291     useDisplay: false
12292 });
12293 </code></pre>
12294          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12295          * @param {Object} options (optional) Object literal with any of the Fx config options
12296          * @return {Roo.Element} The Element
12297          */
12298     ghost : function(anchor, o){
12299         var el = this.getFxEl();
12300         o = o || {};
12301
12302         el.queueFx(o, function(){
12303             anchor = anchor || "b";
12304
12305             // restore values after effect
12306             var r = this.getFxRestore();
12307             var w = this.getWidth(),
12308                 h = this.getHeight();
12309
12310             var st = this.dom.style;
12311
12312             var after = function(){
12313                 if(o.useDisplay){
12314                     el.setDisplayed(false);
12315                 }else{
12316                     el.hide();
12317                 }
12318
12319                 el.clearOpacity();
12320                 el.setPositioning(r.pos);
12321                 st.width = r.width;
12322                 st.height = r.height;
12323
12324                 el.afterFx(o);
12325             };
12326
12327             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12328             switch(anchor.toLowerCase()){
12329                 case "t":
12330                     pt.by = [0, -h];
12331                 break;
12332                 case "l":
12333                     pt.by = [-w, 0];
12334                 break;
12335                 case "r":
12336                     pt.by = [w, 0];
12337                 break;
12338                 case "b":
12339                     pt.by = [0, h];
12340                 break;
12341                 case "tl":
12342                     pt.by = [-w, -h];
12343                 break;
12344                 case "bl":
12345                     pt.by = [-w, h];
12346                 break;
12347                 case "br":
12348                     pt.by = [w, h];
12349                 break;
12350                 case "tr":
12351                     pt.by = [w, -h];
12352                 break;
12353             }
12354
12355             arguments.callee.anim = this.fxanim(a,
12356                 o,
12357                 'motion',
12358                 .5,
12359                 "easeOut", after);
12360         });
12361         return this;
12362     },
12363
12364         /**
12365          * Ensures that all effects queued after syncFx is called on the element are
12366          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12367          * @return {Roo.Element} The Element
12368          */
12369     syncFx : function(){
12370         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12371             block : false,
12372             concurrent : true,
12373             stopFx : false
12374         });
12375         return this;
12376     },
12377
12378         /**
12379          * Ensures that all effects queued after sequenceFx is called on the element are
12380          * run in sequence.  This is the opposite of {@link #syncFx}.
12381          * @return {Roo.Element} The Element
12382          */
12383     sequenceFx : function(){
12384         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12385             block : false,
12386             concurrent : false,
12387             stopFx : false
12388         });
12389         return this;
12390     },
12391
12392         /* @private */
12393     nextFx : function(){
12394         var ef = this.fxQueue[0];
12395         if(ef){
12396             ef.call(this);
12397         }
12398     },
12399
12400         /**
12401          * Returns true if the element has any effects actively running or queued, else returns false.
12402          * @return {Boolean} True if element has active effects, else false
12403          */
12404     hasActiveFx : function(){
12405         return this.fxQueue && this.fxQueue[0];
12406     },
12407
12408         /**
12409          * Stops any running effects and clears the element's internal effects queue if it contains
12410          * any additional effects that haven't started yet.
12411          * @return {Roo.Element} The Element
12412          */
12413     stopFx : function(){
12414         if(this.hasActiveFx()){
12415             var cur = this.fxQueue[0];
12416             if(cur && cur.anim && cur.anim.isAnimated()){
12417                 this.fxQueue = [cur]; // clear out others
12418                 cur.anim.stop(true);
12419             }
12420         }
12421         return this;
12422     },
12423
12424         /* @private */
12425     beforeFx : function(o){
12426         if(this.hasActiveFx() && !o.concurrent){
12427            if(o.stopFx){
12428                this.stopFx();
12429                return true;
12430            }
12431            return false;
12432         }
12433         return true;
12434     },
12435
12436         /**
12437          * Returns true if the element is currently blocking so that no other effect can be queued
12438          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12439          * used to ensure that an effect initiated by a user action runs to completion prior to the
12440          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12441          * @return {Boolean} True if blocking, else false
12442          */
12443     hasFxBlock : function(){
12444         var q = this.fxQueue;
12445         return q && q[0] && q[0].block;
12446     },
12447
12448         /* @private */
12449     queueFx : function(o, fn){
12450         if(!this.fxQueue){
12451             this.fxQueue = [];
12452         }
12453         if(!this.hasFxBlock()){
12454             Roo.applyIf(o, this.fxDefaults);
12455             if(!o.concurrent){
12456                 var run = this.beforeFx(o);
12457                 fn.block = o.block;
12458                 this.fxQueue.push(fn);
12459                 if(run){
12460                     this.nextFx();
12461                 }
12462             }else{
12463                 fn.call(this);
12464             }
12465         }
12466         return this;
12467     },
12468
12469         /* @private */
12470     fxWrap : function(pos, o, vis){
12471         var wrap;
12472         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12473             var wrapXY;
12474             if(o.fixPosition){
12475                 wrapXY = this.getXY();
12476             }
12477             var div = document.createElement("div");
12478             div.style.visibility = vis;
12479             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12480             wrap.setPositioning(pos);
12481             if(wrap.getStyle("position") == "static"){
12482                 wrap.position("relative");
12483             }
12484             this.clearPositioning('auto');
12485             wrap.clip();
12486             wrap.dom.appendChild(this.dom);
12487             if(wrapXY){
12488                 wrap.setXY(wrapXY);
12489             }
12490         }
12491         return wrap;
12492     },
12493
12494         /* @private */
12495     fxUnwrap : function(wrap, pos, o){
12496         this.clearPositioning();
12497         this.setPositioning(pos);
12498         if(!o.wrap){
12499             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12500             wrap.remove();
12501         }
12502     },
12503
12504         /* @private */
12505     getFxRestore : function(){
12506         var st = this.dom.style;
12507         return {pos: this.getPositioning(), width: st.width, height : st.height};
12508     },
12509
12510         /* @private */
12511     afterFx : function(o){
12512         if(o.afterStyle){
12513             this.applyStyles(o.afterStyle);
12514         }
12515         if(o.afterCls){
12516             this.addClass(o.afterCls);
12517         }
12518         if(o.remove === true){
12519             this.remove();
12520         }
12521         Roo.callback(o.callback, o.scope, [this]);
12522         if(!o.concurrent){
12523             this.fxQueue.shift();
12524             this.nextFx();
12525         }
12526     },
12527
12528         /* @private */
12529     getFxEl : function(){ // support for composite element fx
12530         return Roo.get(this.dom);
12531     },
12532
12533         /* @private */
12534     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12535         animType = animType || 'run';
12536         opt = opt || {};
12537         var anim = Roo.lib.Anim[animType](
12538             this.dom, args,
12539             (opt.duration || defaultDur) || .35,
12540             (opt.easing || defaultEase) || 'easeOut',
12541             function(){
12542                 Roo.callback(cb, this);
12543             },
12544             this
12545         );
12546         opt.anim = anim;
12547         return anim;
12548     }
12549 };
12550
12551 // backwords compat
12552 Roo.Fx.resize = Roo.Fx.scale;
12553
12554 //When included, Roo.Fx is automatically applied to Element so that all basic
12555 //effects are available directly via the Element API
12556 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12557  * Based on:
12558  * Ext JS Library 1.1.1
12559  * Copyright(c) 2006-2007, Ext JS, LLC.
12560  *
12561  * Originally Released Under LGPL - original licence link has changed is not relivant.
12562  *
12563  * Fork - LGPL
12564  * <script type="text/javascript">
12565  */
12566
12567
12568 /**
12569  * @class Roo.CompositeElement
12570  * Standard composite class. Creates a Roo.Element for every element in the collection.
12571  * <br><br>
12572  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12573  * actions will be performed on all the elements in this collection.</b>
12574  * <br><br>
12575  * All methods return <i>this</i> and can be chained.
12576  <pre><code>
12577  var els = Roo.select("#some-el div.some-class", true);
12578  // or select directly from an existing element
12579  var el = Roo.get('some-el');
12580  el.select('div.some-class', true);
12581
12582  els.setWidth(100); // all elements become 100 width
12583  els.hide(true); // all elements fade out and hide
12584  // or
12585  els.setWidth(100).hide(true);
12586  </code></pre>
12587  */
12588 Roo.CompositeElement = function(els){
12589     this.elements = [];
12590     this.addElements(els);
12591 };
12592 Roo.CompositeElement.prototype = {
12593     isComposite: true,
12594     addElements : function(els){
12595         if(!els) {
12596             return this;
12597         }
12598         if(typeof els == "string"){
12599             els = Roo.Element.selectorFunction(els);
12600         }
12601         var yels = this.elements;
12602         var index = yels.length-1;
12603         for(var i = 0, len = els.length; i < len; i++) {
12604                 yels[++index] = Roo.get(els[i]);
12605         }
12606         return this;
12607     },
12608
12609     /**
12610     * Clears this composite and adds the elements returned by the passed selector.
12611     * @param {String/Array} els A string CSS selector, an array of elements or an element
12612     * @return {CompositeElement} this
12613     */
12614     fill : function(els){
12615         this.elements = [];
12616         this.add(els);
12617         return this;
12618     },
12619
12620     /**
12621     * Filters this composite to only elements that match the passed selector.
12622     * @param {String} selector A string CSS selector
12623     * @param {Boolean} inverse return inverse filter (not matches)
12624     * @return {CompositeElement} this
12625     */
12626     filter : function(selector, inverse){
12627         var els = [];
12628         inverse = inverse || false;
12629         this.each(function(el){
12630             var match = inverse ? !el.is(selector) : el.is(selector);
12631             if(match){
12632                 els[els.length] = el.dom;
12633             }
12634         });
12635         this.fill(els);
12636         return this;
12637     },
12638
12639     invoke : function(fn, args){
12640         var els = this.elements;
12641         for(var i = 0, len = els.length; i < len; i++) {
12642                 Roo.Element.prototype[fn].apply(els[i], args);
12643         }
12644         return this;
12645     },
12646     /**
12647     * Adds elements to this composite.
12648     * @param {String/Array} els A string CSS selector, an array of elements or an element
12649     * @return {CompositeElement} this
12650     */
12651     add : function(els){
12652         if(typeof els == "string"){
12653             this.addElements(Roo.Element.selectorFunction(els));
12654         }else if(els.length !== undefined){
12655             this.addElements(els);
12656         }else{
12657             this.addElements([els]);
12658         }
12659         return this;
12660     },
12661     /**
12662     * Calls the passed function passing (el, this, index) for each element in this composite.
12663     * @param {Function} fn The function to call
12664     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12665     * @return {CompositeElement} this
12666     */
12667     each : function(fn, scope){
12668         var els = this.elements;
12669         for(var i = 0, len = els.length; i < len; i++){
12670             if(fn.call(scope || els[i], els[i], this, i) === false) {
12671                 break;
12672             }
12673         }
12674         return this;
12675     },
12676
12677     /**
12678      * Returns the Element object at the specified index
12679      * @param {Number} index
12680      * @return {Roo.Element}
12681      */
12682     item : function(index){
12683         return this.elements[index] || null;
12684     },
12685
12686     /**
12687      * Returns the first Element
12688      * @return {Roo.Element}
12689      */
12690     first : function(){
12691         return this.item(0);
12692     },
12693
12694     /**
12695      * Returns the last Element
12696      * @return {Roo.Element}
12697      */
12698     last : function(){
12699         return this.item(this.elements.length-1);
12700     },
12701
12702     /**
12703      * Returns the number of elements in this composite
12704      * @return Number
12705      */
12706     getCount : function(){
12707         return this.elements.length;
12708     },
12709
12710     /**
12711      * Returns true if this composite contains the passed element
12712      * @return Boolean
12713      */
12714     contains : function(el){
12715         return this.indexOf(el) !== -1;
12716     },
12717
12718     /**
12719      * Returns true if this composite contains the passed element
12720      * @return Boolean
12721      */
12722     indexOf : function(el){
12723         return this.elements.indexOf(Roo.get(el));
12724     },
12725
12726
12727     /**
12728     * Removes the specified element(s).
12729     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12730     * or an array of any of those.
12731     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12732     * @return {CompositeElement} this
12733     */
12734     removeElement : function(el, removeDom){
12735         if(el instanceof Array){
12736             for(var i = 0, len = el.length; i < len; i++){
12737                 this.removeElement(el[i]);
12738             }
12739             return this;
12740         }
12741         var index = typeof el == 'number' ? el : this.indexOf(el);
12742         if(index !== -1){
12743             if(removeDom){
12744                 var d = this.elements[index];
12745                 if(d.dom){
12746                     d.remove();
12747                 }else{
12748                     d.parentNode.removeChild(d);
12749                 }
12750             }
12751             this.elements.splice(index, 1);
12752         }
12753         return this;
12754     },
12755
12756     /**
12757     * Replaces the specified element with the passed element.
12758     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12759     * to replace.
12760     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12761     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12762     * @return {CompositeElement} this
12763     */
12764     replaceElement : function(el, replacement, domReplace){
12765         var index = typeof el == 'number' ? el : this.indexOf(el);
12766         if(index !== -1){
12767             if(domReplace){
12768                 this.elements[index].replaceWith(replacement);
12769             }else{
12770                 this.elements.splice(index, 1, Roo.get(replacement))
12771             }
12772         }
12773         return this;
12774     },
12775
12776     /**
12777      * Removes all elements.
12778      */
12779     clear : function(){
12780         this.elements = [];
12781     }
12782 };
12783 (function(){
12784     Roo.CompositeElement.createCall = function(proto, fnName){
12785         if(!proto[fnName]){
12786             proto[fnName] = function(){
12787                 return this.invoke(fnName, arguments);
12788             };
12789         }
12790     };
12791     for(var fnName in Roo.Element.prototype){
12792         if(typeof Roo.Element.prototype[fnName] == "function"){
12793             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12794         }
12795     };
12796 })();
12797 /*
12798  * Based on:
12799  * Ext JS Library 1.1.1
12800  * Copyright(c) 2006-2007, Ext JS, LLC.
12801  *
12802  * Originally Released Under LGPL - original licence link has changed is not relivant.
12803  *
12804  * Fork - LGPL
12805  * <script type="text/javascript">
12806  */
12807
12808 /**
12809  * @class Roo.CompositeElementLite
12810  * @extends Roo.CompositeElement
12811  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12812  <pre><code>
12813  var els = Roo.select("#some-el div.some-class");
12814  // or select directly from an existing element
12815  var el = Roo.get('some-el');
12816  el.select('div.some-class');
12817
12818  els.setWidth(100); // all elements become 100 width
12819  els.hide(true); // all elements fade out and hide
12820  // or
12821  els.setWidth(100).hide(true);
12822  </code></pre><br><br>
12823  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12824  * actions will be performed on all the elements in this collection.</b>
12825  */
12826 Roo.CompositeElementLite = function(els){
12827     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12828     this.el = new Roo.Element.Flyweight();
12829 };
12830 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12831     addElements : function(els){
12832         if(els){
12833             if(els instanceof Array){
12834                 this.elements = this.elements.concat(els);
12835             }else{
12836                 var yels = this.elements;
12837                 var index = yels.length-1;
12838                 for(var i = 0, len = els.length; i < len; i++) {
12839                     yels[++index] = els[i];
12840                 }
12841             }
12842         }
12843         return this;
12844     },
12845     invoke : function(fn, args){
12846         var els = this.elements;
12847         var el = this.el;
12848         for(var i = 0, len = els.length; i < len; i++) {
12849             el.dom = els[i];
12850                 Roo.Element.prototype[fn].apply(el, args);
12851         }
12852         return this;
12853     },
12854     /**
12855      * Returns a flyweight Element of the dom element object at the specified index
12856      * @param {Number} index
12857      * @return {Roo.Element}
12858      */
12859     item : function(index){
12860         if(!this.elements[index]){
12861             return null;
12862         }
12863         this.el.dom = this.elements[index];
12864         return this.el;
12865     },
12866
12867     // fixes scope with flyweight
12868     addListener : function(eventName, handler, scope, opt){
12869         var els = this.elements;
12870         for(var i = 0, len = els.length; i < len; i++) {
12871             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12872         }
12873         return this;
12874     },
12875
12876     /**
12877     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12878     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12879     * a reference to the dom node, use el.dom.</b>
12880     * @param {Function} fn The function to call
12881     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12882     * @return {CompositeElement} this
12883     */
12884     each : function(fn, scope){
12885         var els = this.elements;
12886         var el = this.el;
12887         for(var i = 0, len = els.length; i < len; i++){
12888             el.dom = els[i];
12889                 if(fn.call(scope || el, el, this, i) === false){
12890                 break;
12891             }
12892         }
12893         return this;
12894     },
12895
12896     indexOf : function(el){
12897         return this.elements.indexOf(Roo.getDom(el));
12898     },
12899
12900     replaceElement : function(el, replacement, domReplace){
12901         var index = typeof el == 'number' ? el : this.indexOf(el);
12902         if(index !== -1){
12903             replacement = Roo.getDom(replacement);
12904             if(domReplace){
12905                 var d = this.elements[index];
12906                 d.parentNode.insertBefore(replacement, d);
12907                 d.parentNode.removeChild(d);
12908             }
12909             this.elements.splice(index, 1, replacement);
12910         }
12911         return this;
12912     }
12913 });
12914 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12915
12916 /*
12917  * Based on:
12918  * Ext JS Library 1.1.1
12919  * Copyright(c) 2006-2007, Ext JS, LLC.
12920  *
12921  * Originally Released Under LGPL - original licence link has changed is not relivant.
12922  *
12923  * Fork - LGPL
12924  * <script type="text/javascript">
12925  */
12926
12927  
12928
12929 /**
12930  * @class Roo.data.Connection
12931  * @extends Roo.util.Observable
12932  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12933  * either to a configured URL, or to a URL specified at request time. 
12934  * 
12935  * Requests made by this class are asynchronous, and will return immediately. No data from
12936  * the server will be available to the statement immediately following the {@link #request} call.
12937  * To process returned data, use a callback in the request options object, or an event listener.
12938  * 
12939  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12940  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12941  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12942  * property and, if present, the IFRAME's XML document as the responseXML property.
12943  * 
12944  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12945  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12946  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12947  * standard DOM methods.
12948  * @constructor
12949  * @param {Object} config a configuration object.
12950  */
12951 Roo.data.Connection = function(config){
12952     Roo.apply(this, config);
12953     this.addEvents({
12954         /**
12955          * @event beforerequest
12956          * Fires before a network request is made to retrieve a data object.
12957          * @param {Connection} conn This Connection object.
12958          * @param {Object} options The options config object passed to the {@link #request} method.
12959          */
12960         "beforerequest" : true,
12961         /**
12962          * @event requestcomplete
12963          * Fires if the request was successfully completed.
12964          * @param {Connection} conn This Connection object.
12965          * @param {Object} response The XHR object containing the response data.
12966          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12967          * @param {Object} options The options config object passed to the {@link #request} method.
12968          */
12969         "requestcomplete" : true,
12970         /**
12971          * @event requestexception
12972          * Fires if an error HTTP status was returned from the server.
12973          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12974          * @param {Connection} conn This Connection object.
12975          * @param {Object} response The XHR object containing the response data.
12976          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12977          * @param {Object} options The options config object passed to the {@link #request} method.
12978          */
12979         "requestexception" : true
12980     });
12981     Roo.data.Connection.superclass.constructor.call(this);
12982 };
12983
12984 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12985     /**
12986      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12987      */
12988     /**
12989      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12990      * extra parameters to each request made by this object. (defaults to undefined)
12991      */
12992     /**
12993      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12994      *  to each request made by this object. (defaults to undefined)
12995      */
12996     /**
12997      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12998      */
12999     /**
13000      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13001      */
13002     timeout : 30000,
13003     /**
13004      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13005      * @type Boolean
13006      */
13007     autoAbort:false,
13008
13009     /**
13010      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13011      * @type Boolean
13012      */
13013     disableCaching: true,
13014
13015     /**
13016      * Sends an HTTP request to a remote server.
13017      * @param {Object} options An object which may contain the following properties:<ul>
13018      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
13019      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
13020      * request, a url encoded string or a function to call to get either.</li>
13021      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
13022      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
13023      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
13024      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
13025      * <li>options {Object} The parameter to the request call.</li>
13026      * <li>success {Boolean} True if the request succeeded.</li>
13027      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13028      * </ul></li>
13029      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
13030      * The callback is passed the following parameters:<ul>
13031      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13032      * <li>options {Object} The parameter to the request call.</li>
13033      * </ul></li>
13034      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
13035      * The callback is passed the following parameters:<ul>
13036      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13037      * <li>options {Object} The parameter to the request call.</li>
13038      * </ul></li>
13039      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
13040      * for the callback function. Defaults to the browser window.</li>
13041      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
13042      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
13043      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
13044      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
13045      * params for the post data. Any params will be appended to the URL.</li>
13046      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
13047      * </ul>
13048      * @return {Number} transactionId
13049      */
13050     request : function(o){
13051         if(this.fireEvent("beforerequest", this, o) !== false){
13052             var p = o.params;
13053
13054             if(typeof p == "function"){
13055                 p = p.call(o.scope||window, o);
13056             }
13057             if(typeof p == "object"){
13058                 p = Roo.urlEncode(o.params);
13059             }
13060             if(this.extraParams){
13061                 var extras = Roo.urlEncode(this.extraParams);
13062                 p = p ? (p + '&' + extras) : extras;
13063             }
13064
13065             var url = o.url || this.url;
13066             if(typeof url == 'function'){
13067                 url = url.call(o.scope||window, o);
13068             }
13069
13070             if(o.form){
13071                 var form = Roo.getDom(o.form);
13072                 url = url || form.action;
13073
13074                 var enctype = form.getAttribute("enctype");
13075                 
13076                 if (o.formData) {
13077                     return this.doFormDataUpload(o, url);
13078                 }
13079                 
13080                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
13081                     return this.doFormUpload(o, p, url);
13082                 }
13083                 var f = Roo.lib.Ajax.serializeForm(form);
13084                 p = p ? (p + '&' + f) : f;
13085             }
13086             
13087             if (!o.form && o.formData) {
13088                 o.formData = o.formData === true ? new FormData() : o.formData;
13089                 for (var k in o.params) {
13090                     o.formData.append(k,o.params[k]);
13091                 }
13092                     
13093                 return this.doFormDataUpload(o, url);
13094             }
13095             
13096
13097             var hs = o.headers;
13098             if(this.defaultHeaders){
13099                 hs = Roo.apply(hs || {}, this.defaultHeaders);
13100                 if(!o.headers){
13101                     o.headers = hs;
13102                 }
13103             }
13104
13105             var cb = {
13106                 success: this.handleResponse,
13107                 failure: this.handleFailure,
13108                 scope: this,
13109                 argument: {options: o},
13110                 timeout : o.timeout || this.timeout
13111             };
13112
13113             var method = o.method||this.method||(p ? "POST" : "GET");
13114
13115             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
13116                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
13117             }
13118
13119             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13120                 if(o.autoAbort){
13121                     this.abort();
13122                 }
13123             }else if(this.autoAbort !== false){
13124                 this.abort();
13125             }
13126
13127             if((method == 'GET' && p) || o.xmlData){
13128                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
13129                 p = '';
13130             }
13131             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
13132             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
13133             Roo.lib.Ajax.useDefaultHeader == true;
13134             return this.transId;
13135         }else{
13136             Roo.callback(o.callback, o.scope, [o, null, null]);
13137             return null;
13138         }
13139     },
13140
13141     /**
13142      * Determine whether this object has a request outstanding.
13143      * @param {Number} transactionId (Optional) defaults to the last transaction
13144      * @return {Boolean} True if there is an outstanding request.
13145      */
13146     isLoading : function(transId){
13147         if(transId){
13148             return Roo.lib.Ajax.isCallInProgress(transId);
13149         }else{
13150             return this.transId ? true : false;
13151         }
13152     },
13153
13154     /**
13155      * Aborts any outstanding request.
13156      * @param {Number} transactionId (Optional) defaults to the last transaction
13157      */
13158     abort : function(transId){
13159         if(transId || this.isLoading()){
13160             Roo.lib.Ajax.abort(transId || this.transId);
13161         }
13162     },
13163
13164     // private
13165     handleResponse : function(response){
13166         this.transId = false;
13167         var options = response.argument.options;
13168         response.argument = options ? options.argument : null;
13169         this.fireEvent("requestcomplete", this, response, options);
13170         Roo.callback(options.success, options.scope, [response, options]);
13171         Roo.callback(options.callback, options.scope, [options, true, response]);
13172     },
13173
13174     // private
13175     handleFailure : function(response, e){
13176         this.transId = false;
13177         var options = response.argument.options;
13178         response.argument = options ? options.argument : null;
13179         this.fireEvent("requestexception", this, response, options, e);
13180         Roo.callback(options.failure, options.scope, [response, options]);
13181         Roo.callback(options.callback, options.scope, [options, false, response]);
13182     },
13183
13184     // private
13185     doFormUpload : function(o, ps, url){
13186         var id = Roo.id();
13187         var frame = document.createElement('iframe');
13188         frame.id = id;
13189         frame.name = id;
13190         frame.className = 'x-hidden';
13191         if(Roo.isIE){
13192             frame.src = Roo.SSL_SECURE_URL;
13193         }
13194         document.body.appendChild(frame);
13195
13196         if(Roo.isIE){
13197            document.frames[id].name = id;
13198         }
13199
13200         var form = Roo.getDom(o.form);
13201         form.target = id;
13202         form.method = 'POST';
13203         form.enctype = form.encoding = 'multipart/form-data';
13204         if(url){
13205             form.action = url;
13206         }
13207
13208         var hiddens, hd;
13209         if(ps){ // add dynamic params
13210             hiddens = [];
13211             ps = Roo.urlDecode(ps, false);
13212             for(var k in ps){
13213                 if(ps.hasOwnProperty(k)){
13214                     hd = document.createElement('input');
13215                     hd.type = 'hidden';
13216                     hd.name = k;
13217                     hd.value = ps[k];
13218                     form.appendChild(hd);
13219                     hiddens.push(hd);
13220                 }
13221             }
13222         }
13223
13224         function cb(){
13225             var r = {  // bogus response object
13226                 responseText : '',
13227                 responseXML : null
13228             };
13229
13230             r.argument = o ? o.argument : null;
13231
13232             try { //
13233                 var doc;
13234                 if(Roo.isIE){
13235                     doc = frame.contentWindow.document;
13236                 }else {
13237                     doc = (frame.contentDocument || window.frames[id].document);
13238                 }
13239                 if(doc && doc.body){
13240                     r.responseText = doc.body.innerHTML;
13241                 }
13242                 if(doc && doc.XMLDocument){
13243                     r.responseXML = doc.XMLDocument;
13244                 }else {
13245                     r.responseXML = doc;
13246                 }
13247             }
13248             catch(e) {
13249                 // ignore
13250             }
13251
13252             Roo.EventManager.removeListener(frame, 'load', cb, this);
13253
13254             this.fireEvent("requestcomplete", this, r, o);
13255             Roo.callback(o.success, o.scope, [r, o]);
13256             Roo.callback(o.callback, o.scope, [o, true, r]);
13257
13258             setTimeout(function(){document.body.removeChild(frame);}, 100);
13259         }
13260
13261         Roo.EventManager.on(frame, 'load', cb, this);
13262         form.submit();
13263
13264         if(hiddens){ // remove dynamic params
13265             for(var i = 0, len = hiddens.length; i < len; i++){
13266                 form.removeChild(hiddens[i]);
13267             }
13268         }
13269     },
13270     // this is a 'formdata version???'
13271     
13272     
13273     doFormDataUpload : function(o,  url)
13274     {
13275         var formData;
13276         if (o.form) {
13277             var form =  Roo.getDom(o.form);
13278             form.enctype = form.encoding = 'multipart/form-data';
13279             formData = o.formData === true ? new FormData(form) : o.formData;
13280         } else {
13281             formData = o.formData === true ? new FormData() : o.formData;
13282         }
13283         
13284       
13285         var cb = {
13286             success: this.handleResponse,
13287             failure: this.handleFailure,
13288             scope: this,
13289             argument: {options: o},
13290             timeout : o.timeout || this.timeout
13291         };
13292  
13293         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13294             if(o.autoAbort){
13295                 this.abort();
13296             }
13297         }else if(this.autoAbort !== false){
13298             this.abort();
13299         }
13300
13301         //Roo.lib.Ajax.defaultPostHeader = null;
13302         Roo.lib.Ajax.useDefaultHeader = false;
13303         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13304         Roo.lib.Ajax.useDefaultHeader = true;
13305  
13306          
13307     }
13308     
13309 });
13310 /*
13311  * Based on:
13312  * Ext JS Library 1.1.1
13313  * Copyright(c) 2006-2007, Ext JS, LLC.
13314  *
13315  * Originally Released Under LGPL - original licence link has changed is not relivant.
13316  *
13317  * Fork - LGPL
13318  * <script type="text/javascript">
13319  */
13320  
13321 /**
13322  * Global Ajax request class.
13323  * 
13324  * @class Roo.Ajax
13325  * @extends Roo.data.Connection
13326  * @static
13327  * 
13328  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13329  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13330  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13331  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
13332  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13333  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13334  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13335  */
13336 Roo.Ajax = new Roo.data.Connection({
13337     // fix up the docs
13338     /**
13339      * @scope Roo.Ajax
13340      * @type {Boolear} 
13341      */
13342     autoAbort : false,
13343
13344     /**
13345      * Serialize the passed form into a url encoded string
13346      * @scope Roo.Ajax
13347      * @param {String/HTMLElement} form
13348      * @return {String}
13349      */
13350     serializeForm : function(form){
13351         return Roo.lib.Ajax.serializeForm(form);
13352     }
13353 });/*
13354  * Based on:
13355  * Ext JS Library 1.1.1
13356  * Copyright(c) 2006-2007, Ext JS, LLC.
13357  *
13358  * Originally Released Under LGPL - original licence link has changed is not relivant.
13359  *
13360  * Fork - LGPL
13361  * <script type="text/javascript">
13362  */
13363
13364  
13365 /**
13366  * @class Roo.UpdateManager
13367  * @extends Roo.util.Observable
13368  * Provides AJAX-style update for Element object.<br><br>
13369  * Usage:<br>
13370  * <pre><code>
13371  * // Get it from a Roo.Element object
13372  * var el = Roo.get("foo");
13373  * var mgr = el.getUpdateManager();
13374  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13375  * ...
13376  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13377  * <br>
13378  * // or directly (returns the same UpdateManager instance)
13379  * var mgr = new Roo.UpdateManager("myElementId");
13380  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13381  * mgr.on("update", myFcnNeedsToKnow);
13382  * <br>
13383    // short handed call directly from the element object
13384    Roo.get("foo").load({
13385         url: "bar.php",
13386         scripts:true,
13387         params: "for=bar",
13388         text: "Loading Foo..."
13389    });
13390  * </code></pre>
13391  * @constructor
13392  * Create new UpdateManager directly.
13393  * @param {String/HTMLElement/Roo.Element} el The element to update
13394  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
13395  */
13396 Roo.UpdateManager = function(el, forceNew){
13397     el = Roo.get(el);
13398     if(!forceNew && el.updateManager){
13399         return el.updateManager;
13400     }
13401     /**
13402      * The Element object
13403      * @type Roo.Element
13404      */
13405     this.el = el;
13406     /**
13407      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13408      * @type String
13409      */
13410     this.defaultUrl = null;
13411
13412     this.addEvents({
13413         /**
13414          * @event beforeupdate
13415          * Fired before an update is made, return false from your handler and the update is cancelled.
13416          * @param {Roo.Element} el
13417          * @param {String/Object/Function} url
13418          * @param {String/Object} params
13419          */
13420         "beforeupdate": true,
13421         /**
13422          * @event update
13423          * Fired after successful update is made.
13424          * @param {Roo.Element} el
13425          * @param {Object} oResponseObject The response Object
13426          */
13427         "update": true,
13428         /**
13429          * @event failure
13430          * Fired on update failure.
13431          * @param {Roo.Element} el
13432          * @param {Object} oResponseObject The response Object
13433          */
13434         "failure": true
13435     });
13436     var d = Roo.UpdateManager.defaults;
13437     /**
13438      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13439      * @type String
13440      */
13441     this.sslBlankUrl = d.sslBlankUrl;
13442     /**
13443      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13444      * @type Boolean
13445      */
13446     this.disableCaching = d.disableCaching;
13447     /**
13448      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13449      * @type String
13450      */
13451     this.indicatorText = d.indicatorText;
13452     /**
13453      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13454      * @type String
13455      */
13456     this.showLoadIndicator = d.showLoadIndicator;
13457     /**
13458      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13459      * @type Number
13460      */
13461     this.timeout = d.timeout;
13462
13463     /**
13464      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13465      * @type Boolean
13466      */
13467     this.loadScripts = d.loadScripts;
13468
13469     /**
13470      * Transaction object of current executing transaction
13471      */
13472     this.transaction = null;
13473
13474     /**
13475      * @private
13476      */
13477     this.autoRefreshProcId = null;
13478     /**
13479      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13480      * @type Function
13481      */
13482     this.refreshDelegate = this.refresh.createDelegate(this);
13483     /**
13484      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13485      * @type Function
13486      */
13487     this.updateDelegate = this.update.createDelegate(this);
13488     /**
13489      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13490      * @type Function
13491      */
13492     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13493     /**
13494      * @private
13495      */
13496     this.successDelegate = this.processSuccess.createDelegate(this);
13497     /**
13498      * @private
13499      */
13500     this.failureDelegate = this.processFailure.createDelegate(this);
13501
13502     if(!this.renderer){
13503      /**
13504       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13505       */
13506     this.renderer = new Roo.UpdateManager.BasicRenderer();
13507     }
13508     
13509     Roo.UpdateManager.superclass.constructor.call(this);
13510 };
13511
13512 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13513     /**
13514      * Get the Element this UpdateManager is bound to
13515      * @return {Roo.Element} The element
13516      */
13517     getEl : function(){
13518         return this.el;
13519     },
13520     /**
13521      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13522      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
13523 <pre><code>
13524 um.update({<br/>
13525     url: "your-url.php",<br/>
13526     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13527     callback: yourFunction,<br/>
13528     scope: yourObject, //(optional scope)  <br/>
13529     discardUrl: false, <br/>
13530     nocache: false,<br/>
13531     text: "Loading...",<br/>
13532     timeout: 30,<br/>
13533     scripts: false<br/>
13534 });
13535 </code></pre>
13536      * The only required property is url. The optional properties nocache, text and scripts
13537      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13538      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
13539      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13540      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
13541      */
13542     update : function(url, params, callback, discardUrl){
13543         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13544             var method = this.method,
13545                 cfg;
13546             if(typeof url == "object"){ // must be config object
13547                 cfg = url;
13548                 url = cfg.url;
13549                 params = params || cfg.params;
13550                 callback = callback || cfg.callback;
13551                 discardUrl = discardUrl || cfg.discardUrl;
13552                 if(callback && cfg.scope){
13553                     callback = callback.createDelegate(cfg.scope);
13554                 }
13555                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13556                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13557                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13558                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13559                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13560             }
13561             this.showLoading();
13562             if(!discardUrl){
13563                 this.defaultUrl = url;
13564             }
13565             if(typeof url == "function"){
13566                 url = url.call(this);
13567             }
13568
13569             method = method || (params ? "POST" : "GET");
13570             if(method == "GET"){
13571                 url = this.prepareUrl(url);
13572             }
13573
13574             var o = Roo.apply(cfg ||{}, {
13575                 url : url,
13576                 params: params,
13577                 success: this.successDelegate,
13578                 failure: this.failureDelegate,
13579                 callback: undefined,
13580                 timeout: (this.timeout*1000),
13581                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13582             });
13583             Roo.log("updated manager called with timeout of " + o.timeout);
13584             this.transaction = Roo.Ajax.request(o);
13585         }
13586     },
13587
13588     /**
13589      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
13590      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13591      * @param {String/HTMLElement} form The form Id or form element
13592      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13593      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13594      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13595      */
13596     formUpdate : function(form, url, reset, callback){
13597         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13598             if(typeof url == "function"){
13599                 url = url.call(this);
13600             }
13601             form = Roo.getDom(form);
13602             this.transaction = Roo.Ajax.request({
13603                 form: form,
13604                 url:url,
13605                 success: this.successDelegate,
13606                 failure: this.failureDelegate,
13607                 timeout: (this.timeout*1000),
13608                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13609             });
13610             this.showLoading.defer(1, this);
13611         }
13612     },
13613
13614     /**
13615      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13616      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13617      */
13618     refresh : function(callback){
13619         if(this.defaultUrl == null){
13620             return;
13621         }
13622         this.update(this.defaultUrl, null, callback, true);
13623     },
13624
13625     /**
13626      * Set this element to auto refresh.
13627      * @param {Number} interval How often to update (in seconds).
13628      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
13629      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
13630      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13631      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13632      */
13633     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13634         if(refreshNow){
13635             this.update(url || this.defaultUrl, params, callback, true);
13636         }
13637         if(this.autoRefreshProcId){
13638             clearInterval(this.autoRefreshProcId);
13639         }
13640         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13641     },
13642
13643     /**
13644      * Stop auto refresh on this element.
13645      */
13646      stopAutoRefresh : function(){
13647         if(this.autoRefreshProcId){
13648             clearInterval(this.autoRefreshProcId);
13649             delete this.autoRefreshProcId;
13650         }
13651     },
13652
13653     isAutoRefreshing : function(){
13654        return this.autoRefreshProcId ? true : false;
13655     },
13656     /**
13657      * Called to update the element to "Loading" state. Override to perform custom action.
13658      */
13659     showLoading : function(){
13660         if(this.showLoadIndicator){
13661             this.el.update(this.indicatorText);
13662         }
13663     },
13664
13665     /**
13666      * Adds unique parameter to query string if disableCaching = true
13667      * @private
13668      */
13669     prepareUrl : function(url){
13670         if(this.disableCaching){
13671             var append = "_dc=" + (new Date().getTime());
13672             if(url.indexOf("?") !== -1){
13673                 url += "&" + append;
13674             }else{
13675                 url += "?" + append;
13676             }
13677         }
13678         return url;
13679     },
13680
13681     /**
13682      * @private
13683      */
13684     processSuccess : function(response){
13685         this.transaction = null;
13686         if(response.argument.form && response.argument.reset){
13687             try{ // put in try/catch since some older FF releases had problems with this
13688                 response.argument.form.reset();
13689             }catch(e){}
13690         }
13691         if(this.loadScripts){
13692             this.renderer.render(this.el, response, this,
13693                 this.updateComplete.createDelegate(this, [response]));
13694         }else{
13695             this.renderer.render(this.el, response, this);
13696             this.updateComplete(response);
13697         }
13698     },
13699
13700     updateComplete : function(response){
13701         this.fireEvent("update", this.el, response);
13702         if(typeof response.argument.callback == "function"){
13703             response.argument.callback(this.el, true, response);
13704         }
13705     },
13706
13707     /**
13708      * @private
13709      */
13710     processFailure : function(response){
13711         this.transaction = null;
13712         this.fireEvent("failure", this.el, response);
13713         if(typeof response.argument.callback == "function"){
13714             response.argument.callback(this.el, false, response);
13715         }
13716     },
13717
13718     /**
13719      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13720      * @param {Object} renderer The object implementing the render() method
13721      */
13722     setRenderer : function(renderer){
13723         this.renderer = renderer;
13724     },
13725
13726     getRenderer : function(){
13727        return this.renderer;
13728     },
13729
13730     /**
13731      * Set the defaultUrl used for updates
13732      * @param {String/Function} defaultUrl The url or a function to call to get the url
13733      */
13734     setDefaultUrl : function(defaultUrl){
13735         this.defaultUrl = defaultUrl;
13736     },
13737
13738     /**
13739      * Aborts the executing transaction
13740      */
13741     abort : function(){
13742         if(this.transaction){
13743             Roo.Ajax.abort(this.transaction);
13744         }
13745     },
13746
13747     /**
13748      * Returns true if an update is in progress
13749      * @return {Boolean}
13750      */
13751     isUpdating : function(){
13752         if(this.transaction){
13753             return Roo.Ajax.isLoading(this.transaction);
13754         }
13755         return false;
13756     }
13757 });
13758
13759 /**
13760  * @class Roo.UpdateManager.defaults
13761  * @static (not really - but it helps the doc tool)
13762  * The defaults collection enables customizing the default properties of UpdateManager
13763  */
13764    Roo.UpdateManager.defaults = {
13765        /**
13766          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13767          * @type Number
13768          */
13769          timeout : 30,
13770
13771          /**
13772          * True to process scripts by default (Defaults to false).
13773          * @type Boolean
13774          */
13775         loadScripts : false,
13776
13777         /**
13778         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13779         * @type String
13780         */
13781         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13782         /**
13783          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13784          * @type Boolean
13785          */
13786         disableCaching : false,
13787         /**
13788          * Whether to show indicatorText when loading (Defaults to true).
13789          * @type Boolean
13790          */
13791         showLoadIndicator : true,
13792         /**
13793          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13794          * @type String
13795          */
13796         indicatorText : '<div class="loading-indicator">Loading...</div>'
13797    };
13798
13799 /**
13800  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13801  *Usage:
13802  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13803  * @param {String/HTMLElement/Roo.Element} el The element to update
13804  * @param {String} url The url
13805  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13806  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13807  * @static
13808  * @deprecated
13809  * @member Roo.UpdateManager
13810  */
13811 Roo.UpdateManager.updateElement = function(el, url, params, options){
13812     var um = Roo.get(el, true).getUpdateManager();
13813     Roo.apply(um, options);
13814     um.update(url, params, options ? options.callback : null);
13815 };
13816 // alias for backwards compat
13817 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13818 /**
13819  * @class Roo.UpdateManager.BasicRenderer
13820  * Default Content renderer. Updates the elements innerHTML with the responseText.
13821  */
13822 Roo.UpdateManager.BasicRenderer = function(){};
13823
13824 Roo.UpdateManager.BasicRenderer.prototype = {
13825     /**
13826      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13827      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13828      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13829      * @param {Roo.Element} el The element being rendered
13830      * @param {Object} response The YUI Connect response object
13831      * @param {UpdateManager} updateManager The calling update manager
13832      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13833      */
13834      render : function(el, response, updateManager, callback){
13835         el.update(response.responseText, updateManager.loadScripts, callback);
13836     }
13837 };
13838 /*
13839  * Based on:
13840  * Roo JS
13841  * (c)) Alan Knowles
13842  * Licence : LGPL
13843  */
13844
13845
13846 /**
13847  * @class Roo.DomTemplate
13848  * @extends Roo.Template
13849  * An effort at a dom based template engine..
13850  *
13851  * Similar to XTemplate, except it uses dom parsing to create the template..
13852  *
13853  * Supported features:
13854  *
13855  *  Tags:
13856
13857 <pre><code>
13858       {a_variable} - output encoded.
13859       {a_variable.format:("Y-m-d")} - call a method on the variable
13860       {a_variable:raw} - unencoded output
13861       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13862       {a_variable:this.method_on_template(...)} - call a method on the template object.
13863  
13864 </code></pre>
13865  *  The tpl tag:
13866 <pre><code>
13867         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13868         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13869         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13870         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13871   
13872 </code></pre>
13873  *      
13874  */
13875 Roo.DomTemplate = function()
13876 {
13877      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13878      if (this.html) {
13879         this.compile();
13880      }
13881 };
13882
13883
13884 Roo.extend(Roo.DomTemplate, Roo.Template, {
13885     /**
13886      * id counter for sub templates.
13887      */
13888     id : 0,
13889     /**
13890      * flag to indicate if dom parser is inside a pre,
13891      * it will strip whitespace if not.
13892      */
13893     inPre : false,
13894     
13895     /**
13896      * The various sub templates
13897      */
13898     tpls : false,
13899     
13900     
13901     
13902     /**
13903      *
13904      * basic tag replacing syntax
13905      * WORD:WORD()
13906      *
13907      * // you can fake an object call by doing this
13908      *  x.t:(test,tesT) 
13909      * 
13910      */
13911     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13912     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13913     
13914     iterChild : function (node, method) {
13915         
13916         var oldPre = this.inPre;
13917         if (node.tagName == 'PRE') {
13918             this.inPre = true;
13919         }
13920         for( var i = 0; i < node.childNodes.length; i++) {
13921             method.call(this, node.childNodes[i]);
13922         }
13923         this.inPre = oldPre;
13924     },
13925     
13926     
13927     
13928     /**
13929      * compile the template
13930      *
13931      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13932      *
13933      */
13934     compile: function()
13935     {
13936         var s = this.html;
13937         
13938         // covert the html into DOM...
13939         var doc = false;
13940         var div =false;
13941         try {
13942             doc = document.implementation.createHTMLDocument("");
13943             doc.documentElement.innerHTML =   this.html  ;
13944             div = doc.documentElement;
13945         } catch (e) {
13946             // old IE... - nasty -- it causes all sorts of issues.. with
13947             // images getting pulled from server..
13948             div = document.createElement('div');
13949             div.innerHTML = this.html;
13950         }
13951         //doc.documentElement.innerHTML = htmlBody
13952          
13953         
13954         
13955         this.tpls = [];
13956         var _t = this;
13957         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13958         
13959         var tpls = this.tpls;
13960         
13961         // create a top level template from the snippet..
13962         
13963         //Roo.log(div.innerHTML);
13964         
13965         var tpl = {
13966             uid : 'master',
13967             id : this.id++,
13968             attr : false,
13969             value : false,
13970             body : div.innerHTML,
13971             
13972             forCall : false,
13973             execCall : false,
13974             dom : div,
13975             isTop : true
13976             
13977         };
13978         tpls.unshift(tpl);
13979         
13980         
13981         // compile them...
13982         this.tpls = [];
13983         Roo.each(tpls, function(tp){
13984             this.compileTpl(tp);
13985             this.tpls[tp.id] = tp;
13986         }, this);
13987         
13988         this.master = tpls[0];
13989         return this;
13990         
13991         
13992     },
13993     
13994     compileNode : function(node, istop) {
13995         // test for
13996         //Roo.log(node);
13997         
13998         
13999         // skip anything not a tag..
14000         if (node.nodeType != 1) {
14001             if (node.nodeType == 3 && !this.inPre) {
14002                 // reduce white space..
14003                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
14004                 
14005             }
14006             return;
14007         }
14008         
14009         var tpl = {
14010             uid : false,
14011             id : false,
14012             attr : false,
14013             value : false,
14014             body : '',
14015             
14016             forCall : false,
14017             execCall : false,
14018             dom : false,
14019             isTop : istop
14020             
14021             
14022         };
14023         
14024         
14025         switch(true) {
14026             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
14027             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
14028             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
14029             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
14030             // no default..
14031         }
14032         
14033         
14034         if (!tpl.attr) {
14035             // just itterate children..
14036             this.iterChild(node,this.compileNode);
14037             return;
14038         }
14039         tpl.uid = this.id++;
14040         tpl.value = node.getAttribute('roo-' +  tpl.attr);
14041         node.removeAttribute('roo-'+ tpl.attr);
14042         if (tpl.attr != 'name') {
14043             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
14044             node.parentNode.replaceChild(placeholder,  node);
14045         } else {
14046             
14047             var placeholder =  document.createElement('span');
14048             placeholder.className = 'roo-tpl-' + tpl.value;
14049             node.parentNode.replaceChild(placeholder,  node);
14050         }
14051         
14052         // parent now sees '{domtplXXXX}
14053         this.iterChild(node,this.compileNode);
14054         
14055         // we should now have node body...
14056         var div = document.createElement('div');
14057         div.appendChild(node);
14058         tpl.dom = node;
14059         // this has the unfortunate side effect of converting tagged attributes
14060         // eg. href="{...}" into %7C...%7D
14061         // this has been fixed by searching for those combo's although it's a bit hacky..
14062         
14063         
14064         tpl.body = div.innerHTML;
14065         
14066         
14067          
14068         tpl.id = tpl.uid;
14069         switch(tpl.attr) {
14070             case 'for' :
14071                 switch (tpl.value) {
14072                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
14073                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
14074                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
14075                 }
14076                 break;
14077             
14078             case 'exec':
14079                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14080                 break;
14081             
14082             case 'if':     
14083                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14084                 break;
14085             
14086             case 'name':
14087                 tpl.id  = tpl.value; // replace non characters???
14088                 break;
14089             
14090         }
14091         
14092         
14093         this.tpls.push(tpl);
14094         
14095         
14096         
14097     },
14098     
14099     
14100     
14101     
14102     /**
14103      * Compile a segment of the template into a 'sub-template'
14104      *
14105      * 
14106      * 
14107      *
14108      */
14109     compileTpl : function(tpl)
14110     {
14111         var fm = Roo.util.Format;
14112         var useF = this.disableFormats !== true;
14113         
14114         var sep = Roo.isGecko ? "+\n" : ",\n";
14115         
14116         var undef = function(str) {
14117             Roo.debug && Roo.log("Property not found :"  + str);
14118             return '';
14119         };
14120           
14121         //Roo.log(tpl.body);
14122         
14123         
14124         
14125         var fn = function(m, lbrace, name, format, args)
14126         {
14127             //Roo.log("ARGS");
14128             //Roo.log(arguments);
14129             args = args ? args.replace(/\\'/g,"'") : args;
14130             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
14131             if (typeof(format) == 'undefined') {
14132                 format =  'htmlEncode'; 
14133             }
14134             if (format == 'raw' ) {
14135                 format = false;
14136             }
14137             
14138             if(name.substr(0, 6) == 'domtpl'){
14139                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
14140             }
14141             
14142             // build an array of options to determine if value is undefined..
14143             
14144             // basically get 'xxxx.yyyy' then do
14145             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
14146             //    (function () { Roo.log("Property not found"); return ''; })() :
14147             //    ......
14148             
14149             var udef_ar = [];
14150             var lookfor = '';
14151             Roo.each(name.split('.'), function(st) {
14152                 lookfor += (lookfor.length ? '.': '') + st;
14153                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
14154             });
14155             
14156             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
14157             
14158             
14159             if(format && useF){
14160                 
14161                 args = args ? ',' + args : "";
14162                  
14163                 if(format.substr(0, 5) != "this."){
14164                     format = "fm." + format + '(';
14165                 }else{
14166                     format = 'this.call("'+ format.substr(5) + '", ';
14167                     args = ", values";
14168                 }
14169                 
14170                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
14171             }
14172              
14173             if (args && args.length) {
14174                 // called with xxyx.yuu:(test,test)
14175                 // change to ()
14176                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
14177             }
14178             // raw.. - :raw modifier..
14179             return "'"+ sep + udef_st  + name + ")"+sep+"'";
14180             
14181         };
14182         var body;
14183         // branched to use + in gecko and [].join() in others
14184         if(Roo.isGecko){
14185             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
14186                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
14187                     "';};};";
14188         }else{
14189             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
14190             body.push(tpl.body.replace(/(\r\n|\n)/g,
14191                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
14192             body.push("'].join('');};};");
14193             body = body.join('');
14194         }
14195         
14196         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
14197        
14198         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
14199         eval(body);
14200         
14201         return this;
14202     },
14203      
14204     /**
14205      * same as applyTemplate, except it's done to one of the subTemplates
14206      * when using named templates, you can do:
14207      *
14208      * var str = pl.applySubTemplate('your-name', values);
14209      *
14210      * 
14211      * @param {Number} id of the template
14212      * @param {Object} values to apply to template
14213      * @param {Object} parent (normaly the instance of this object)
14214      */
14215     applySubTemplate : function(id, values, parent)
14216     {
14217         
14218         
14219         var t = this.tpls[id];
14220         
14221         
14222         try { 
14223             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14224                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14225                 return '';
14226             }
14227         } catch(e) {
14228             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14229             Roo.log(values);
14230           
14231             return '';
14232         }
14233         try { 
14234             
14235             if(t.execCall && t.execCall.call(this, values, parent)){
14236                 return '';
14237             }
14238         } catch(e) {
14239             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14240             Roo.log(values);
14241             return '';
14242         }
14243         
14244         try {
14245             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14246             parent = t.target ? values : parent;
14247             if(t.forCall && vs instanceof Array){
14248                 var buf = [];
14249                 for(var i = 0, len = vs.length; i < len; i++){
14250                     try {
14251                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14252                     } catch (e) {
14253                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14254                         Roo.log(e.body);
14255                         //Roo.log(t.compiled);
14256                         Roo.log(vs[i]);
14257                     }   
14258                 }
14259                 return buf.join('');
14260             }
14261         } catch (e) {
14262             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14263             Roo.log(values);
14264             return '';
14265         }
14266         try {
14267             return t.compiled.call(this, vs, parent);
14268         } catch (e) {
14269             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14270             Roo.log(e.body);
14271             //Roo.log(t.compiled);
14272             Roo.log(values);
14273             return '';
14274         }
14275     },
14276
14277    
14278
14279     applyTemplate : function(values){
14280         return this.master.compiled.call(this, values, {});
14281         //var s = this.subs;
14282     },
14283
14284     apply : function(){
14285         return this.applyTemplate.apply(this, arguments);
14286     }
14287
14288  });
14289
14290 Roo.DomTemplate.from = function(el){
14291     el = Roo.getDom(el);
14292     return new Roo.Domtemplate(el.value || el.innerHTML);
14293 };/*
14294  * Based on:
14295  * Ext JS Library 1.1.1
14296  * Copyright(c) 2006-2007, Ext JS, LLC.
14297  *
14298  * Originally Released Under LGPL - original licence link has changed is not relivant.
14299  *
14300  * Fork - LGPL
14301  * <script type="text/javascript">
14302  */
14303
14304 /**
14305  * @class Roo.util.DelayedTask
14306  * Provides a convenient method of performing setTimeout where a new
14307  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14308  * You can use this class to buffer
14309  * the keypress events for a certain number of milliseconds, and perform only if they stop
14310  * for that amount of time.
14311  * @constructor The parameters to this constructor serve as defaults and are not required.
14312  * @param {Function} fn (optional) The default function to timeout
14313  * @param {Object} scope (optional) The default scope of that timeout
14314  * @param {Array} args (optional) The default Array of arguments
14315  */
14316 Roo.util.DelayedTask = function(fn, scope, args){
14317     var id = null, d, t;
14318
14319     var call = function(){
14320         var now = new Date().getTime();
14321         if(now - t >= d){
14322             clearInterval(id);
14323             id = null;
14324             fn.apply(scope, args || []);
14325         }
14326     };
14327     /**
14328      * Cancels any pending timeout and queues a new one
14329      * @param {Number} delay The milliseconds to delay
14330      * @param {Function} newFn (optional) Overrides function passed to constructor
14331      * @param {Object} newScope (optional) Overrides scope passed to constructor
14332      * @param {Array} newArgs (optional) Overrides args passed to constructor
14333      */
14334     this.delay = function(delay, newFn, newScope, newArgs){
14335         if(id && delay != d){
14336             this.cancel();
14337         }
14338         d = delay;
14339         t = new Date().getTime();
14340         fn = newFn || fn;
14341         scope = newScope || scope;
14342         args = newArgs || args;
14343         if(!id){
14344             id = setInterval(call, d);
14345         }
14346     };
14347
14348     /**
14349      * Cancel the last queued timeout
14350      */
14351     this.cancel = function(){
14352         if(id){
14353             clearInterval(id);
14354             id = null;
14355         }
14356     };
14357 };/*
14358  * Based on:
14359  * Ext JS Library 1.1.1
14360  * Copyright(c) 2006-2007, Ext JS, LLC.
14361  *
14362  * Originally Released Under LGPL - original licence link has changed is not relivant.
14363  *
14364  * Fork - LGPL
14365  * <script type="text/javascript">
14366  */
14367 /**
14368  * @class Roo.util.TaskRunner
14369  * Manage background tasks - not sure why this is better that setInterval?
14370  * @static
14371  *
14372  */
14373  
14374 Roo.util.TaskRunner = function(interval){
14375     interval = interval || 10;
14376     var tasks = [], removeQueue = [];
14377     var id = 0;
14378     var running = false;
14379
14380     var stopThread = function(){
14381         running = false;
14382         clearInterval(id);
14383         id = 0;
14384     };
14385
14386     var startThread = function(){
14387         if(!running){
14388             running = true;
14389             id = setInterval(runTasks, interval);
14390         }
14391     };
14392
14393     var removeTask = function(task){
14394         removeQueue.push(task);
14395         if(task.onStop){
14396             task.onStop();
14397         }
14398     };
14399
14400     var runTasks = function(){
14401         if(removeQueue.length > 0){
14402             for(var i = 0, len = removeQueue.length; i < len; i++){
14403                 tasks.remove(removeQueue[i]);
14404             }
14405             removeQueue = [];
14406             if(tasks.length < 1){
14407                 stopThread();
14408                 return;
14409             }
14410         }
14411         var now = new Date().getTime();
14412         for(var i = 0, len = tasks.length; i < len; ++i){
14413             var t = tasks[i];
14414             var itime = now - t.taskRunTime;
14415             if(t.interval <= itime){
14416                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14417                 t.taskRunTime = now;
14418                 if(rt === false || t.taskRunCount === t.repeat){
14419                     removeTask(t);
14420                     return;
14421                 }
14422             }
14423             if(t.duration && t.duration <= (now - t.taskStartTime)){
14424                 removeTask(t);
14425             }
14426         }
14427     };
14428
14429     /**
14430      * Queues a new task.
14431      * @param {Object} task
14432      *
14433      * Task property : interval = how frequent to run.
14434      * Task object should implement
14435      * function run()
14436      * Task object may implement
14437      * function onStop()
14438      */
14439     this.start = function(task){
14440         tasks.push(task);
14441         task.taskStartTime = new Date().getTime();
14442         task.taskRunTime = 0;
14443         task.taskRunCount = 0;
14444         startThread();
14445         return task;
14446     };
14447     /**
14448      * Stop  new task.
14449      * @param {Object} task
14450      */
14451     this.stop = function(task){
14452         removeTask(task);
14453         return task;
14454     };
14455     /**
14456      * Stop all Tasks
14457      */
14458     this.stopAll = function(){
14459         stopThread();
14460         for(var i = 0, len = tasks.length; i < len; i++){
14461             if(tasks[i].onStop){
14462                 tasks[i].onStop();
14463             }
14464         }
14465         tasks = [];
14466         removeQueue = [];
14467     };
14468 };
14469
14470 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14471  * Based on:
14472  * Ext JS Library 1.1.1
14473  * Copyright(c) 2006-2007, Ext JS, LLC.
14474  *
14475  * Originally Released Under LGPL - original licence link has changed is not relivant.
14476  *
14477  * Fork - LGPL
14478  * <script type="text/javascript">
14479  */
14480
14481  
14482 /**
14483  * @class Roo.util.MixedCollection
14484  * @extends Roo.util.Observable
14485  * A Collection class that maintains both numeric indexes and keys and exposes events.
14486  * @constructor
14487  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14488  * collection (defaults to false)
14489  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14490  * and return the key value for that item.  This is used when available to look up the key on items that
14491  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14492  * equivalent to providing an implementation for the {@link #getKey} method.
14493  */
14494 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14495     this.items = [];
14496     this.map = {};
14497     this.keys = [];
14498     this.length = 0;
14499     this.addEvents({
14500         /**
14501          * @event clear
14502          * Fires when the collection is cleared.
14503          */
14504         "clear" : true,
14505         /**
14506          * @event add
14507          * Fires when an item is added to the collection.
14508          * @param {Number} index The index at which the item was added.
14509          * @param {Object} o The item added.
14510          * @param {String} key The key associated with the added item.
14511          */
14512         "add" : true,
14513         /**
14514          * @event replace
14515          * Fires when an item is replaced in the collection.
14516          * @param {String} key he key associated with the new added.
14517          * @param {Object} old The item being replaced.
14518          * @param {Object} new The new item.
14519          */
14520         "replace" : true,
14521         /**
14522          * @event remove
14523          * Fires when an item is removed from the collection.
14524          * @param {Object} o The item being removed.
14525          * @param {String} key (optional) The key associated with the removed item.
14526          */
14527         "remove" : true,
14528         "sort" : true
14529     });
14530     this.allowFunctions = allowFunctions === true;
14531     if(keyFn){
14532         this.getKey = keyFn;
14533     }
14534     Roo.util.MixedCollection.superclass.constructor.call(this);
14535 };
14536
14537 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14538     allowFunctions : false,
14539     
14540 /**
14541  * Adds an item to the collection.
14542  * @param {String} key The key to associate with the item
14543  * @param {Object} o The item to add.
14544  * @return {Object} The item added.
14545  */
14546     add : function(key, o){
14547         if(arguments.length == 1){
14548             o = arguments[0];
14549             key = this.getKey(o);
14550         }
14551         if(typeof key == "undefined" || key === null){
14552             this.length++;
14553             this.items.push(o);
14554             this.keys.push(null);
14555         }else{
14556             var old = this.map[key];
14557             if(old){
14558                 return this.replace(key, o);
14559             }
14560             this.length++;
14561             this.items.push(o);
14562             this.map[key] = o;
14563             this.keys.push(key);
14564         }
14565         this.fireEvent("add", this.length-1, o, key);
14566         return o;
14567     },
14568        
14569 /**
14570   * MixedCollection has a generic way to fetch keys if you implement getKey.
14571 <pre><code>
14572 // normal way
14573 var mc = new Roo.util.MixedCollection();
14574 mc.add(someEl.dom.id, someEl);
14575 mc.add(otherEl.dom.id, otherEl);
14576 //and so on
14577
14578 // using getKey
14579 var mc = new Roo.util.MixedCollection();
14580 mc.getKey = function(el){
14581    return el.dom.id;
14582 };
14583 mc.add(someEl);
14584 mc.add(otherEl);
14585
14586 // or via the constructor
14587 var mc = new Roo.util.MixedCollection(false, function(el){
14588    return el.dom.id;
14589 });
14590 mc.add(someEl);
14591 mc.add(otherEl);
14592 </code></pre>
14593  * @param o {Object} The item for which to find the key.
14594  * @return {Object} The key for the passed item.
14595  */
14596     getKey : function(o){
14597          return o.id; 
14598     },
14599    
14600 /**
14601  * Replaces an item in the collection.
14602  * @param {String} key The key associated with the item to replace, or the item to replace.
14603  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14604  * @return {Object}  The new item.
14605  */
14606     replace : function(key, o){
14607         if(arguments.length == 1){
14608             o = arguments[0];
14609             key = this.getKey(o);
14610         }
14611         var old = this.item(key);
14612         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14613              return this.add(key, o);
14614         }
14615         var index = this.indexOfKey(key);
14616         this.items[index] = o;
14617         this.map[key] = o;
14618         this.fireEvent("replace", key, old, o);
14619         return o;
14620     },
14621    
14622 /**
14623  * Adds all elements of an Array or an Object to the collection.
14624  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14625  * an Array of values, each of which are added to the collection.
14626  */
14627     addAll : function(objs){
14628         if(arguments.length > 1 || objs instanceof Array){
14629             var args = arguments.length > 1 ? arguments : objs;
14630             for(var i = 0, len = args.length; i < len; i++){
14631                 this.add(args[i]);
14632             }
14633         }else{
14634             for(var key in objs){
14635                 if(this.allowFunctions || typeof objs[key] != "function"){
14636                     this.add(key, objs[key]);
14637                 }
14638             }
14639         }
14640     },
14641    
14642 /**
14643  * Executes the specified function once for every item in the collection, passing each
14644  * item as the first and only parameter. returning false from the function will stop the iteration.
14645  * @param {Function} fn The function to execute for each item.
14646  * @param {Object} scope (optional) The scope in which to execute the function.
14647  */
14648     each : function(fn, scope){
14649         var items = [].concat(this.items); // each safe for removal
14650         for(var i = 0, len = items.length; i < len; i++){
14651             if(fn.call(scope || items[i], items[i], i, len) === false){
14652                 break;
14653             }
14654         }
14655     },
14656    
14657 /**
14658  * Executes the specified function once for every key in the collection, passing each
14659  * key, and its associated item as the first two parameters.
14660  * @param {Function} fn The function to execute for each item.
14661  * @param {Object} scope (optional) The scope in which to execute the function.
14662  */
14663     eachKey : function(fn, scope){
14664         for(var i = 0, len = this.keys.length; i < len; i++){
14665             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14666         }
14667     },
14668    
14669 /**
14670  * Returns the first item in the collection which elicits a true return value from the
14671  * passed selection function.
14672  * @param {Function} fn The selection function to execute for each item.
14673  * @param {Object} scope (optional) The scope in which to execute the function.
14674  * @return {Object} The first item in the collection which returned true from the selection function.
14675  */
14676     find : function(fn, scope){
14677         for(var i = 0, len = this.items.length; i < len; i++){
14678             if(fn.call(scope || window, this.items[i], this.keys[i])){
14679                 return this.items[i];
14680             }
14681         }
14682         return null;
14683     },
14684    
14685 /**
14686  * Inserts an item at the specified index in the collection.
14687  * @param {Number} index The index to insert the item at.
14688  * @param {String} key The key to associate with the new item, or the item itself.
14689  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14690  * @return {Object} The item inserted.
14691  */
14692     insert : function(index, key, o){
14693         if(arguments.length == 2){
14694             o = arguments[1];
14695             key = this.getKey(o);
14696         }
14697         if(index >= this.length){
14698             return this.add(key, o);
14699         }
14700         this.length++;
14701         this.items.splice(index, 0, o);
14702         if(typeof key != "undefined" && key != null){
14703             this.map[key] = o;
14704         }
14705         this.keys.splice(index, 0, key);
14706         this.fireEvent("add", index, o, key);
14707         return o;
14708     },
14709    
14710 /**
14711  * Removed an item from the collection.
14712  * @param {Object} o The item to remove.
14713  * @return {Object} The item removed.
14714  */
14715     remove : function(o){
14716         return this.removeAt(this.indexOf(o));
14717     },
14718    
14719 /**
14720  * Remove an item from a specified index in the collection.
14721  * @param {Number} index The index within the collection of the item to remove.
14722  */
14723     removeAt : function(index){
14724         if(index < this.length && index >= 0){
14725             this.length--;
14726             var o = this.items[index];
14727             this.items.splice(index, 1);
14728             var key = this.keys[index];
14729             if(typeof key != "undefined"){
14730                 delete this.map[key];
14731             }
14732             this.keys.splice(index, 1);
14733             this.fireEvent("remove", o, key);
14734         }
14735     },
14736    
14737 /**
14738  * Removed an item associated with the passed key fom the collection.
14739  * @param {String} key The key of the item to remove.
14740  */
14741     removeKey : function(key){
14742         return this.removeAt(this.indexOfKey(key));
14743     },
14744    
14745 /**
14746  * Returns the number of items in the collection.
14747  * @return {Number} the number of items in the collection.
14748  */
14749     getCount : function(){
14750         return this.length; 
14751     },
14752    
14753 /**
14754  * Returns index within the collection of the passed Object.
14755  * @param {Object} o The item to find the index of.
14756  * @return {Number} index of the item.
14757  */
14758     indexOf : function(o){
14759         if(!this.items.indexOf){
14760             for(var i = 0, len = this.items.length; i < len; i++){
14761                 if(this.items[i] == o) {
14762                     return i;
14763                 }
14764             }
14765             return -1;
14766         }else{
14767             return this.items.indexOf(o);
14768         }
14769     },
14770    
14771 /**
14772  * Returns index within the collection of the passed key.
14773  * @param {String} key The key to find the index of.
14774  * @return {Number} index of the key.
14775  */
14776     indexOfKey : function(key){
14777         if(!this.keys.indexOf){
14778             for(var i = 0, len = this.keys.length; i < len; i++){
14779                 if(this.keys[i] == key) {
14780                     return i;
14781                 }
14782             }
14783             return -1;
14784         }else{
14785             return this.keys.indexOf(key);
14786         }
14787     },
14788    
14789 /**
14790  * Returns the item associated with the passed key OR index. Key has priority over index.
14791  * @param {String/Number} key The key or index of the item.
14792  * @return {Object} The item associated with the passed key.
14793  */
14794     item : function(key){
14795         if (key === 'length') {
14796             return null;
14797         }
14798         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14799         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14800     },
14801     
14802 /**
14803  * Returns the item at the specified index.
14804  * @param {Number} index The index of the item.
14805  * @return {Object}
14806  */
14807     itemAt : function(index){
14808         return this.items[index];
14809     },
14810     
14811 /**
14812  * Returns the item associated with the passed key.
14813  * @param {String/Number} key The key of the item.
14814  * @return {Object} The item associated with the passed key.
14815  */
14816     key : function(key){
14817         return this.map[key];
14818     },
14819    
14820 /**
14821  * Returns true if the collection contains the passed Object as an item.
14822  * @param {Object} o  The Object to look for in the collection.
14823  * @return {Boolean} True if the collection contains the Object as an item.
14824  */
14825     contains : function(o){
14826         return this.indexOf(o) != -1;
14827     },
14828    
14829 /**
14830  * Returns true if the collection contains the passed Object as a key.
14831  * @param {String} key The key to look for in the collection.
14832  * @return {Boolean} True if the collection contains the Object as a key.
14833  */
14834     containsKey : function(key){
14835         return typeof this.map[key] != "undefined";
14836     },
14837    
14838 /**
14839  * Removes all items from the collection.
14840  */
14841     clear : function(){
14842         this.length = 0;
14843         this.items = [];
14844         this.keys = [];
14845         this.map = {};
14846         this.fireEvent("clear");
14847     },
14848    
14849 /**
14850  * Returns the first item in the collection.
14851  * @return {Object} the first item in the collection..
14852  */
14853     first : function(){
14854         return this.items[0]; 
14855     },
14856    
14857 /**
14858  * Returns the last item in the collection.
14859  * @return {Object} the last item in the collection..
14860  */
14861     last : function(){
14862         return this.items[this.length-1];   
14863     },
14864     
14865     _sort : function(property, dir, fn){
14866         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14867         fn = fn || function(a, b){
14868             return a-b;
14869         };
14870         var c = [], k = this.keys, items = this.items;
14871         for(var i = 0, len = items.length; i < len; i++){
14872             c[c.length] = {key: k[i], value: items[i], index: i};
14873         }
14874         c.sort(function(a, b){
14875             var v = fn(a[property], b[property]) * dsc;
14876             if(v == 0){
14877                 v = (a.index < b.index ? -1 : 1);
14878             }
14879             return v;
14880         });
14881         for(var i = 0, len = c.length; i < len; i++){
14882             items[i] = c[i].value;
14883             k[i] = c[i].key;
14884         }
14885         this.fireEvent("sort", this);
14886     },
14887     
14888     /**
14889      * Sorts this collection with the passed comparison function
14890      * @param {String} direction (optional) "ASC" or "DESC"
14891      * @param {Function} fn (optional) comparison function
14892      */
14893     sort : function(dir, fn){
14894         this._sort("value", dir, fn);
14895     },
14896     
14897     /**
14898      * Sorts this collection by keys
14899      * @param {String} direction (optional) "ASC" or "DESC"
14900      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14901      */
14902     keySort : function(dir, fn){
14903         this._sort("key", dir, fn || function(a, b){
14904             return String(a).toUpperCase()-String(b).toUpperCase();
14905         });
14906     },
14907     
14908     /**
14909      * Returns a range of items in this collection
14910      * @param {Number} startIndex (optional) defaults to 0
14911      * @param {Number} endIndex (optional) default to the last item
14912      * @return {Array} An array of items
14913      */
14914     getRange : function(start, end){
14915         var items = this.items;
14916         if(items.length < 1){
14917             return [];
14918         }
14919         start = start || 0;
14920         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14921         var r = [];
14922         if(start <= end){
14923             for(var i = start; i <= end; i++) {
14924                     r[r.length] = items[i];
14925             }
14926         }else{
14927             for(var i = start; i >= end; i--) {
14928                     r[r.length] = items[i];
14929             }
14930         }
14931         return r;
14932     },
14933         
14934     /**
14935      * Filter the <i>objects</i> in this collection by a specific property. 
14936      * Returns a new collection that has been filtered.
14937      * @param {String} property A property on your objects
14938      * @param {String/RegExp} value Either string that the property values 
14939      * should start with or a RegExp to test against the property
14940      * @return {MixedCollection} The new filtered collection
14941      */
14942     filter : function(property, value){
14943         if(!value.exec){ // not a regex
14944             value = String(value);
14945             if(value.length == 0){
14946                 return this.clone();
14947             }
14948             value = new RegExp("^" + Roo.escapeRe(value), "i");
14949         }
14950         return this.filterBy(function(o){
14951             return o && value.test(o[property]);
14952         });
14953         },
14954     
14955     /**
14956      * Filter by a function. * Returns a new collection that has been filtered.
14957      * The passed function will be called with each 
14958      * object in the collection. If the function returns true, the value is included 
14959      * otherwise it is filtered.
14960      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14961      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14962      * @return {MixedCollection} The new filtered collection
14963      */
14964     filterBy : function(fn, scope){
14965         var r = new Roo.util.MixedCollection();
14966         r.getKey = this.getKey;
14967         var k = this.keys, it = this.items;
14968         for(var i = 0, len = it.length; i < len; i++){
14969             if(fn.call(scope||this, it[i], k[i])){
14970                                 r.add(k[i], it[i]);
14971                         }
14972         }
14973         return r;
14974     },
14975     
14976     /**
14977      * Creates a duplicate of this collection
14978      * @return {MixedCollection}
14979      */
14980     clone : function(){
14981         var r = new Roo.util.MixedCollection();
14982         var k = this.keys, it = this.items;
14983         for(var i = 0, len = it.length; i < len; i++){
14984             r.add(k[i], it[i]);
14985         }
14986         r.getKey = this.getKey;
14987         return r;
14988     }
14989 });
14990 /**
14991  * Returns the item associated with the passed key or index.
14992  * @method
14993  * @param {String/Number} key The key or index of the item.
14994  * @return {Object} The item associated with the passed key.
14995  */
14996 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14997  * Based on:
14998  * Ext JS Library 1.1.1
14999  * Copyright(c) 2006-2007, Ext JS, LLC.
15000  *
15001  * Originally Released Under LGPL - original licence link has changed is not relivant.
15002  *
15003  * Fork - LGPL
15004  * <script type="text/javascript">
15005  */
15006 /**
15007  * @class Roo.util.JSON
15008  * Modified version of Douglas Crockford"s json.js that doesn"t
15009  * mess with the Object prototype 
15010  * http://www.json.org/js.html
15011  * @static
15012  */
15013 Roo.util.JSON = new (function(){
15014     var useHasOwn = {}.hasOwnProperty ? true : false;
15015     
15016     // crashes Safari in some instances
15017     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
15018     
15019     var pad = function(n) {
15020         return n < 10 ? "0" + n : n;
15021     };
15022     
15023     var m = {
15024         "\b": '\\b',
15025         "\t": '\\t',
15026         "\n": '\\n',
15027         "\f": '\\f',
15028         "\r": '\\r',
15029         '"' : '\\"',
15030         "\\": '\\\\'
15031     };
15032
15033     var encodeString = function(s){
15034         if (/["\\\x00-\x1f]/.test(s)) {
15035             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
15036                 var c = m[b];
15037                 if(c){
15038                     return c;
15039                 }
15040                 c = b.charCodeAt();
15041                 return "\\u00" +
15042                     Math.floor(c / 16).toString(16) +
15043                     (c % 16).toString(16);
15044             }) + '"';
15045         }
15046         return '"' + s + '"';
15047     };
15048     
15049     var encodeArray = function(o){
15050         var a = ["["], b, i, l = o.length, v;
15051             for (i = 0; i < l; i += 1) {
15052                 v = o[i];
15053                 switch (typeof v) {
15054                     case "undefined":
15055                     case "function":
15056                     case "unknown":
15057                         break;
15058                     default:
15059                         if (b) {
15060                             a.push(',');
15061                         }
15062                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
15063                         b = true;
15064                 }
15065             }
15066             a.push("]");
15067             return a.join("");
15068     };
15069     
15070     var encodeDate = function(o){
15071         return '"' + o.getFullYear() + "-" +
15072                 pad(o.getMonth() + 1) + "-" +
15073                 pad(o.getDate()) + "T" +
15074                 pad(o.getHours()) + ":" +
15075                 pad(o.getMinutes()) + ":" +
15076                 pad(o.getSeconds()) + '"';
15077     };
15078     
15079     /**
15080      * Encodes an Object, Array or other value
15081      * @param {Mixed} o The variable to encode
15082      * @return {String} The JSON string
15083      */
15084     this.encode = function(o)
15085     {
15086         // should this be extended to fully wrap stringify..
15087         
15088         if(typeof o == "undefined" || o === null){
15089             return "null";
15090         }else if(o instanceof Array){
15091             return encodeArray(o);
15092         }else if(o instanceof Date){
15093             return encodeDate(o);
15094         }else if(typeof o == "string"){
15095             return encodeString(o);
15096         }else if(typeof o == "number"){
15097             return isFinite(o) ? String(o) : "null";
15098         }else if(typeof o == "boolean"){
15099             return String(o);
15100         }else {
15101             var a = ["{"], b, i, v;
15102             for (i in o) {
15103                 if(!useHasOwn || o.hasOwnProperty(i)) {
15104                     v = o[i];
15105                     switch (typeof v) {
15106                     case "undefined":
15107                     case "function":
15108                     case "unknown":
15109                         break;
15110                     default:
15111                         if(b){
15112                             a.push(',');
15113                         }
15114                         a.push(this.encode(i), ":",
15115                                 v === null ? "null" : this.encode(v));
15116                         b = true;
15117                     }
15118                 }
15119             }
15120             a.push("}");
15121             return a.join("");
15122         }
15123     };
15124     
15125     /**
15126      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
15127      * @param {String} json The JSON string
15128      * @return {Object} The resulting object
15129      */
15130     this.decode = function(json){
15131         
15132         return  /** eval:var:json */ eval("(" + json + ')');
15133     };
15134 })();
15135 /** 
15136  * Shorthand for {@link Roo.util.JSON#encode}
15137  * @member Roo encode 
15138  * @method */
15139 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
15140 /** 
15141  * Shorthand for {@link Roo.util.JSON#decode}
15142  * @member Roo decode 
15143  * @method */
15144 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
15145 /*
15146  * Based on:
15147  * Ext JS Library 1.1.1
15148  * Copyright(c) 2006-2007, Ext JS, LLC.
15149  *
15150  * Originally Released Under LGPL - original licence link has changed is not relivant.
15151  *
15152  * Fork - LGPL
15153  * <script type="text/javascript">
15154  */
15155  
15156 /**
15157  * @class Roo.util.Format
15158  * Reusable data formatting functions
15159  * @static
15160  */
15161 Roo.util.Format = function(){
15162     var trimRe = /^\s+|\s+$/g;
15163     return {
15164         /**
15165          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
15166          * @param {String} value The string to truncate
15167          * @param {Number} length The maximum length to allow before truncating
15168          * @return {String} The converted text
15169          */
15170         ellipsis : function(value, len){
15171             if(value && value.length > len){
15172                 return value.substr(0, len-3)+"...";
15173             }
15174             return value;
15175         },
15176
15177         /**
15178          * Checks a reference and converts it to empty string if it is undefined
15179          * @param {Mixed} value Reference to check
15180          * @return {Mixed} Empty string if converted, otherwise the original value
15181          */
15182         undef : function(value){
15183             return typeof value != "undefined" ? value : "";
15184         },
15185
15186         /**
15187          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
15188          * @param {String} value The string to encode
15189          * @return {String} The encoded text
15190          */
15191         htmlEncode : function(value){
15192             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
15193         },
15194
15195         /**
15196          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
15197          * @param {String} value The string to decode
15198          * @return {String} The decoded text
15199          */
15200         htmlDecode : function(value){
15201             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
15202         },
15203
15204         /**
15205          * Trims any whitespace from either side of a string
15206          * @param {String} value The text to trim
15207          * @return {String} The trimmed text
15208          */
15209         trim : function(value){
15210             return String(value).replace(trimRe, "");
15211         },
15212
15213         /**
15214          * Returns a substring from within an original string
15215          * @param {String} value The original text
15216          * @param {Number} start The start index of the substring
15217          * @param {Number} length The length of the substring
15218          * @return {String} The substring
15219          */
15220         substr : function(value, start, length){
15221             return String(value).substr(start, length);
15222         },
15223
15224         /**
15225          * Converts a string to all lower case letters
15226          * @param {String} value The text to convert
15227          * @return {String} The converted text
15228          */
15229         lowercase : function(value){
15230             return String(value).toLowerCase();
15231         },
15232
15233         /**
15234          * Converts a string to all upper case letters
15235          * @param {String} value The text to convert
15236          * @return {String} The converted text
15237          */
15238         uppercase : function(value){
15239             return String(value).toUpperCase();
15240         },
15241
15242         /**
15243          * Converts the first character only of a string to upper case
15244          * @param {String} value The text to convert
15245          * @return {String} The converted text
15246          */
15247         capitalize : function(value){
15248             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15249         },
15250
15251         // private
15252         call : function(value, fn){
15253             if(arguments.length > 2){
15254                 var args = Array.prototype.slice.call(arguments, 2);
15255                 args.unshift(value);
15256                  
15257                 return /** eval:var:value */  eval(fn).apply(window, args);
15258             }else{
15259                 /** eval:var:value */
15260                 return /** eval:var:value */ eval(fn).call(window, value);
15261             }
15262         },
15263
15264        
15265         /**
15266          * safer version of Math.toFixed..??/
15267          * @param {Number/String} value The numeric value to format
15268          * @param {Number/String} value Decimal places 
15269          * @return {String} The formatted currency string
15270          */
15271         toFixed : function(v, n)
15272         {
15273             // why not use to fixed - precision is buggered???
15274             if (!n) {
15275                 return Math.round(v-0);
15276             }
15277             var fact = Math.pow(10,n+1);
15278             v = (Math.round((v-0)*fact))/fact;
15279             var z = (''+fact).substring(2);
15280             if (v == Math.floor(v)) {
15281                 return Math.floor(v) + '.' + z;
15282             }
15283             
15284             // now just padd decimals..
15285             var ps = String(v).split('.');
15286             var fd = (ps[1] + z);
15287             var r = fd.substring(0,n); 
15288             var rm = fd.substring(n); 
15289             if (rm < 5) {
15290                 return ps[0] + '.' + r;
15291             }
15292             r*=1; // turn it into a number;
15293             r++;
15294             if (String(r).length != n) {
15295                 ps[0]*=1;
15296                 ps[0]++;
15297                 r = String(r).substring(1); // chop the end off.
15298             }
15299             
15300             return ps[0] + '.' + r;
15301              
15302         },
15303         
15304         /**
15305          * Format a number as US currency
15306          * @param {Number/String} value The numeric value to format
15307          * @return {String} The formatted currency string
15308          */
15309         usMoney : function(v){
15310             return '$' + Roo.util.Format.number(v);
15311         },
15312         
15313         /**
15314          * Format a number
15315          * eventually this should probably emulate php's number_format
15316          * @param {Number/String} value The numeric value to format
15317          * @param {Number} decimals number of decimal places
15318          * @param {String} delimiter for thousands (default comma)
15319          * @return {String} The formatted currency string
15320          */
15321         number : function(v, decimals, thousandsDelimiter)
15322         {
15323             // multiply and round.
15324             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15325             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15326             
15327             var mul = Math.pow(10, decimals);
15328             var zero = String(mul).substring(1);
15329             v = (Math.round((v-0)*mul))/mul;
15330             
15331             // if it's '0' number.. then
15332             
15333             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15334             v = String(v);
15335             var ps = v.split('.');
15336             var whole = ps[0];
15337             
15338             var r = /(\d+)(\d{3})/;
15339             // add comma's
15340             
15341             if(thousandsDelimiter.length != 0) {
15342                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15343             } 
15344             
15345             var sub = ps[1] ?
15346                     // has decimals..
15347                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15348                     // does not have decimals
15349                     (decimals ? ('.' + zero) : '');
15350             
15351             
15352             return whole + sub ;
15353         },
15354         
15355         /**
15356          * Parse a value into a formatted date using the specified format pattern.
15357          * @param {Mixed} value The value to format
15358          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15359          * @return {String} The formatted date string
15360          */
15361         date : function(v, format){
15362             if(!v){
15363                 return "";
15364             }
15365             if(!(v instanceof Date)){
15366                 v = new Date(Date.parse(v));
15367             }
15368             return v.dateFormat(format || Roo.util.Format.defaults.date);
15369         },
15370
15371         /**
15372          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15373          * @param {String} format Any valid date format string
15374          * @return {Function} The date formatting function
15375          */
15376         dateRenderer : function(format){
15377             return function(v){
15378                 return Roo.util.Format.date(v, format);  
15379             };
15380         },
15381
15382         // private
15383         stripTagsRE : /<\/?[^>]+>/gi,
15384         
15385         /**
15386          * Strips all HTML tags
15387          * @param {Mixed} value The text from which to strip tags
15388          * @return {String} The stripped text
15389          */
15390         stripTags : function(v){
15391             return !v ? v : String(v).replace(this.stripTagsRE, "");
15392         },
15393         
15394         /**
15395          * Size in Mb,Gb etc.
15396          * @param {Number} value The number to be formated
15397          * @param {number} decimals how many decimal places
15398          * @return {String} the formated string
15399          */
15400         size : function(value, decimals)
15401         {
15402             var sizes = ['b', 'k', 'M', 'G', 'T'];
15403             if (value == 0) {
15404                 return 0;
15405             }
15406             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15407             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15408         }
15409         
15410         
15411         
15412     };
15413 }();
15414 Roo.util.Format.defaults = {
15415     date : 'd/M/Y'
15416 };/*
15417  * Based on:
15418  * Ext JS Library 1.1.1
15419  * Copyright(c) 2006-2007, Ext JS, LLC.
15420  *
15421  * Originally Released Under LGPL - original licence link has changed is not relivant.
15422  *
15423  * Fork - LGPL
15424  * <script type="text/javascript">
15425  */
15426
15427
15428  
15429
15430 /**
15431  * @class Roo.MasterTemplate
15432  * @extends Roo.Template
15433  * Provides a template that can have child templates. The syntax is:
15434 <pre><code>
15435 var t = new Roo.MasterTemplate(
15436         '&lt;select name="{name}"&gt;',
15437                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15438         '&lt;/select&gt;'
15439 );
15440 t.add('options', {value: 'foo', text: 'bar'});
15441 // or you can add multiple child elements in one shot
15442 t.addAll('options', [
15443     {value: 'foo', text: 'bar'},
15444     {value: 'foo2', text: 'bar2'},
15445     {value: 'foo3', text: 'bar3'}
15446 ]);
15447 // then append, applying the master template values
15448 t.append('my-form', {name: 'my-select'});
15449 </code></pre>
15450 * A name attribute for the child template is not required if you have only one child
15451 * template or you want to refer to them by index.
15452  */
15453 Roo.MasterTemplate = function(){
15454     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15455     this.originalHtml = this.html;
15456     var st = {};
15457     var m, re = this.subTemplateRe;
15458     re.lastIndex = 0;
15459     var subIndex = 0;
15460     while(m = re.exec(this.html)){
15461         var name = m[1], content = m[2];
15462         st[subIndex] = {
15463             name: name,
15464             index: subIndex,
15465             buffer: [],
15466             tpl : new Roo.Template(content)
15467         };
15468         if(name){
15469             st[name] = st[subIndex];
15470         }
15471         st[subIndex].tpl.compile();
15472         st[subIndex].tpl.call = this.call.createDelegate(this);
15473         subIndex++;
15474     }
15475     this.subCount = subIndex;
15476     this.subs = st;
15477 };
15478 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15479     /**
15480     * The regular expression used to match sub templates
15481     * @type RegExp
15482     * @property
15483     */
15484     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15485
15486     /**
15487      * Applies the passed values to a child template.
15488      * @param {String/Number} name (optional) The name or index of the child template
15489      * @param {Array/Object} values The values to be applied to the template
15490      * @return {MasterTemplate} this
15491      */
15492      add : function(name, values){
15493         if(arguments.length == 1){
15494             values = arguments[0];
15495             name = 0;
15496         }
15497         var s = this.subs[name];
15498         s.buffer[s.buffer.length] = s.tpl.apply(values);
15499         return this;
15500     },
15501
15502     /**
15503      * Applies all the passed values to a child template.
15504      * @param {String/Number} name (optional) The name or index of the child template
15505      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15506      * @param {Boolean} reset (optional) True to reset the template first
15507      * @return {MasterTemplate} this
15508      */
15509     fill : function(name, values, reset){
15510         var a = arguments;
15511         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15512             values = a[0];
15513             name = 0;
15514             reset = a[1];
15515         }
15516         if(reset){
15517             this.reset();
15518         }
15519         for(var i = 0, len = values.length; i < len; i++){
15520             this.add(name, values[i]);
15521         }
15522         return this;
15523     },
15524
15525     /**
15526      * Resets the template for reuse
15527      * @return {MasterTemplate} this
15528      */
15529      reset : function(){
15530         var s = this.subs;
15531         for(var i = 0; i < this.subCount; i++){
15532             s[i].buffer = [];
15533         }
15534         return this;
15535     },
15536
15537     applyTemplate : function(values){
15538         var s = this.subs;
15539         var replaceIndex = -1;
15540         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15541             return s[++replaceIndex].buffer.join("");
15542         });
15543         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15544     },
15545
15546     apply : function(){
15547         return this.applyTemplate.apply(this, arguments);
15548     },
15549
15550     compile : function(){return this;}
15551 });
15552
15553 /**
15554  * Alias for fill().
15555  * @method
15556  */
15557 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15558  /**
15559  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15560  * var tpl = Roo.MasterTemplate.from('element-id');
15561  * @param {String/HTMLElement} el
15562  * @param {Object} config
15563  * @static
15564  */
15565 Roo.MasterTemplate.from = function(el, config){
15566     el = Roo.getDom(el);
15567     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15568 };/*
15569  * Based on:
15570  * Ext JS Library 1.1.1
15571  * Copyright(c) 2006-2007, Ext JS, LLC.
15572  *
15573  * Originally Released Under LGPL - original licence link has changed is not relivant.
15574  *
15575  * Fork - LGPL
15576  * <script type="text/javascript">
15577  */
15578
15579  
15580 /**
15581  * @class Roo.util.CSS
15582  * Utility class for manipulating CSS rules
15583  * @static
15584
15585  */
15586 Roo.util.CSS = function(){
15587         var rules = null;
15588         var doc = document;
15589
15590     var camelRe = /(-[a-z])/gi;
15591     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15592
15593    return {
15594    /**
15595     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15596     * tag and appended to the HEAD of the document.
15597     * @param {String|Object} cssText The text containing the css rules
15598     * @param {String} id An id to add to the stylesheet for later removal
15599     * @return {StyleSheet}
15600     */
15601     createStyleSheet : function(cssText, id){
15602         var ss;
15603         var head = doc.getElementsByTagName("head")[0];
15604         var nrules = doc.createElement("style");
15605         nrules.setAttribute("type", "text/css");
15606         if(id){
15607             nrules.setAttribute("id", id);
15608         }
15609         if (typeof(cssText) != 'string') {
15610             // support object maps..
15611             // not sure if this a good idea.. 
15612             // perhaps it should be merged with the general css handling
15613             // and handle js style props.
15614             var cssTextNew = [];
15615             for(var n in cssText) {
15616                 var citems = [];
15617                 for(var k in cssText[n]) {
15618                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15619                 }
15620                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15621                 
15622             }
15623             cssText = cssTextNew.join("\n");
15624             
15625         }
15626        
15627        
15628        if(Roo.isIE){
15629            head.appendChild(nrules);
15630            ss = nrules.styleSheet;
15631            ss.cssText = cssText;
15632        }else{
15633            try{
15634                 nrules.appendChild(doc.createTextNode(cssText));
15635            }catch(e){
15636                nrules.cssText = cssText; 
15637            }
15638            head.appendChild(nrules);
15639            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15640        }
15641        this.cacheStyleSheet(ss);
15642        return ss;
15643    },
15644
15645    /**
15646     * Removes a style or link tag by id
15647     * @param {String} id The id of the tag
15648     */
15649    removeStyleSheet : function(id){
15650        var existing = doc.getElementById(id);
15651        if(existing){
15652            existing.parentNode.removeChild(existing);
15653        }
15654    },
15655
15656    /**
15657     * Dynamically swaps an existing stylesheet reference for a new one
15658     * @param {String} id The id of an existing link tag to remove
15659     * @param {String} url The href of the new stylesheet to include
15660     */
15661    swapStyleSheet : function(id, url){
15662        this.removeStyleSheet(id);
15663        var ss = doc.createElement("link");
15664        ss.setAttribute("rel", "stylesheet");
15665        ss.setAttribute("type", "text/css");
15666        ss.setAttribute("id", id);
15667        ss.setAttribute("href", url);
15668        doc.getElementsByTagName("head")[0].appendChild(ss);
15669    },
15670    
15671    /**
15672     * Refresh the rule cache if you have dynamically added stylesheets
15673     * @return {Object} An object (hash) of rules indexed by selector
15674     */
15675    refreshCache : function(){
15676        return this.getRules(true);
15677    },
15678
15679    // private
15680    cacheStyleSheet : function(stylesheet){
15681        if(!rules){
15682            rules = {};
15683        }
15684        try{// try catch for cross domain access issue
15685            var ssRules = stylesheet.cssRules || stylesheet.rules;
15686            for(var j = ssRules.length-1; j >= 0; --j){
15687                rules[ssRules[j].selectorText] = ssRules[j];
15688            }
15689        }catch(e){}
15690    },
15691    
15692    /**
15693     * Gets all css rules for the document
15694     * @param {Boolean} refreshCache true to refresh the internal cache
15695     * @return {Object} An object (hash) of rules indexed by selector
15696     */
15697    getRules : function(refreshCache){
15698                 if(rules == null || refreshCache){
15699                         rules = {};
15700                         var ds = doc.styleSheets;
15701                         for(var i =0, len = ds.length; i < len; i++){
15702                             try{
15703                         this.cacheStyleSheet(ds[i]);
15704                     }catch(e){} 
15705                 }
15706                 }
15707                 return rules;
15708         },
15709         
15710         /**
15711     * Gets an an individual CSS rule by selector(s)
15712     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15713     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15714     * @return {CSSRule} The CSS rule or null if one is not found
15715     */
15716    getRule : function(selector, refreshCache){
15717                 var rs = this.getRules(refreshCache);
15718                 if(!(selector instanceof Array)){
15719                     return rs[selector];
15720                 }
15721                 for(var i = 0; i < selector.length; i++){
15722                         if(rs[selector[i]]){
15723                                 return rs[selector[i]];
15724                         }
15725                 }
15726                 return null;
15727         },
15728         
15729         
15730         /**
15731     * Updates a rule property
15732     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15733     * @param {String} property The css property
15734     * @param {String} value The new value for the property
15735     * @return {Boolean} true If a rule was found and updated
15736     */
15737    updateRule : function(selector, property, value){
15738                 if(!(selector instanceof Array)){
15739                         var rule = this.getRule(selector);
15740                         if(rule){
15741                                 rule.style[property.replace(camelRe, camelFn)] = value;
15742                                 return true;
15743                         }
15744                 }else{
15745                         for(var i = 0; i < selector.length; i++){
15746                                 if(this.updateRule(selector[i], property, value)){
15747                                         return true;
15748                                 }
15749                         }
15750                 }
15751                 return false;
15752         }
15753    };   
15754 }();/*
15755  * Based on:
15756  * Ext JS Library 1.1.1
15757  * Copyright(c) 2006-2007, Ext JS, LLC.
15758  *
15759  * Originally Released Under LGPL - original licence link has changed is not relivant.
15760  *
15761  * Fork - LGPL
15762  * <script type="text/javascript">
15763  */
15764
15765  
15766
15767 /**
15768  * @class Roo.util.ClickRepeater
15769  * @extends Roo.util.Observable
15770  * 
15771  * A wrapper class which can be applied to any element. Fires a "click" event while the
15772  * mouse is pressed. The interval between firings may be specified in the config but
15773  * defaults to 10 milliseconds.
15774  * 
15775  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15776  * 
15777  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15778  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15779  * Similar to an autorepeat key delay.
15780  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15781  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15782  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15783  *           "interval" and "delay" are ignored. "immediate" is honored.
15784  * @cfg {Boolean} preventDefault True to prevent the default click event
15785  * @cfg {Boolean} stopDefault True to stop the default click event
15786  * 
15787  * @history
15788  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15789  *     2007-02-02 jvs Renamed to ClickRepeater
15790  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15791  *
15792  *  @constructor
15793  * @param {String/HTMLElement/Element} el The element to listen on
15794  * @param {Object} config
15795  **/
15796 Roo.util.ClickRepeater = function(el, config)
15797 {
15798     this.el = Roo.get(el);
15799     this.el.unselectable();
15800
15801     Roo.apply(this, config);
15802
15803     this.addEvents({
15804     /**
15805      * @event mousedown
15806      * Fires when the mouse button is depressed.
15807      * @param {Roo.util.ClickRepeater} this
15808      */
15809         "mousedown" : true,
15810     /**
15811      * @event click
15812      * Fires on a specified interval during the time the element is pressed.
15813      * @param {Roo.util.ClickRepeater} this
15814      */
15815         "click" : true,
15816     /**
15817      * @event mouseup
15818      * Fires when the mouse key is released.
15819      * @param {Roo.util.ClickRepeater} this
15820      */
15821         "mouseup" : true
15822     });
15823
15824     this.el.on("mousedown", this.handleMouseDown, this);
15825     if(this.preventDefault || this.stopDefault){
15826         this.el.on("click", function(e){
15827             if(this.preventDefault){
15828                 e.preventDefault();
15829             }
15830             if(this.stopDefault){
15831                 e.stopEvent();
15832             }
15833         }, this);
15834     }
15835
15836     // allow inline handler
15837     if(this.handler){
15838         this.on("click", this.handler,  this.scope || this);
15839     }
15840
15841     Roo.util.ClickRepeater.superclass.constructor.call(this);
15842 };
15843
15844 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15845     interval : 20,
15846     delay: 250,
15847     preventDefault : true,
15848     stopDefault : false,
15849     timer : 0,
15850
15851     // private
15852     handleMouseDown : function(){
15853         clearTimeout(this.timer);
15854         this.el.blur();
15855         if(this.pressClass){
15856             this.el.addClass(this.pressClass);
15857         }
15858         this.mousedownTime = new Date();
15859
15860         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15861         this.el.on("mouseout", this.handleMouseOut, this);
15862
15863         this.fireEvent("mousedown", this);
15864         this.fireEvent("click", this);
15865         
15866         this.timer = this.click.defer(this.delay || this.interval, this);
15867     },
15868
15869     // private
15870     click : function(){
15871         this.fireEvent("click", this);
15872         this.timer = this.click.defer(this.getInterval(), this);
15873     },
15874
15875     // private
15876     getInterval: function(){
15877         if(!this.accelerate){
15878             return this.interval;
15879         }
15880         var pressTime = this.mousedownTime.getElapsed();
15881         if(pressTime < 500){
15882             return 400;
15883         }else if(pressTime < 1700){
15884             return 320;
15885         }else if(pressTime < 2600){
15886             return 250;
15887         }else if(pressTime < 3500){
15888             return 180;
15889         }else if(pressTime < 4400){
15890             return 140;
15891         }else if(pressTime < 5300){
15892             return 80;
15893         }else if(pressTime < 6200){
15894             return 50;
15895         }else{
15896             return 10;
15897         }
15898     },
15899
15900     // private
15901     handleMouseOut : function(){
15902         clearTimeout(this.timer);
15903         if(this.pressClass){
15904             this.el.removeClass(this.pressClass);
15905         }
15906         this.el.on("mouseover", this.handleMouseReturn, this);
15907     },
15908
15909     // private
15910     handleMouseReturn : function(){
15911         this.el.un("mouseover", this.handleMouseReturn);
15912         if(this.pressClass){
15913             this.el.addClass(this.pressClass);
15914         }
15915         this.click();
15916     },
15917
15918     // private
15919     handleMouseUp : function(){
15920         clearTimeout(this.timer);
15921         this.el.un("mouseover", this.handleMouseReturn);
15922         this.el.un("mouseout", this.handleMouseOut);
15923         Roo.get(document).un("mouseup", this.handleMouseUp);
15924         this.el.removeClass(this.pressClass);
15925         this.fireEvent("mouseup", this);
15926     }
15927 });/**
15928  * @class Roo.util.Clipboard
15929  * @static
15930  * 
15931  * Clipboard UTILS
15932  * 
15933  **/
15934 Roo.util.Clipboard = {
15935     /**
15936      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15937      * @param {String} text to copy to clipboard
15938      */
15939     write : function(text) {
15940         // navigator clipboard api needs a secure context (https)
15941         if (navigator.clipboard && window.isSecureContext) {
15942             // navigator clipboard api method'
15943             navigator.clipboard.writeText(text);
15944             return ;
15945         } 
15946         // text area method
15947         var ta = document.createElement("textarea");
15948         ta.value = text;
15949         // make the textarea out of viewport
15950         ta.style.position = "fixed";
15951         ta.style.left = "-999999px";
15952         ta.style.top = "-999999px";
15953         document.body.appendChild(ta);
15954         ta.focus();
15955         ta.select();
15956         document.execCommand('copy');
15957         (function() {
15958             ta.remove();
15959         }).defer(100);
15960         
15961     }
15962         
15963 }
15964     /*
15965  * Based on:
15966  * Ext JS Library 1.1.1
15967  * Copyright(c) 2006-2007, Ext JS, LLC.
15968  *
15969  * Originally Released Under LGPL - original licence link has changed is not relivant.
15970  *
15971  * Fork - LGPL
15972  * <script type="text/javascript">
15973  */
15974
15975  
15976 /**
15977  * @class Roo.KeyNav
15978  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15979  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15980  * way to implement custom navigation schemes for any UI component.</p>
15981  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15982  * pageUp, pageDown, del, home, end.  Usage:</p>
15983  <pre><code>
15984 var nav = new Roo.KeyNav("my-element", {
15985     "left" : function(e){
15986         this.moveLeft(e.ctrlKey);
15987     },
15988     "right" : function(e){
15989         this.moveRight(e.ctrlKey);
15990     },
15991     "enter" : function(e){
15992         this.save();
15993     },
15994     scope : this
15995 });
15996 </code></pre>
15997  * @constructor
15998  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15999  * @param {Object} config The config
16000  */
16001 Roo.KeyNav = function(el, config){
16002     this.el = Roo.get(el);
16003     Roo.apply(this, config);
16004     if(!this.disabled){
16005         this.disabled = true;
16006         this.enable();
16007     }
16008 };
16009
16010 Roo.KeyNav.prototype = {
16011     /**
16012      * @cfg {Boolean} disabled
16013      * True to disable this KeyNav instance (defaults to false)
16014      */
16015     disabled : false,
16016     /**
16017      * @cfg {String} defaultEventAction
16018      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
16019      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
16020      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
16021      */
16022     defaultEventAction: "stopEvent",
16023     /**
16024      * @cfg {Boolean} forceKeyDown
16025      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
16026      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
16027      * handle keydown instead of keypress.
16028      */
16029     forceKeyDown : false,
16030
16031     // private
16032     prepareEvent : function(e){
16033         var k = e.getKey();
16034         var h = this.keyToHandler[k];
16035         //if(h && this[h]){
16036         //    e.stopPropagation();
16037         //}
16038         if(Roo.isSafari && h && k >= 37 && k <= 40){
16039             e.stopEvent();
16040         }
16041     },
16042
16043     // private
16044     relay : function(e){
16045         var k = e.getKey();
16046         var h = this.keyToHandler[k];
16047         if(h && this[h]){
16048             if(this.doRelay(e, this[h], h) !== true){
16049                 e[this.defaultEventAction]();
16050             }
16051         }
16052     },
16053
16054     // private
16055     doRelay : function(e, h, hname){
16056         return h.call(this.scope || this, e);
16057     },
16058
16059     // possible handlers
16060     enter : false,
16061     left : false,
16062     right : false,
16063     up : false,
16064     down : false,
16065     tab : false,
16066     esc : false,
16067     pageUp : false,
16068     pageDown : false,
16069     del : false,
16070     home : false,
16071     end : false,
16072
16073     // quick lookup hash
16074     keyToHandler : {
16075         37 : "left",
16076         39 : "right",
16077         38 : "up",
16078         40 : "down",
16079         33 : "pageUp",
16080         34 : "pageDown",
16081         46 : "del",
16082         36 : "home",
16083         35 : "end",
16084         13 : "enter",
16085         27 : "esc",
16086         9  : "tab"
16087     },
16088
16089         /**
16090          * Enable this KeyNav
16091          */
16092         enable: function(){
16093                 if(this.disabled){
16094             // ie won't do special keys on keypress, no one else will repeat keys with keydown
16095             // the EventObject will normalize Safari automatically
16096             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16097                 this.el.on("keydown", this.relay,  this);
16098             }else{
16099                 this.el.on("keydown", this.prepareEvent,  this);
16100                 this.el.on("keypress", this.relay,  this);
16101             }
16102                     this.disabled = false;
16103                 }
16104         },
16105
16106         /**
16107          * Disable this KeyNav
16108          */
16109         disable: function(){
16110                 if(!this.disabled){
16111                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16112                 this.el.un("keydown", this.relay);
16113             }else{
16114                 this.el.un("keydown", this.prepareEvent);
16115                 this.el.un("keypress", this.relay);
16116             }
16117                     this.disabled = true;
16118                 }
16119         }
16120 };/*
16121  * Based on:
16122  * Ext JS Library 1.1.1
16123  * Copyright(c) 2006-2007, Ext JS, LLC.
16124  *
16125  * Originally Released Under LGPL - original licence link has changed is not relivant.
16126  *
16127  * Fork - LGPL
16128  * <script type="text/javascript">
16129  */
16130
16131  
16132 /**
16133  * @class Roo.KeyMap
16134  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
16135  * The constructor accepts the same config object as defined by {@link #addBinding}.
16136  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
16137  * combination it will call the function with this signature (if the match is a multi-key
16138  * combination the callback will still be called only once): (String key, Roo.EventObject e)
16139  * A KeyMap can also handle a string representation of keys.<br />
16140  * Usage:
16141  <pre><code>
16142 // map one key by key code
16143 var map = new Roo.KeyMap("my-element", {
16144     key: 13, // or Roo.EventObject.ENTER
16145     fn: myHandler,
16146     scope: myObject
16147 });
16148
16149 // map multiple keys to one action by string
16150 var map = new Roo.KeyMap("my-element", {
16151     key: "a\r\n\t",
16152     fn: myHandler,
16153     scope: myObject
16154 });
16155
16156 // map multiple keys to multiple actions by strings and array of codes
16157 var map = new Roo.KeyMap("my-element", [
16158     {
16159         key: [10,13],
16160         fn: function(){ alert("Return was pressed"); }
16161     }, {
16162         key: "abc",
16163         fn: function(){ alert('a, b or c was pressed'); }
16164     }, {
16165         key: "\t",
16166         ctrl:true,
16167         shift:true,
16168         fn: function(){ alert('Control + shift + tab was pressed.'); }
16169     }
16170 ]);
16171 </code></pre>
16172  * <b>Note: A KeyMap starts enabled</b>
16173  * @constructor
16174  * @param {String/HTMLElement/Roo.Element} el The element to bind to
16175  * @param {Object} config The config (see {@link #addBinding})
16176  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
16177  */
16178 Roo.KeyMap = function(el, config, eventName){
16179     this.el  = Roo.get(el);
16180     this.eventName = eventName || "keydown";
16181     this.bindings = [];
16182     if(config){
16183         this.addBinding(config);
16184     }
16185     this.enable();
16186 };
16187
16188 Roo.KeyMap.prototype = {
16189     /**
16190      * True to stop the event from bubbling and prevent the default browser action if the
16191      * key was handled by the KeyMap (defaults to false)
16192      * @type Boolean
16193      */
16194     stopEvent : false,
16195
16196     /**
16197      * Add a new binding to this KeyMap. The following config object properties are supported:
16198      * <pre>
16199 Property    Type             Description
16200 ----------  ---------------  ----------------------------------------------------------------------
16201 key         String/Array     A single keycode or an array of keycodes to handle
16202 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
16203 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
16204 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
16205 fn          Function         The function to call when KeyMap finds the expected key combination
16206 scope       Object           The scope of the callback function
16207 </pre>
16208      *
16209      * Usage:
16210      * <pre><code>
16211 // Create a KeyMap
16212 var map = new Roo.KeyMap(document, {
16213     key: Roo.EventObject.ENTER,
16214     fn: handleKey,
16215     scope: this
16216 });
16217
16218 //Add a new binding to the existing KeyMap later
16219 map.addBinding({
16220     key: 'abc',
16221     shift: true,
16222     fn: handleKey,
16223     scope: this
16224 });
16225 </code></pre>
16226      * @param {Object/Array} config A single KeyMap config or an array of configs
16227      */
16228         addBinding : function(config){
16229         if(config instanceof Array){
16230             for(var i = 0, len = config.length; i < len; i++){
16231                 this.addBinding(config[i]);
16232             }
16233             return;
16234         }
16235         var keyCode = config.key,
16236             shift = config.shift, 
16237             ctrl = config.ctrl, 
16238             alt = config.alt,
16239             fn = config.fn,
16240             scope = config.scope;
16241         if(typeof keyCode == "string"){
16242             var ks = [];
16243             var keyString = keyCode.toUpperCase();
16244             for(var j = 0, len = keyString.length; j < len; j++){
16245                 ks.push(keyString.charCodeAt(j));
16246             }
16247             keyCode = ks;
16248         }
16249         var keyArray = keyCode instanceof Array;
16250         var handler = function(e){
16251             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16252                 var k = e.getKey();
16253                 if(keyArray){
16254                     for(var i = 0, len = keyCode.length; i < len; i++){
16255                         if(keyCode[i] == k){
16256                           if(this.stopEvent){
16257                               e.stopEvent();
16258                           }
16259                           fn.call(scope || window, k, e);
16260                           return;
16261                         }
16262                     }
16263                 }else{
16264                     if(k == keyCode){
16265                         if(this.stopEvent){
16266                            e.stopEvent();
16267                         }
16268                         fn.call(scope || window, k, e);
16269                     }
16270                 }
16271             }
16272         };
16273         this.bindings.push(handler);  
16274         },
16275
16276     /**
16277      * Shorthand for adding a single key listener
16278      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16279      * following options:
16280      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16281      * @param {Function} fn The function to call
16282      * @param {Object} scope (optional) The scope of the function
16283      */
16284     on : function(key, fn, scope){
16285         var keyCode, shift, ctrl, alt;
16286         if(typeof key == "object" && !(key instanceof Array)){
16287             keyCode = key.key;
16288             shift = key.shift;
16289             ctrl = key.ctrl;
16290             alt = key.alt;
16291         }else{
16292             keyCode = key;
16293         }
16294         this.addBinding({
16295             key: keyCode,
16296             shift: shift,
16297             ctrl: ctrl,
16298             alt: alt,
16299             fn: fn,
16300             scope: scope
16301         })
16302     },
16303
16304     // private
16305     handleKeyDown : function(e){
16306             if(this.enabled){ //just in case
16307             var b = this.bindings;
16308             for(var i = 0, len = b.length; i < len; i++){
16309                 b[i].call(this, e);
16310             }
16311             }
16312         },
16313         
16314         /**
16315          * Returns true if this KeyMap is enabled
16316          * @return {Boolean} 
16317          */
16318         isEnabled : function(){
16319             return this.enabled;  
16320         },
16321         
16322         /**
16323          * Enables this KeyMap
16324          */
16325         enable: function(){
16326                 if(!this.enabled){
16327                     this.el.on(this.eventName, this.handleKeyDown, this);
16328                     this.enabled = true;
16329                 }
16330         },
16331
16332         /**
16333          * Disable this KeyMap
16334          */
16335         disable: function(){
16336                 if(this.enabled){
16337                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16338                     this.enabled = false;
16339                 }
16340         }
16341 };/*
16342  * Based on:
16343  * Ext JS Library 1.1.1
16344  * Copyright(c) 2006-2007, Ext JS, LLC.
16345  *
16346  * Originally Released Under LGPL - original licence link has changed is not relivant.
16347  *
16348  * Fork - LGPL
16349  * <script type="text/javascript">
16350  */
16351
16352  
16353 /**
16354  * @class Roo.util.TextMetrics
16355  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16356  * wide, in pixels, a given block of text will be.
16357  * @static
16358  */
16359 Roo.util.TextMetrics = function(){
16360     var shared;
16361     return {
16362         /**
16363          * Measures the size of the specified text
16364          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16365          * that can affect the size of the rendered text
16366          * @param {String} text The text to measure
16367          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16368          * in order to accurately measure the text height
16369          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16370          */
16371         measure : function(el, text, fixedWidth){
16372             if(!shared){
16373                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16374             }
16375             shared.bind(el);
16376             shared.setFixedWidth(fixedWidth || 'auto');
16377             return shared.getSize(text);
16378         },
16379
16380         /**
16381          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16382          * the overhead of multiple calls to initialize the style properties on each measurement.
16383          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16384          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16385          * in order to accurately measure the text height
16386          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16387          */
16388         createInstance : function(el, fixedWidth){
16389             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16390         }
16391     };
16392 }();
16393
16394 /**
16395  * @class Roo.util.TextMetrics.Instance
16396  * Instance of  TextMetrics Calcuation
16397  * @constructor
16398  * Create a new TextMetrics Instance
16399  * @param {Object} bindto
16400  * @param {Boolean} fixedWidth
16401  */
16402
16403 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16404 {
16405     var ml = new Roo.Element(document.createElement('div'));
16406     document.body.appendChild(ml.dom);
16407     ml.position('absolute');
16408     ml.setLeftTop(-1000, -1000);
16409     ml.hide();
16410
16411     if(fixedWidth){
16412         ml.setWidth(fixedWidth);
16413     }
16414      
16415     var instance = {
16416         /**
16417          * Returns the size of the specified text based on the internal element's style and width properties
16418          * @param {String} text The text to measure
16419          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16420          */
16421         getSize : function(text){
16422             ml.update(text);
16423             var s = ml.getSize();
16424             ml.update('');
16425             return s;
16426         },
16427
16428         /**
16429          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16430          * that can affect the size of the rendered text
16431          * @param {String/HTMLElement} el The element, dom node or id
16432          */
16433         bind : function(el){
16434             ml.setStyle(
16435                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16436             );
16437         },
16438
16439         /**
16440          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16441          * to set a fixed width in order to accurately measure the text height.
16442          * @param {Number} width The width to set on the element
16443          */
16444         setFixedWidth : function(width){
16445             ml.setWidth(width);
16446         },
16447
16448         /**
16449          * Returns the measured width of the specified text
16450          * @param {String} text The text to measure
16451          * @return {Number} width The width in pixels
16452          */
16453         getWidth : function(text){
16454             ml.dom.style.width = 'auto';
16455             return this.getSize(text).width;
16456         },
16457
16458         /**
16459          * Returns the measured height of the specified text.  For multiline text, be sure to call
16460          * {@link #setFixedWidth} if necessary.
16461          * @param {String} text The text to measure
16462          * @return {Number} height The height in pixels
16463          */
16464         getHeight : function(text){
16465             return this.getSize(text).height;
16466         }
16467     };
16468
16469     instance.bind(bindTo);
16470
16471     return instance;
16472 };
16473
16474 // backwards compat
16475 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16476  * Based on:
16477  * Ext JS Library 1.1.1
16478  * Copyright(c) 2006-2007, Ext JS, LLC.
16479  *
16480  * Originally Released Under LGPL - original licence link has changed is not relivant.
16481  *
16482  * Fork - LGPL
16483  * <script type="text/javascript">
16484  */
16485
16486 /**
16487  * @class Roo.state.Provider
16488  * Abstract base class for state provider implementations. This class provides methods
16489  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16490  * Provider interface.
16491  */
16492 Roo.state.Provider = function(){
16493     /**
16494      * @event statechange
16495      * Fires when a state change occurs.
16496      * @param {Provider} this This state provider
16497      * @param {String} key The state key which was changed
16498      * @param {String} value The encoded value for the state
16499      */
16500     this.addEvents({
16501         "statechange": true
16502     });
16503     this.state = {};
16504     Roo.state.Provider.superclass.constructor.call(this);
16505 };
16506 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16507     /**
16508      * Returns the current value for a key
16509      * @param {String} name The key name
16510      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16511      * @return {Mixed} The state data
16512      */
16513     get : function(name, defaultValue){
16514         return typeof this.state[name] == "undefined" ?
16515             defaultValue : this.state[name];
16516     },
16517     
16518     /**
16519      * Clears a value from the state
16520      * @param {String} name The key name
16521      */
16522     clear : function(name){
16523         delete this.state[name];
16524         this.fireEvent("statechange", this, name, null);
16525     },
16526     
16527     /**
16528      * Sets the value for a key
16529      * @param {String} name The key name
16530      * @param {Mixed} value The value to set
16531      */
16532     set : function(name, value){
16533         this.state[name] = value;
16534         this.fireEvent("statechange", this, name, value);
16535     },
16536     
16537     /**
16538      * Decodes a string previously encoded with {@link #encodeValue}.
16539      * @param {String} value The value to decode
16540      * @return {Mixed} The decoded value
16541      */
16542     decodeValue : function(cookie){
16543         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16544         var matches = re.exec(unescape(cookie));
16545         if(!matches || !matches[1]) {
16546             return; // non state cookie
16547         }
16548         var type = matches[1];
16549         var v = matches[2];
16550         switch(type){
16551             case "n":
16552                 return parseFloat(v);
16553             case "d":
16554                 return new Date(Date.parse(v));
16555             case "b":
16556                 return (v == "1");
16557             case "a":
16558                 var all = [];
16559                 var values = v.split("^");
16560                 for(var i = 0, len = values.length; i < len; i++){
16561                     all.push(this.decodeValue(values[i]));
16562                 }
16563                 return all;
16564            case "o":
16565                 var all = {};
16566                 var values = v.split("^");
16567                 for(var i = 0, len = values.length; i < len; i++){
16568                     var kv = values[i].split("=");
16569                     all[kv[0]] = this.decodeValue(kv[1]);
16570                 }
16571                 return all;
16572            default:
16573                 return v;
16574         }
16575     },
16576     
16577     /**
16578      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16579      * @param {Mixed} value The value to encode
16580      * @return {String} The encoded value
16581      */
16582     encodeValue : function(v){
16583         var enc;
16584         if(typeof v == "number"){
16585             enc = "n:" + v;
16586         }else if(typeof v == "boolean"){
16587             enc = "b:" + (v ? "1" : "0");
16588         }else if(v instanceof Date){
16589             enc = "d:" + v.toGMTString();
16590         }else if(v instanceof Array){
16591             var flat = "";
16592             for(var i = 0, len = v.length; i < len; i++){
16593                 flat += this.encodeValue(v[i]);
16594                 if(i != len-1) {
16595                     flat += "^";
16596                 }
16597             }
16598             enc = "a:" + flat;
16599         }else if(typeof v == "object"){
16600             var flat = "";
16601             for(var key in v){
16602                 if(typeof v[key] != "function"){
16603                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16604                 }
16605             }
16606             enc = "o:" + flat.substring(0, flat.length-1);
16607         }else{
16608             enc = "s:" + v;
16609         }
16610         return escape(enc);        
16611     }
16612 });
16613
16614 /*
16615  * Based on:
16616  * Ext JS Library 1.1.1
16617  * Copyright(c) 2006-2007, Ext JS, LLC.
16618  *
16619  * Originally Released Under LGPL - original licence link has changed is not relivant.
16620  *
16621  * Fork - LGPL
16622  * <script type="text/javascript">
16623  */
16624 /**
16625  * @class Roo.state.Manager
16626  * This is the global state manager. By default all components that are "state aware" check this class
16627  * for state information if you don't pass them a custom state provider. In order for this class
16628  * to be useful, it must be initialized with a provider when your application initializes.
16629  <pre><code>
16630 // in your initialization function
16631 init : function(){
16632    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16633    ...
16634    // supposed you have a {@link Roo.BorderLayout}
16635    var layout = new Roo.BorderLayout(...);
16636    layout.restoreState();
16637    // or a {Roo.BasicDialog}
16638    var dialog = new Roo.BasicDialog(...);
16639    dialog.restoreState();
16640  </code></pre>
16641  * @static
16642  */
16643 Roo.state.Manager = function(){
16644     var provider = new Roo.state.Provider();
16645     
16646     return {
16647         /**
16648          * Configures the default state provider for your application
16649          * @param {Provider} stateProvider The state provider to set
16650          */
16651         setProvider : function(stateProvider){
16652             provider = stateProvider;
16653         },
16654         
16655         /**
16656          * Returns the current value for a key
16657          * @param {String} name The key name
16658          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16659          * @return {Mixed} The state data
16660          */
16661         get : function(key, defaultValue){
16662             return provider.get(key, defaultValue);
16663         },
16664         
16665         /**
16666          * Sets the value for a key
16667          * @param {String} name The key name
16668          * @param {Mixed} value The state data
16669          */
16670          set : function(key, value){
16671             provider.set(key, value);
16672         },
16673         
16674         /**
16675          * Clears a value from the state
16676          * @param {String} name The key name
16677          */
16678         clear : function(key){
16679             provider.clear(key);
16680         },
16681         
16682         /**
16683          * Gets the currently configured state provider
16684          * @return {Provider} The state provider
16685          */
16686         getProvider : function(){
16687             return provider;
16688         }
16689     };
16690 }();
16691 /*
16692  * Based on:
16693  * Ext JS Library 1.1.1
16694  * Copyright(c) 2006-2007, Ext JS, LLC.
16695  *
16696  * Originally Released Under LGPL - original licence link has changed is not relivant.
16697  *
16698  * Fork - LGPL
16699  * <script type="text/javascript">
16700  */
16701 /**
16702  * @class Roo.state.CookieProvider
16703  * @extends Roo.state.Provider
16704  * The default Provider implementation which saves state via cookies.
16705  * <br />Usage:
16706  <pre><code>
16707    var cp = new Roo.state.CookieProvider({
16708        path: "/cgi-bin/",
16709        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16710        domain: "roojs.com"
16711    })
16712    Roo.state.Manager.setProvider(cp);
16713  </code></pre>
16714  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16715  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16716  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16717  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16718  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16719  * domain the page is running on including the 'www' like 'www.roojs.com')
16720  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16721  * @constructor
16722  * Create a new CookieProvider
16723  * @param {Object} config The configuration object
16724  */
16725 Roo.state.CookieProvider = function(config){
16726     Roo.state.CookieProvider.superclass.constructor.call(this);
16727     this.path = "/";
16728     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16729     this.domain = null;
16730     this.secure = false;
16731     Roo.apply(this, config);
16732     this.state = this.readCookies();
16733 };
16734
16735 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16736     // private
16737     set : function(name, value){
16738         if(typeof value == "undefined" || value === null){
16739             this.clear(name);
16740             return;
16741         }
16742         this.setCookie(name, value);
16743         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16744     },
16745
16746     // private
16747     clear : function(name){
16748         this.clearCookie(name);
16749         Roo.state.CookieProvider.superclass.clear.call(this, name);
16750     },
16751
16752     // private
16753     readCookies : function(){
16754         var cookies = {};
16755         var c = document.cookie + ";";
16756         var re = /\s?(.*?)=(.*?);/g;
16757         var matches;
16758         while((matches = re.exec(c)) != null){
16759             var name = matches[1];
16760             var value = matches[2];
16761             if(name && name.substring(0,3) == "ys-"){
16762                 cookies[name.substr(3)] = this.decodeValue(value);
16763             }
16764         }
16765         return cookies;
16766     },
16767
16768     // private
16769     setCookie : function(name, value){
16770         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16771            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16772            ((this.path == null) ? "" : ("; path=" + this.path)) +
16773            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16774            ((this.secure == true) ? "; secure" : "");
16775     },
16776
16777     // private
16778     clearCookie : function(name){
16779         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16780            ((this.path == null) ? "" : ("; path=" + this.path)) +
16781            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16782            ((this.secure == true) ? "; secure" : "");
16783     }
16784 });/*
16785  * Based on:
16786  * Ext JS Library 1.1.1
16787  * Copyright(c) 2006-2007, Ext JS, LLC.
16788  *
16789  * Originally Released Under LGPL - original licence link has changed is not relivant.
16790  *
16791  * Fork - LGPL
16792  * <script type="text/javascript">
16793  */
16794  
16795
16796 /**
16797  * @class Roo.ComponentMgr
16798  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16799  * @static
16800  */
16801 Roo.ComponentMgr = function(){
16802     var all = new Roo.util.MixedCollection();
16803
16804     return {
16805         /**
16806          * Registers a component.
16807          * @param {Roo.Component} c The component
16808          */
16809         register : function(c){
16810             all.add(c);
16811         },
16812
16813         /**
16814          * Unregisters a component.
16815          * @param {Roo.Component} c The component
16816          */
16817         unregister : function(c){
16818             all.remove(c);
16819         },
16820
16821         /**
16822          * Returns a component by id
16823          * @param {String} id The component id
16824          */
16825         get : function(id){
16826             return all.get(id);
16827         },
16828
16829         /**
16830          * Registers a function that will be called when a specified component is added to ComponentMgr
16831          * @param {String} id The component id
16832          * @param {Funtction} fn The callback function
16833          * @param {Object} scope The scope of the callback
16834          */
16835         onAvailable : function(id, fn, scope){
16836             all.on("add", function(index, o){
16837                 if(o.id == id){
16838                     fn.call(scope || o, o);
16839                     all.un("add", fn, scope);
16840                 }
16841             });
16842         }
16843     };
16844 }();/*
16845  * Based on:
16846  * Ext JS Library 1.1.1
16847  * Copyright(c) 2006-2007, Ext JS, LLC.
16848  *
16849  * Originally Released Under LGPL - original licence link has changed is not relivant.
16850  *
16851  * Fork - LGPL
16852  * <script type="text/javascript">
16853  */
16854  
16855 /**
16856  * @class Roo.Component
16857  * @extends Roo.util.Observable
16858  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16859  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16860  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16861  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16862  * All visual components (widgets) that require rendering into a layout should subclass Component.
16863  * @constructor
16864  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16865  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
16866  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16867  */
16868 Roo.Component = function(config){
16869     console.log("COMPONENT CONSTRUCTOR");
16870     config = config || {};
16871     if(config.tagName || config.dom || typeof config == "string"){ // element object
16872         config = {el: config, id: config.id || config};
16873     }
16874     this.initialConfig = config;
16875
16876     Roo.apply(this, config);
16877     this.addEvents({
16878         /**
16879          * @event disable
16880          * Fires after the component is disabled.
16881              * @param {Roo.Component} this
16882              */
16883         disable : true,
16884         /**
16885          * @event enable
16886          * Fires after the component is enabled.
16887              * @param {Roo.Component} this
16888              */
16889         enable : true,
16890         /**
16891          * @event beforeshow
16892          * Fires before the component is shown.  Return false to stop the show.
16893              * @param {Roo.Component} this
16894              */
16895         beforeshow : true,
16896         /**
16897          * @event show
16898          * Fires after the component is shown.
16899              * @param {Roo.Component} this
16900              */
16901         show : true,
16902         /**
16903          * @event beforehide
16904          * Fires before the component is hidden. Return false to stop the hide.
16905              * @param {Roo.Component} this
16906              */
16907         beforehide : true,
16908         /**
16909          * @event hide
16910          * Fires after the component is hidden.
16911              * @param {Roo.Component} this
16912              */
16913         hide : true,
16914         /**
16915          * @event beforerender
16916          * Fires before the component is rendered. Return false to stop the render.
16917              * @param {Roo.Component} this
16918              */
16919         beforerender : true,
16920         /**
16921          * @event render
16922          * Fires after the component is rendered.
16923              * @param {Roo.Component} this
16924              */
16925         render : true,
16926         /**
16927          * @event beforedestroy
16928          * Fires before the component is destroyed. Return false to stop the destroy.
16929              * @param {Roo.Component} this
16930              */
16931         beforedestroy : true,
16932         /**
16933          * @event destroy
16934          * Fires after the component is destroyed.
16935              * @param {Roo.Component} this
16936              */
16937         destroy : true
16938     });
16939     if(!this.id){
16940         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16941     }
16942     Roo.ComponentMgr.register(this);
16943     Roo.Component.superclass.constructor.call(this);
16944     this.initComponent();
16945     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16946         this.render(this.renderTo);
16947         delete this.renderTo;
16948     }
16949 };
16950
16951 /** @private */
16952 Roo.Component.AUTO_ID = 1000;
16953
16954 Roo.extend(Roo.Component, Roo.util.Observable, {
16955     /**
16956      * @scope Roo.Component.prototype
16957      * @type {Boolean}
16958      * true if this component is hidden. Read-only.
16959      */
16960     hidden : false,
16961     /**
16962      * @type {Boolean}
16963      * true if this component is disabled. Read-only.
16964      */
16965     disabled : false,
16966     /**
16967      * @type {Boolean}
16968      * true if this component has been rendered. Read-only.
16969      */
16970     rendered : false,
16971     
16972     /** @cfg {String} disableClass
16973      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16974      */
16975     disabledClass : "x-item-disabled",
16976         /** @cfg {Boolean} allowDomMove
16977          * Whether the component can move the Dom node when rendering (defaults to true).
16978          */
16979     allowDomMove : true,
16980     /** @cfg {String} hideMode (display|visibility)
16981      * How this component should hidden. Supported values are
16982      * "visibility" (css visibility), "offsets" (negative offset position) and
16983      * "display" (css display) - defaults to "display".
16984      */
16985     hideMode: 'display',
16986
16987     /** @private */
16988     ctype : "Roo.Component",
16989
16990     /**
16991      * @cfg {String} actionMode 
16992      * which property holds the element that used for  hide() / show() / disable() / enable()
16993      * default is 'el' for forms you probably want to set this to fieldEl 
16994      */
16995     actionMode : "el",
16996
16997     /** @private */
16998     getActionEl : function(){
16999         return this[this.actionMode];
17000     },
17001
17002     initComponent : Roo.emptyFn,
17003     /**
17004      * If this is a lazy rendering component, render it to its container element.
17005      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
17006      */
17007     render : function(container, position){
17008         
17009         if(this.rendered){
17010             return this;
17011         }
17012         
17013         if(this.fireEvent("beforerender", this) === false){
17014             return false;
17015         }
17016         
17017         if(!container && this.el){
17018             this.el = Roo.get(this.el);
17019             container = this.el.dom.parentNode;
17020             this.allowDomMove = false;
17021         }
17022         this.container = Roo.get(container);
17023         this.rendered = true;
17024         if(position !== undefined){
17025             if(typeof position == 'number'){
17026                 position = this.container.dom.childNodes[position];
17027             }else{
17028                 position = Roo.getDom(position);
17029             }
17030         }
17031         this.onRender(this.container, position || null);
17032         if(this.cls){
17033             this.el.addClass(this.cls);
17034             delete this.cls;
17035         }
17036         if(this.style){
17037             this.el.applyStyles(this.style);
17038             delete this.style;
17039         }
17040         this.fireEvent("render", this);
17041         this.afterRender(this.container);
17042         if(this.hidden){
17043             this.hide();
17044         }
17045         if(this.disabled){
17046             this.disable();
17047         }
17048
17049         return this;
17050         
17051     },
17052
17053     /** @private */
17054     // default function is not really useful
17055     onRender : function(ct, position){
17056         if(this.el){
17057             this.el = Roo.get(this.el);
17058             if(this.allowDomMove !== false){
17059                 ct.dom.insertBefore(this.el.dom, position);
17060             }
17061         }
17062     },
17063
17064     /** @private */
17065     getAutoCreate : function(){
17066         var cfg = typeof this.autoCreate == "object" ?
17067                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17068         if(this.id && !cfg.id){
17069             cfg.id = this.id;
17070         }
17071         return cfg;
17072     },
17073
17074     /** @private */
17075     afterRender : Roo.emptyFn,
17076
17077     /**
17078      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17079      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17080      */
17081     destroy : function(){
17082         if(this.fireEvent("beforedestroy", this) !== false){
17083             this.purgeListeners();
17084             this.beforeDestroy();
17085             if(this.rendered){
17086                 this.el.removeAllListeners();
17087                 this.el.remove();
17088                 if(this.actionMode == "container"){
17089                     this.container.remove();
17090                 }
17091             }
17092             this.onDestroy();
17093             Roo.ComponentMgr.unregister(this);
17094             this.fireEvent("destroy", this);
17095         }
17096     },
17097
17098         /** @private */
17099     beforeDestroy : function(){
17100
17101     },
17102
17103         /** @private */
17104         onDestroy : function(){
17105
17106     },
17107
17108     /**
17109      * Returns the underlying {@link Roo.Element}.
17110      * @return {Roo.Element} The element
17111      */
17112     getEl : function(){
17113         return this.el;
17114     },
17115
17116     /**
17117      * Returns the id of this component.
17118      * @return {String}
17119      */
17120     getId : function(){
17121         return this.id;
17122     },
17123
17124     /**
17125      * Try to focus this component.
17126      * @param {Boolean} selectText True to also select the text in this component (if applicable)
17127      * @return {Roo.Component} this
17128      */
17129     focus : function(selectText){
17130         if(this.rendered){
17131             this.el.focus();
17132             if(selectText === true){
17133                 this.el.dom.select();
17134             }
17135         }
17136         return this;
17137     },
17138
17139     /** @private */
17140     blur : function(){
17141         if(this.rendered){
17142             this.el.blur();
17143         }
17144         return this;
17145     },
17146
17147     /**
17148      * Disable this component.
17149      * @return {Roo.Component} this
17150      */
17151     disable : function(){
17152         if(this.rendered){
17153             this.onDisable();
17154         }
17155         this.disabled = true;
17156         this.fireEvent("disable", this);
17157         return this;
17158     },
17159
17160         // private
17161     onDisable : function(){
17162         this.getActionEl().addClass(this.disabledClass);
17163         this.el.dom.disabled = true;
17164     },
17165
17166     /**
17167      * Enable this component.
17168      * @return {Roo.Component} this
17169      */
17170     enable : function(){
17171         if(this.rendered){
17172             this.onEnable();
17173         }
17174         this.disabled = false;
17175         this.fireEvent("enable", this);
17176         return this;
17177     },
17178
17179         // private
17180     onEnable : function(){
17181         this.getActionEl().removeClass(this.disabledClass);
17182         this.el.dom.disabled = false;
17183     },
17184
17185     /**
17186      * Convenience function for setting disabled/enabled by boolean.
17187      * @param {Boolean} disabled
17188      */
17189     setDisabled : function(disabled){
17190         this[disabled ? "disable" : "enable"]();
17191     },
17192
17193     /**
17194      * Show this component.
17195      * @return {Roo.Component} this
17196      */
17197     show: function(){
17198         if(this.fireEvent("beforeshow", this) !== false){
17199             this.hidden = false;
17200             if(this.rendered){
17201                 this.onShow();
17202             }
17203             this.fireEvent("show", this);
17204         }
17205         return this;
17206     },
17207
17208     // private
17209     onShow : function(){
17210         var ae = this.getActionEl();
17211         if(this.hideMode == 'visibility'){
17212             ae.dom.style.visibility = "visible";
17213         }else if(this.hideMode == 'offsets'){
17214             ae.removeClass('x-hidden');
17215         }else{
17216             ae.dom.style.display = "";
17217         }
17218     },
17219
17220     /**
17221      * Hide this component.
17222      * @return {Roo.Component} this
17223      */
17224     hide: function(){
17225         if(this.fireEvent("beforehide", this) !== false){
17226             this.hidden = true;
17227             if(this.rendered){
17228                 this.onHide();
17229             }
17230             this.fireEvent("hide", this);
17231         }
17232         return this;
17233     },
17234
17235     // private
17236     onHide : function(){
17237         var ae = this.getActionEl();
17238         if(this.hideMode == 'visibility'){
17239             ae.dom.style.visibility = "hidden";
17240         }else if(this.hideMode == 'offsets'){
17241             ae.addClass('x-hidden');
17242         }else{
17243             ae.dom.style.display = "none";
17244         }
17245     },
17246
17247     /**
17248      * Convenience function to hide or show this component by boolean.
17249      * @param {Boolean} visible True to show, false to hide
17250      * @return {Roo.Component} this
17251      */
17252     setVisible: function(visible){
17253         if(visible) {
17254             this.show();
17255         }else{
17256             this.hide();
17257         }
17258         return this;
17259     },
17260
17261     /**
17262      * Returns true if this component is visible.
17263      */
17264     isVisible : function(){
17265         return this.getActionEl().isVisible();
17266     },
17267
17268     cloneConfig : function(overrides){
17269         overrides = overrides || {};
17270         var id = overrides.id || Roo.id();
17271         var cfg = Roo.applyIf(overrides, this.initialConfig);
17272         cfg.id = id; // prevent dup id
17273         return new this.constructor(cfg);
17274     }
17275 });/*
17276  * Based on:
17277  * Ext JS Library 1.1.1
17278  * Copyright(c) 2006-2007, Ext JS, LLC.
17279  *
17280  * Originally Released Under LGPL - original licence link has changed is not relivant.
17281  *
17282  * Fork - LGPL
17283  * <script type="text/javascript">
17284  */
17285
17286 /**
17287  * @class Roo.BoxComponent
17288  * @extends Roo.Component
17289  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17290  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17291  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17292  * layout containers.
17293  * @constructor
17294  * @param {Roo.Element/String/Object} config The configuration options.
17295  */
17296 Roo.BoxComponent = function(config){
17297     Roo.Component.call(this, config);
17298     this.addEvents({
17299         /**
17300          * @event resize
17301          * Fires after the component is resized.
17302              * @param {Roo.Component} this
17303              * @param {Number} adjWidth The box-adjusted width that was set
17304              * @param {Number} adjHeight The box-adjusted height that was set
17305              * @param {Number} rawWidth The width that was originally specified
17306              * @param {Number} rawHeight The height that was originally specified
17307              */
17308         resize : true,
17309         /**
17310          * @event move
17311          * Fires after the component is moved.
17312              * @param {Roo.Component} this
17313              * @param {Number} x The new x position
17314              * @param {Number} y The new y position
17315              */
17316         move : true
17317     });
17318 };
17319
17320 Roo.extend(Roo.BoxComponent, Roo.Component, {
17321     // private, set in afterRender to signify that the component has been rendered
17322     boxReady : false,
17323     // private, used to defer height settings to subclasses
17324     deferHeight: false,
17325     /** @cfg {Number} width
17326      * width (optional) size of component
17327      */
17328      /** @cfg {Number} height
17329      * height (optional) size of component
17330      */
17331      
17332     /**
17333      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17334      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17335      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17336      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17337      * @return {Roo.BoxComponent} this
17338      */
17339     setSize : function(w, h){
17340         // support for standard size objects
17341         if(typeof w == 'object'){
17342             h = w.height;
17343             w = w.width;
17344         }
17345         // not rendered
17346         if(!this.boxReady){
17347             this.width = w;
17348             this.height = h;
17349             return this;
17350         }
17351
17352         // prevent recalcs when not needed
17353         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17354             return this;
17355         }
17356         this.lastSize = {width: w, height: h};
17357
17358         var adj = this.adjustSize(w, h);
17359         var aw = adj.width, ah = adj.height;
17360         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17361             var rz = this.getResizeEl();
17362             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17363                 rz.setSize(aw, ah);
17364             }else if(!this.deferHeight && ah !== undefined){
17365                 rz.setHeight(ah);
17366             }else if(aw !== undefined){
17367                 rz.setWidth(aw);
17368             }
17369             this.onResize(aw, ah, w, h);
17370             this.fireEvent('resize', this, aw, ah, w, h);
17371         }
17372         return this;
17373     },
17374
17375     /**
17376      * Gets the current size of the component's underlying element.
17377      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17378      */
17379     getSize : function(){
17380         return this.el.getSize();
17381     },
17382
17383     /**
17384      * Gets the current XY position of the component's underlying element.
17385      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17386      * @return {Array} The XY position of the element (e.g., [100, 200])
17387      */
17388     getPosition : function(local){
17389         if(local === true){
17390             return [this.el.getLeft(true), this.el.getTop(true)];
17391         }
17392         return this.xy || this.el.getXY();
17393     },
17394
17395     /**
17396      * Gets the current box measurements of the component's underlying element.
17397      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17398      * @returns {Object} box An object in the format {x, y, width, height}
17399      */
17400     getBox : function(local){
17401         var s = this.el.getSize();
17402         if(local){
17403             s.x = this.el.getLeft(true);
17404             s.y = this.el.getTop(true);
17405         }else{
17406             var xy = this.xy || this.el.getXY();
17407             s.x = xy[0];
17408             s.y = xy[1];
17409         }
17410         return s;
17411     },
17412
17413     /**
17414      * Sets the current box measurements of the component's underlying element.
17415      * @param {Object} box An object in the format {x, y, width, height}
17416      * @returns {Roo.BoxComponent} this
17417      */
17418     updateBox : function(box){
17419         this.setSize(box.width, box.height);
17420         this.setPagePosition(box.x, box.y);
17421         return this;
17422     },
17423
17424     // protected
17425     getResizeEl : function(){
17426         return this.resizeEl || this.el;
17427     },
17428
17429     // protected
17430     getPositionEl : function(){
17431         return this.positionEl || this.el;
17432     },
17433
17434     /**
17435      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17436      * This method fires the move event.
17437      * @param {Number} left The new left
17438      * @param {Number} top The new top
17439      * @returns {Roo.BoxComponent} this
17440      */
17441     setPosition : function(x, y){
17442         this.x = x;
17443         this.y = y;
17444         if(!this.boxReady){
17445             return this;
17446         }
17447         var adj = this.adjustPosition(x, y);
17448         var ax = adj.x, ay = adj.y;
17449
17450         var el = this.getPositionEl();
17451         if(ax !== undefined || ay !== undefined){
17452             if(ax !== undefined && ay !== undefined){
17453                 el.setLeftTop(ax, ay);
17454             }else if(ax !== undefined){
17455                 el.setLeft(ax);
17456             }else if(ay !== undefined){
17457                 el.setTop(ay);
17458             }
17459             this.onPosition(ax, ay);
17460             this.fireEvent('move', this, ax, ay);
17461         }
17462         return this;
17463     },
17464
17465     /**
17466      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17467      * This method fires the move event.
17468      * @param {Number} x The new x position
17469      * @param {Number} y The new y position
17470      * @returns {Roo.BoxComponent} this
17471      */
17472     setPagePosition : function(x, y){
17473         this.pageX = x;
17474         this.pageY = y;
17475         if(!this.boxReady){
17476             return;
17477         }
17478         if(x === undefined || y === undefined){ // cannot translate undefined points
17479             return;
17480         }
17481         var p = this.el.translatePoints(x, y);
17482         this.setPosition(p.left, p.top);
17483         return this;
17484     },
17485
17486     // private
17487     onRender : function(ct, position){
17488         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17489         if(this.resizeEl){
17490             this.resizeEl = Roo.get(this.resizeEl);
17491         }
17492         if(this.positionEl){
17493             this.positionEl = Roo.get(this.positionEl);
17494         }
17495     },
17496
17497     // private
17498     afterRender : function(){
17499         Roo.BoxComponent.superclass.afterRender.call(this);
17500         this.boxReady = true;
17501         this.setSize(this.width, this.height);
17502         if(this.x || this.y){
17503             this.setPosition(this.x, this.y);
17504         }
17505         if(this.pageX || this.pageY){
17506             this.setPagePosition(this.pageX, this.pageY);
17507         }
17508     },
17509
17510     /**
17511      * Force the component's size to recalculate based on the underlying element's current height and width.
17512      * @returns {Roo.BoxComponent} this
17513      */
17514     syncSize : function(){
17515         delete this.lastSize;
17516         this.setSize(this.el.getWidth(), this.el.getHeight());
17517         return this;
17518     },
17519
17520     /**
17521      * Called after the component is resized, this method is empty by default but can be implemented by any
17522      * subclass that needs to perform custom logic after a resize occurs.
17523      * @param {Number} adjWidth The box-adjusted width that was set
17524      * @param {Number} adjHeight The box-adjusted height that was set
17525      * @param {Number} rawWidth The width that was originally specified
17526      * @param {Number} rawHeight The height that was originally specified
17527      */
17528     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17529
17530     },
17531
17532     /**
17533      * Called after the component is moved, this method is empty by default but can be implemented by any
17534      * subclass that needs to perform custom logic after a move occurs.
17535      * @param {Number} x The new x position
17536      * @param {Number} y The new y position
17537      */
17538     onPosition : function(x, y){
17539
17540     },
17541
17542     // private
17543     adjustSize : function(w, h){
17544         if(this.autoWidth){
17545             w = 'auto';
17546         }
17547         if(this.autoHeight){
17548             h = 'auto';
17549         }
17550         return {width : w, height: h};
17551     },
17552
17553     // private
17554     adjustPosition : function(x, y){
17555         return {x : x, y: y};
17556     }
17557 });/*
17558  * Based on:
17559  * Ext JS Library 1.1.1
17560  * Copyright(c) 2006-2007, Ext JS, LLC.
17561  *
17562  * Originally Released Under LGPL - original licence link has changed is not relivant.
17563  *
17564  * Fork - LGPL
17565  * <script type="text/javascript">
17566  */
17567  (function(){ 
17568 /**
17569  * @class Roo.Layer
17570  * @extends Roo.Element
17571  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17572  * automatic maintaining of shadow/shim positions.
17573  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17574  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17575  * you can pass a string with a CSS class name. False turns off the shadow.
17576  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17577  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17578  * @cfg {String} cls CSS class to add to the element
17579  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17580  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17581  * @constructor
17582  * @param {Object} config An object with config options.
17583  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17584  */
17585
17586 Roo.Layer = function(config, existingEl){
17587     config = config || {};
17588     var dh = Roo.DomHelper;
17589     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17590     if(existingEl){
17591         this.dom = Roo.getDom(existingEl);
17592     }
17593     if(!this.dom){
17594         var o = config.dh || {tag: "div", cls: "x-layer"};
17595         this.dom = dh.append(pel, o);
17596     }
17597     if(config.cls){
17598         this.addClass(config.cls);
17599     }
17600     this.constrain = config.constrain !== false;
17601     this.visibilityMode = Roo.Element.VISIBILITY;
17602     if(config.id){
17603         this.id = this.dom.id = config.id;
17604     }else{
17605         this.id = Roo.id(this.dom);
17606     }
17607     this.zindex = config.zindex || this.getZIndex();
17608     this.position("absolute", this.zindex);
17609     if(config.shadow){
17610         this.shadowOffset = config.shadowOffset || 4;
17611         this.shadow = new Roo.Shadow({
17612             offset : this.shadowOffset,
17613             mode : config.shadow
17614         });
17615     }else{
17616         this.shadowOffset = 0;
17617     }
17618     this.useShim = config.shim !== false && Roo.useShims;
17619     this.useDisplay = config.useDisplay;
17620     this.hide();
17621 };
17622
17623 var supr = Roo.Element.prototype;
17624
17625 // shims are shared among layer to keep from having 100 iframes
17626 var shims = [];
17627
17628 Roo.extend(Roo.Layer, Roo.Element, {
17629
17630     getZIndex : function(){
17631         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17632     },
17633
17634     getShim : function(){
17635         if(!this.useShim){
17636             return null;
17637         }
17638         if(this.shim){
17639             return this.shim;
17640         }
17641         var shim = shims.shift();
17642         if(!shim){
17643             shim = this.createShim();
17644             shim.enableDisplayMode('block');
17645             shim.dom.style.display = 'none';
17646             shim.dom.style.visibility = 'visible';
17647         }
17648         var pn = this.dom.parentNode;
17649         if(shim.dom.parentNode != pn){
17650             pn.insertBefore(shim.dom, this.dom);
17651         }
17652         shim.setStyle('z-index', this.getZIndex()-2);
17653         this.shim = shim;
17654         return shim;
17655     },
17656
17657     hideShim : function(){
17658         if(this.shim){
17659             this.shim.setDisplayed(false);
17660             shims.push(this.shim);
17661             delete this.shim;
17662         }
17663     },
17664
17665     disableShadow : function(){
17666         if(this.shadow){
17667             this.shadowDisabled = true;
17668             this.shadow.hide();
17669             this.lastShadowOffset = this.shadowOffset;
17670             this.shadowOffset = 0;
17671         }
17672     },
17673
17674     enableShadow : function(show){
17675         if(this.shadow){
17676             this.shadowDisabled = false;
17677             this.shadowOffset = this.lastShadowOffset;
17678             delete this.lastShadowOffset;
17679             if(show){
17680                 this.sync(true);
17681             }
17682         }
17683     },
17684
17685     // private
17686     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17687     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17688     sync : function(doShow){
17689         var sw = this.shadow;
17690         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17691             var sh = this.getShim();
17692
17693             var w = this.getWidth(),
17694                 h = this.getHeight();
17695
17696             var l = this.getLeft(true),
17697                 t = this.getTop(true);
17698
17699             if(sw && !this.shadowDisabled){
17700                 if(doShow && !sw.isVisible()){
17701                     sw.show(this);
17702                 }else{
17703                     sw.realign(l, t, w, h);
17704                 }
17705                 if(sh){
17706                     if(doShow){
17707                        sh.show();
17708                     }
17709                     // fit the shim behind the shadow, so it is shimmed too
17710                     var a = sw.adjusts, s = sh.dom.style;
17711                     s.left = (Math.min(l, l+a.l))+"px";
17712                     s.top = (Math.min(t, t+a.t))+"px";
17713                     s.width = (w+a.w)+"px";
17714                     s.height = (h+a.h)+"px";
17715                 }
17716             }else if(sh){
17717                 if(doShow){
17718                    sh.show();
17719                 }
17720                 sh.setSize(w, h);
17721                 sh.setLeftTop(l, t);
17722             }
17723             
17724         }
17725     },
17726
17727     // private
17728     destroy : function(){
17729         this.hideShim();
17730         if(this.shadow){
17731             this.shadow.hide();
17732         }
17733         this.removeAllListeners();
17734         var pn = this.dom.parentNode;
17735         if(pn){
17736             pn.removeChild(this.dom);
17737         }
17738         Roo.Element.uncache(this.id);
17739     },
17740
17741     remove : function(){
17742         this.destroy();
17743     },
17744
17745     // private
17746     beginUpdate : function(){
17747         this.updating = true;
17748     },
17749
17750     // private
17751     endUpdate : function(){
17752         this.updating = false;
17753         this.sync(true);
17754     },
17755
17756     // private
17757     hideUnders : function(negOffset){
17758         if(this.shadow){
17759             this.shadow.hide();
17760         }
17761         this.hideShim();
17762     },
17763
17764     // private
17765     constrainXY : function(){
17766         if(this.constrain){
17767             var vw = Roo.lib.Dom.getViewWidth(),
17768                 vh = Roo.lib.Dom.getViewHeight();
17769             var s = Roo.get(document).getScroll();
17770
17771             var xy = this.getXY();
17772             var x = xy[0], y = xy[1];   
17773             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17774             // only move it if it needs it
17775             var moved = false;
17776             // first validate right/bottom
17777             if((x + w) > vw+s.left){
17778                 x = vw - w - this.shadowOffset;
17779                 moved = true;
17780             }
17781             if((y + h) > vh+s.top){
17782                 y = vh - h - this.shadowOffset;
17783                 moved = true;
17784             }
17785             // then make sure top/left isn't negative
17786             if(x < s.left){
17787                 x = s.left;
17788                 moved = true;
17789             }
17790             if(y < s.top){
17791                 y = s.top;
17792                 moved = true;
17793             }
17794             if(moved){
17795                 if(this.avoidY){
17796                     var ay = this.avoidY;
17797                     if(y <= ay && (y+h) >= ay){
17798                         y = ay-h-5;   
17799                     }
17800                 }
17801                 xy = [x, y];
17802                 this.storeXY(xy);
17803                 supr.setXY.call(this, xy);
17804                 this.sync();
17805             }
17806         }
17807     },
17808
17809     isVisible : function(){
17810         return this.visible;    
17811     },
17812
17813     // private
17814     showAction : function(){
17815         this.visible = true; // track visibility to prevent getStyle calls
17816         if(this.useDisplay === true){
17817             this.setDisplayed("");
17818         }else if(this.lastXY){
17819             supr.setXY.call(this, this.lastXY);
17820         }else if(this.lastLT){
17821             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17822         }
17823     },
17824
17825     // private
17826     hideAction : function(){
17827         this.visible = false;
17828         if(this.useDisplay === true){
17829             this.setDisplayed(false);
17830         }else{
17831             this.setLeftTop(-10000,-10000);
17832         }
17833     },
17834
17835     // overridden Element method
17836     setVisible : function(v, a, d, c, e){
17837         if(v){
17838             this.showAction();
17839         }
17840         if(a && v){
17841             var cb = function(){
17842                 this.sync(true);
17843                 if(c){
17844                     c();
17845                 }
17846             }.createDelegate(this);
17847             supr.setVisible.call(this, true, true, d, cb, e);
17848         }else{
17849             if(!v){
17850                 this.hideUnders(true);
17851             }
17852             var cb = c;
17853             if(a){
17854                 cb = function(){
17855                     this.hideAction();
17856                     if(c){
17857                         c();
17858                     }
17859                 }.createDelegate(this);
17860             }
17861             supr.setVisible.call(this, v, a, d, cb, e);
17862             if(v){
17863                 this.sync(true);
17864             }else if(!a){
17865                 this.hideAction();
17866             }
17867         }
17868     },
17869
17870     storeXY : function(xy){
17871         delete this.lastLT;
17872         this.lastXY = xy;
17873     },
17874
17875     storeLeftTop : function(left, top){
17876         delete this.lastXY;
17877         this.lastLT = [left, top];
17878     },
17879
17880     // private
17881     beforeFx : function(){
17882         this.beforeAction();
17883         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17884     },
17885
17886     // private
17887     afterFx : function(){
17888         Roo.Layer.superclass.afterFx.apply(this, arguments);
17889         this.sync(this.isVisible());
17890     },
17891
17892     // private
17893     beforeAction : function(){
17894         if(!this.updating && this.shadow){
17895             this.shadow.hide();
17896         }
17897     },
17898
17899     // overridden Element method
17900     setLeft : function(left){
17901         this.storeLeftTop(left, this.getTop(true));
17902         supr.setLeft.apply(this, arguments);
17903         this.sync();
17904     },
17905
17906     setTop : function(top){
17907         this.storeLeftTop(this.getLeft(true), top);
17908         supr.setTop.apply(this, arguments);
17909         this.sync();
17910     },
17911
17912     setLeftTop : function(left, top){
17913         this.storeLeftTop(left, top);
17914         supr.setLeftTop.apply(this, arguments);
17915         this.sync();
17916     },
17917
17918     setXY : function(xy, a, d, c, e){
17919         this.fixDisplay();
17920         this.beforeAction();
17921         this.storeXY(xy);
17922         var cb = this.createCB(c);
17923         supr.setXY.call(this, xy, a, d, cb, e);
17924         if(!a){
17925             cb();
17926         }
17927     },
17928
17929     // private
17930     createCB : function(c){
17931         var el = this;
17932         return function(){
17933             el.constrainXY();
17934             el.sync(true);
17935             if(c){
17936                 c();
17937             }
17938         };
17939     },
17940
17941     // overridden Element method
17942     setX : function(x, a, d, c, e){
17943         this.setXY([x, this.getY()], a, d, c, e);
17944     },
17945
17946     // overridden Element method
17947     setY : function(y, a, d, c, e){
17948         this.setXY([this.getX(), y], a, d, c, e);
17949     },
17950
17951     // overridden Element method
17952     setSize : function(w, h, a, d, c, e){
17953         this.beforeAction();
17954         var cb = this.createCB(c);
17955         supr.setSize.call(this, w, h, a, d, cb, e);
17956         if(!a){
17957             cb();
17958         }
17959     },
17960
17961     // overridden Element method
17962     setWidth : function(w, a, d, c, e){
17963         this.beforeAction();
17964         var cb = this.createCB(c);
17965         supr.setWidth.call(this, w, a, d, cb, e);
17966         if(!a){
17967             cb();
17968         }
17969     },
17970
17971     // overridden Element method
17972     setHeight : function(h, a, d, c, e){
17973         this.beforeAction();
17974         var cb = this.createCB(c);
17975         supr.setHeight.call(this, h, a, d, cb, e);
17976         if(!a){
17977             cb();
17978         }
17979     },
17980
17981     // overridden Element method
17982     setBounds : function(x, y, w, h, a, d, c, e){
17983         this.beforeAction();
17984         var cb = this.createCB(c);
17985         if(!a){
17986             this.storeXY([x, y]);
17987             supr.setXY.call(this, [x, y]);
17988             supr.setSize.call(this, w, h, a, d, cb, e);
17989             cb();
17990         }else{
17991             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17992         }
17993         return this;
17994     },
17995     
17996     /**
17997      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17998      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17999      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
18000      * @param {Number} zindex The new z-index to set
18001      * @return {this} The Layer
18002      */
18003     setZIndex : function(zindex){
18004         this.zindex = zindex;
18005         this.setStyle("z-index", zindex + 2);
18006         if(this.shadow){
18007             this.shadow.setZIndex(zindex + 1);
18008         }
18009         if(this.shim){
18010             this.shim.setStyle("z-index", zindex);
18011         }
18012     }
18013 });
18014 })();/*
18015  * Original code for Roojs - LGPL
18016  * <script type="text/javascript">
18017  */
18018  
18019 /**
18020  * @class Roo.XComponent
18021  * A delayed Element creator...
18022  * Or a way to group chunks of interface together.
18023  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
18024  *  used in conjunction with XComponent.build() it will create an instance of each element,
18025  *  then call addxtype() to build the User interface.
18026  * 
18027  * Mypart.xyx = new Roo.XComponent({
18028
18029     parent : 'Mypart.xyz', // empty == document.element.!!
18030     order : '001',
18031     name : 'xxxx'
18032     region : 'xxxx'
18033     disabled : function() {} 
18034      
18035     tree : function() { // return an tree of xtype declared components
18036         var MODULE = this;
18037         return 
18038         {
18039             xtype : 'NestedLayoutPanel',
18040             // technicall
18041         }
18042      ]
18043  *})
18044  *
18045  *
18046  * It can be used to build a big heiracy, with parent etc.
18047  * or you can just use this to render a single compoent to a dom element
18048  * MYPART.render(Roo.Element | String(id) | dom_element )
18049  *
18050  *
18051  * Usage patterns.
18052  *
18053  * Classic Roo
18054  *
18055  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18056  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18057  *
18058  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18059  *
18060  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
18061  * - if mulitple topModules exist, the last one is defined as the top module.
18062  *
18063  * Embeded Roo
18064  * 
18065  * When the top level or multiple modules are to embedded into a existing HTML page,
18066  * the parent element can container '#id' of the element where the module will be drawn.
18067  *
18068  * Bootstrap Roo
18069  *
18070  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18071  * it relies more on a include mechanism, where sub modules are included into an outer page.
18072  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18073  * 
18074  * Bootstrap Roo Included elements
18075  *
18076  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18077  * hence confusing the component builder as it thinks there are multiple top level elements. 
18078  *
18079  * String Over-ride & Translations
18080  *
18081  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18082  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18083  * are needed. @see Roo.XComponent.overlayString  
18084  * 
18085  * 
18086  * 
18087  * @extends Roo.util.Observable
18088  * @constructor
18089  * @param cfg {Object} configuration of component
18090  * 
18091  */
18092 Roo.XComponent = function(cfg) {
18093     Roo.apply(this, cfg);
18094     this.addEvents({ 
18095         /**
18096              * @event built
18097              * Fires when this the componnt is built
18098              * @param {Roo.XComponent} c the component
18099              */
18100         'built' : true
18101         
18102     });
18103     this.region = this.region || 'center'; // default..
18104     Roo.XComponent.register(this);
18105     this.modules = false;
18106     this.el = false; // where the layout goes..
18107     
18108     
18109 }
18110 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18111     /**
18112      * @property el
18113      * The created element (with Roo.factory())
18114      * @type {Roo.Layout}
18115      */
18116     el  : false,
18117     
18118     /**
18119      * @property el
18120      * for BC  - use el in new code
18121      * @type {Roo.Layout}
18122      */
18123     panel : false,
18124     
18125     /**
18126      * @property layout
18127      * for BC  - use el in new code
18128      * @type {Roo.Layout}
18129      */
18130     layout : false,
18131     
18132      /**
18133      * @cfg {Function|boolean} disabled
18134      * If this module is disabled by some rule, return true from the funtion
18135      */
18136     disabled : false,
18137     
18138     /**
18139      * @cfg {String} parent 
18140      * Name of parent element which it get xtype added to..
18141      */
18142     parent: false,
18143     
18144     /**
18145      * @cfg {String} order
18146      * Used to set the order in which elements are created (usefull for multiple tabs)
18147      */
18148     
18149     order : false,
18150     /**
18151      * @cfg {String} name
18152      * String to display while loading.
18153      */
18154     name : false,
18155     /**
18156      * @cfg {String} region
18157      * Region to render component to (defaults to center)
18158      */
18159     region : 'center',
18160     
18161     /**
18162      * @cfg {Array} items
18163      * A single item array - the first element is the root of the tree..
18164      * It's done this way to stay compatible with the Xtype system...
18165      */
18166     items : false,
18167     
18168     /**
18169      * @property _tree
18170      * The method that retuns the tree of parts that make up this compoennt 
18171      * @type {function}
18172      */
18173     _tree  : false,
18174     
18175      /**
18176      * render
18177      * render element to dom or tree
18178      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18179      */
18180     
18181     render : function(el)
18182     {
18183         
18184         el = el || false;
18185         var hp = this.parent ? 1 : 0;
18186         Roo.debug &&  Roo.log(this);
18187         
18188         var tree = this._tree ? this._tree() : this.tree();
18189
18190         
18191         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18192             // if parent is a '#.....' string, then let's use that..
18193             var ename = this.parent.substr(1);
18194             this.parent = false;
18195             Roo.debug && Roo.log(ename);
18196             switch (ename) {
18197                 case 'bootstrap-body':
18198                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
18199                         // this is the BorderLayout standard?
18200                        this.parent = { el : true };
18201                        break;
18202                     }
18203                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18204                         // need to insert stuff...
18205                         this.parent =  {
18206                              el : new Roo.bootstrap.layout.Border({
18207                                  el : document.body, 
18208                      
18209                                  center: {
18210                                     titlebar: false,
18211                                     autoScroll:false,
18212                                     closeOnTab: true,
18213                                     tabPosition: 'top',
18214                                       //resizeTabs: true,
18215                                     alwaysShowTabs: true,
18216                                     hideTabs: false
18217                                      //minTabWidth: 140
18218                                  }
18219                              })
18220                         
18221                          };
18222                          break;
18223                     }
18224                          
18225                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18226                         this.parent = { el :  new  Roo.bootstrap.Body() };
18227                         Roo.debug && Roo.log("setting el to doc body");
18228                          
18229                     } else {
18230                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18231                     }
18232                     break;
18233                 case 'bootstrap':
18234                     this.parent = { el : true};
18235                     // fall through
18236                 default:
18237                     el = Roo.get(ename);
18238                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18239                         this.parent = { el : true};
18240                     }
18241                     
18242                     break;
18243             }
18244                 
18245             
18246             if (!el && !this.parent) {
18247                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18248                 return;
18249             }
18250         }
18251         
18252         Roo.debug && Roo.log("EL:");
18253         Roo.debug && Roo.log(el);
18254         Roo.debug && Roo.log("this.parent.el:");
18255         Roo.debug && Roo.log(this.parent.el);
18256         
18257
18258         // altertive root elements ??? - we need a better way to indicate these.
18259         var is_alt = Roo.XComponent.is_alt ||
18260                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18261                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18262                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18263         
18264         
18265         
18266         if (!this.parent && is_alt) {
18267             //el = Roo.get(document.body);
18268             this.parent = { el : true };
18269         }
18270             
18271             
18272         
18273         if (!this.parent) {
18274             
18275             Roo.debug && Roo.log("no parent - creating one");
18276             
18277             el = el ? Roo.get(el) : false;      
18278             
18279             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18280                 
18281                 this.parent =  {
18282                     el : new Roo.bootstrap.layout.Border({
18283                         el: el || document.body,
18284                     
18285                         center: {
18286                             titlebar: false,
18287                             autoScroll:false,
18288                             closeOnTab: true,
18289                             tabPosition: 'top',
18290                              //resizeTabs: true,
18291                             alwaysShowTabs: false,
18292                             hideTabs: true,
18293                             minTabWidth: 140,
18294                             overflow: 'visible'
18295                          }
18296                      })
18297                 };
18298             } else {
18299             
18300                 // it's a top level one..
18301                 this.parent =  {
18302                     el : new Roo.BorderLayout(el || document.body, {
18303                         center: {
18304                             titlebar: false,
18305                             autoScroll:false,
18306                             closeOnTab: true,
18307                             tabPosition: 'top',
18308                              //resizeTabs: true,
18309                             alwaysShowTabs: el && hp? false :  true,
18310                             hideTabs: el || !hp ? true :  false,
18311                             minTabWidth: 140
18312                          }
18313                     })
18314                 };
18315             }
18316         }
18317         
18318         if (!this.parent.el) {
18319                 // probably an old style ctor, which has been disabled.
18320                 return;
18321
18322         }
18323                 // The 'tree' method is  '_tree now' 
18324             
18325         tree.region = tree.region || this.region;
18326         var is_body = false;
18327         if (this.parent.el === true) {
18328             // bootstrap... - body..
18329             if (el) {
18330                 tree.el = el;
18331             }
18332             this.parent.el = Roo.factory(tree);
18333             is_body = true;
18334         }
18335         
18336         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18337         this.fireEvent('built', this);
18338         
18339         this.panel = this.el;
18340         this.layout = this.panel.layout;
18341         this.parentLayout = this.parent.layout  || false;  
18342          
18343     }
18344     
18345 });
18346
18347 Roo.apply(Roo.XComponent, {
18348     /**
18349      * @property  hideProgress
18350      * true to disable the building progress bar.. usefull on single page renders.
18351      * @type Boolean
18352      */
18353     hideProgress : false,
18354     /**
18355      * @property  buildCompleted
18356      * True when the builder has completed building the interface.
18357      * @type Boolean
18358      */
18359     buildCompleted : false,
18360      
18361     /**
18362      * @property  topModule
18363      * the upper most module - uses document.element as it's constructor.
18364      * @type Object
18365      */
18366      
18367     topModule  : false,
18368       
18369     /**
18370      * @property  modules
18371      * array of modules to be created by registration system.
18372      * @type {Array} of Roo.XComponent
18373      */
18374     
18375     modules : [],
18376     /**
18377      * @property  elmodules
18378      * array of modules to be created by which use #ID 
18379      * @type {Array} of Roo.XComponent
18380      */
18381      
18382     elmodules : [],
18383
18384      /**
18385      * @property  is_alt
18386      * Is an alternative Root - normally used by bootstrap or other systems,
18387      *    where the top element in the tree can wrap 'body' 
18388      * @type {boolean}  (default false)
18389      */
18390      
18391     is_alt : false,
18392     /**
18393      * @property  build_from_html
18394      * Build elements from html - used by bootstrap HTML stuff 
18395      *    - this is cleared after build is completed
18396      * @type {boolean}    (default false)
18397      */
18398      
18399     build_from_html : false,
18400     /**
18401      * Register components to be built later.
18402      *
18403      * This solves the following issues
18404      * - Building is not done on page load, but after an authentication process has occured.
18405      * - Interface elements are registered on page load
18406      * - Parent Interface elements may not be loaded before child, so this handles that..
18407      * 
18408      *
18409      * example:
18410      * 
18411      * MyApp.register({
18412           order : '000001',
18413           module : 'Pman.Tab.projectMgr',
18414           region : 'center',
18415           parent : 'Pman.layout',
18416           disabled : false,  // or use a function..
18417         })
18418      
18419      * * @param {Object} details about module
18420      */
18421     register : function(obj) {
18422                 
18423         Roo.XComponent.event.fireEvent('register', obj);
18424         switch(typeof(obj.disabled) ) {
18425                 
18426             case 'undefined':
18427                 break;
18428             
18429             case 'function':
18430                 if ( obj.disabled() ) {
18431                         return;
18432                 }
18433                 break;
18434             
18435             default:
18436                 if (obj.disabled || obj.region == '#disabled') {
18437                         return;
18438                 }
18439                 break;
18440         }
18441                 
18442         this.modules.push(obj);
18443          
18444     },
18445     /**
18446      * convert a string to an object..
18447      * eg. 'AAA.BBB' -> finds AAA.BBB
18448
18449      */
18450     
18451     toObject : function(str)
18452     {
18453         if (!str || typeof(str) == 'object') {
18454             return str;
18455         }
18456         if (str.substring(0,1) == '#') {
18457             return str;
18458         }
18459
18460         var ar = str.split('.');
18461         var rt, o;
18462         rt = ar.shift();
18463             /** eval:var:o */
18464         try {
18465             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18466         } catch (e) {
18467             throw "Module not found : " + str;
18468         }
18469         
18470         if (o === false) {
18471             throw "Module not found : " + str;
18472         }
18473         Roo.each(ar, function(e) {
18474             if (typeof(o[e]) == 'undefined') {
18475                 throw "Module not found : " + str;
18476             }
18477             o = o[e];
18478         });
18479         
18480         return o;
18481         
18482     },
18483     
18484     
18485     /**
18486      * move modules into their correct place in the tree..
18487      * 
18488      */
18489     preBuild : function ()
18490     {
18491         var _t = this;
18492         Roo.each(this.modules , function (obj)
18493         {
18494             Roo.XComponent.event.fireEvent('beforebuild', obj);
18495             
18496             var opar = obj.parent;
18497             try { 
18498                 obj.parent = this.toObject(opar);
18499             } catch(e) {
18500                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18501                 return;
18502             }
18503             
18504             if (!obj.parent) {
18505                 Roo.debug && Roo.log("GOT top level module");
18506                 Roo.debug && Roo.log(obj);
18507                 obj.modules = new Roo.util.MixedCollection(false, 
18508                     function(o) { return o.order + '' }
18509                 );
18510                 this.topModule = obj;
18511                 return;
18512             }
18513                         // parent is a string (usually a dom element name..)
18514             if (typeof(obj.parent) == 'string') {
18515                 this.elmodules.push(obj);
18516                 return;
18517             }
18518             if (obj.parent.constructor != Roo.XComponent) {
18519                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18520             }
18521             if (!obj.parent.modules) {
18522                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18523                     function(o) { return o.order + '' }
18524                 );
18525             }
18526             if (obj.parent.disabled) {
18527                 obj.disabled = true;
18528             }
18529             obj.parent.modules.add(obj);
18530         }, this);
18531     },
18532     
18533      /**
18534      * make a list of modules to build.
18535      * @return {Array} list of modules. 
18536      */ 
18537     
18538     buildOrder : function()
18539     {
18540         var _this = this;
18541         var cmp = function(a,b) {   
18542             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18543         };
18544         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18545             throw "No top level modules to build";
18546         }
18547         
18548         // make a flat list in order of modules to build.
18549         var mods = this.topModule ? [ this.topModule ] : [];
18550                 
18551         
18552         // elmodules (is a list of DOM based modules )
18553         Roo.each(this.elmodules, function(e) {
18554             mods.push(e);
18555             if (!this.topModule &&
18556                 typeof(e.parent) == 'string' &&
18557                 e.parent.substring(0,1) == '#' &&
18558                 Roo.get(e.parent.substr(1))
18559                ) {
18560                 
18561                 _this.topModule = e;
18562             }
18563             
18564         });
18565
18566         
18567         // add modules to their parents..
18568         var addMod = function(m) {
18569             Roo.debug && Roo.log("build Order: add: " + m.name);
18570                 
18571             mods.push(m);
18572             if (m.modules && !m.disabled) {
18573                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18574                 m.modules.keySort('ASC',  cmp );
18575                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18576     
18577                 m.modules.each(addMod);
18578             } else {
18579                 Roo.debug && Roo.log("build Order: no child modules");
18580             }
18581             // not sure if this is used any more..
18582             if (m.finalize) {
18583                 m.finalize.name = m.name + " (clean up) ";
18584                 mods.push(m.finalize);
18585             }
18586             
18587         }
18588         if (this.topModule && this.topModule.modules) { 
18589             this.topModule.modules.keySort('ASC',  cmp );
18590             this.topModule.modules.each(addMod);
18591         } 
18592         return mods;
18593     },
18594     
18595      /**
18596      * Build the registered modules.
18597      * @param {Object} parent element.
18598      * @param {Function} optional method to call after module has been added.
18599      * 
18600      */ 
18601    
18602     build : function(opts) 
18603     {
18604         
18605         if (typeof(opts) != 'undefined') {
18606             Roo.apply(this,opts);
18607         }
18608         
18609         this.preBuild();
18610         var mods = this.buildOrder();
18611       
18612         //this.allmods = mods;
18613         //Roo.debug && Roo.log(mods);
18614         //return;
18615         if (!mods.length) { // should not happen
18616             throw "NO modules!!!";
18617         }
18618         
18619         
18620         var msg = "Building Interface...";
18621         // flash it up as modal - so we store the mask!?
18622         if (!this.hideProgress && Roo.MessageBox) {
18623             Roo.MessageBox.show({ title: 'loading' });
18624             Roo.MessageBox.show({
18625                title: "Please wait...",
18626                msg: msg,
18627                width:450,
18628                progress:true,
18629                buttons : false,
18630                closable:false,
18631                modal: false
18632               
18633             });
18634         }
18635         var total = mods.length;
18636         
18637         var _this = this;
18638         var progressRun = function() {
18639             if (!mods.length) {
18640                 Roo.debug && Roo.log('hide?');
18641                 if (!this.hideProgress && Roo.MessageBox) {
18642                     Roo.MessageBox.hide();
18643                 }
18644                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18645                 
18646                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18647                 
18648                 // THE END...
18649                 return false;   
18650             }
18651             
18652             var m = mods.shift();
18653             
18654             
18655             Roo.debug && Roo.log(m);
18656             // not sure if this is supported any more.. - modules that are are just function
18657             if (typeof(m) == 'function') { 
18658                 m.call(this);
18659                 return progressRun.defer(10, _this);
18660             } 
18661             
18662             
18663             msg = "Building Interface " + (total  - mods.length) + 
18664                     " of " + total + 
18665                     (m.name ? (' - ' + m.name) : '');
18666                         Roo.debug && Roo.log(msg);
18667             if (!_this.hideProgress &&  Roo.MessageBox) { 
18668                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18669             }
18670             
18671          
18672             // is the module disabled?
18673             var disabled = (typeof(m.disabled) == 'function') ?
18674                 m.disabled.call(m.module.disabled) : m.disabled;    
18675             
18676             
18677             if (disabled) {
18678                 return progressRun(); // we do not update the display!
18679             }
18680             
18681             // now build 
18682             
18683                         
18684                         
18685             m.render();
18686             // it's 10 on top level, and 1 on others??? why...
18687             return progressRun.defer(10, _this);
18688              
18689         }
18690         progressRun.defer(1, _this);
18691      
18692         
18693         
18694     },
18695     /**
18696      * Overlay a set of modified strings onto a component
18697      * This is dependant on our builder exporting the strings and 'named strings' elements.
18698      * 
18699      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18700      * @param {Object} associative array of 'named' string and it's new value.
18701      * 
18702      */
18703         overlayStrings : function( component, strings )
18704     {
18705         if (typeof(component['_named_strings']) == 'undefined') {
18706             throw "ERROR: component does not have _named_strings";
18707         }
18708         for ( var k in strings ) {
18709             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18710             if (md !== false) {
18711                 component['_strings'][md] = strings[k];
18712             } else {
18713                 Roo.log('could not find named string: ' + k + ' in');
18714                 Roo.log(component);
18715             }
18716             
18717         }
18718         
18719     },
18720     
18721         
18722         /**
18723          * Event Object.
18724          *
18725          *
18726          */
18727         event: false, 
18728     /**
18729          * wrapper for event.on - aliased later..  
18730          * Typically use to register a event handler for register:
18731          *
18732          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18733          *
18734          */
18735     on : false
18736    
18737     
18738     
18739 });
18740
18741 Roo.XComponent.event = new Roo.util.Observable({
18742                 events : { 
18743                         /**
18744                          * @event register
18745                          * Fires when an Component is registered,
18746                          * set the disable property on the Component to stop registration.
18747                          * @param {Roo.XComponent} c the component being registerd.
18748                          * 
18749                          */
18750                         'register' : true,
18751             /**
18752                          * @event beforebuild
18753                          * Fires before each Component is built
18754                          * can be used to apply permissions.
18755                          * @param {Roo.XComponent} c the component being registerd.
18756                          * 
18757                          */
18758                         'beforebuild' : true,
18759                         /**
18760                          * @event buildcomplete
18761                          * Fires on the top level element when all elements have been built
18762                          * @param {Roo.XComponent} the top level component.
18763                          */
18764                         'buildcomplete' : true
18765                         
18766                 }
18767 });
18768
18769 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18770  //
18771  /**
18772  * marked - a markdown parser
18773  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18774  * https://github.com/chjj/marked
18775  */
18776
18777
18778 /**
18779  *
18780  * Roo.Markdown - is a very crude wrapper around marked..
18781  *
18782  * usage:
18783  * 
18784  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18785  * 
18786  * Note: move the sample code to the bottom of this
18787  * file before uncommenting it.
18788  *
18789  */
18790
18791 Roo.Markdown = {};
18792 Roo.Markdown.toHtml = function(text) {
18793     
18794     var c = new Roo.Markdown.marked.setOptions({
18795             renderer: new Roo.Markdown.marked.Renderer(),
18796             gfm: true,
18797             tables: true,
18798             breaks: false,
18799             pedantic: false,
18800             sanitize: false,
18801             smartLists: true,
18802             smartypants: false
18803           });
18804     // A FEW HACKS!!?
18805     
18806     text = text.replace(/\\\n/g,' ');
18807     return Roo.Markdown.marked(text);
18808 };
18809 //
18810 // converter
18811 //
18812 // Wraps all "globals" so that the only thing
18813 // exposed is makeHtml().
18814 //
18815 (function() {
18816     
18817      /**
18818          * eval:var:escape
18819          * eval:var:unescape
18820          * eval:var:replace
18821          */
18822       
18823     /**
18824      * Helpers
18825      */
18826     
18827     var escape = function (html, encode) {
18828       return html
18829         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18830         .replace(/</g, '&lt;')
18831         .replace(/>/g, '&gt;')
18832         .replace(/"/g, '&quot;')
18833         .replace(/'/g, '&#39;');
18834     }
18835     
18836     var unescape = function (html) {
18837         // explicitly match decimal, hex, and named HTML entities 
18838       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18839         n = n.toLowerCase();
18840         if (n === 'colon') { return ':'; }
18841         if (n.charAt(0) === '#') {
18842           return n.charAt(1) === 'x'
18843             ? String.fromCharCode(parseInt(n.substring(2), 16))
18844             : String.fromCharCode(+n.substring(1));
18845         }
18846         return '';
18847       });
18848     }
18849     
18850     var replace = function (regex, opt) {
18851       regex = regex.source;
18852       opt = opt || '';
18853       return function self(name, val) {
18854         if (!name) { return new RegExp(regex, opt); }
18855         val = val.source || val;
18856         val = val.replace(/(^|[^\[])\^/g, '$1');
18857         regex = regex.replace(name, val);
18858         return self;
18859       };
18860     }
18861
18862
18863          /**
18864          * eval:var:noop
18865     */
18866     var noop = function () {}
18867     noop.exec = noop;
18868     
18869          /**
18870          * eval:var:merge
18871     */
18872     var merge = function (obj) {
18873       var i = 1
18874         , target
18875         , key;
18876     
18877       for (; i < arguments.length; i++) {
18878         target = arguments[i];
18879         for (key in target) {
18880           if (Object.prototype.hasOwnProperty.call(target, key)) {
18881             obj[key] = target[key];
18882           }
18883         }
18884       }
18885     
18886       return obj;
18887     }
18888     
18889     
18890     /**
18891      * Block-Level Grammar
18892      */
18893     
18894     
18895     
18896     
18897     var block = {
18898       newline: /^\n+/,
18899       code: /^( {4}[^\n]+\n*)+/,
18900       fences: noop,
18901       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18902       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18903       nptable: noop,
18904       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18905       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18906       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18907       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18908       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18909       table: noop,
18910       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18911       text: /^[^\n]+/
18912     };
18913     
18914     block.bullet = /(?:[*+-]|\d+\.)/;
18915     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18916     block.item = replace(block.item, 'gm')
18917       (/bull/g, block.bullet)
18918       ();
18919     
18920     block.list = replace(block.list)
18921       (/bull/g, block.bullet)
18922       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18923       ('def', '\\n+(?=' + block.def.source + ')')
18924       ();
18925     
18926     block.blockquote = replace(block.blockquote)
18927       ('def', block.def)
18928       ();
18929     
18930     block._tag = '(?!(?:'
18931       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18932       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18933       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18934     
18935     block.html = replace(block.html)
18936       ('comment', /<!--[\s\S]*?-->/)
18937       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18938       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18939       (/tag/g, block._tag)
18940       ();
18941     
18942     block.paragraph = replace(block.paragraph)
18943       ('hr', block.hr)
18944       ('heading', block.heading)
18945       ('lheading', block.lheading)
18946       ('blockquote', block.blockquote)
18947       ('tag', '<' + block._tag)
18948       ('def', block.def)
18949       ();
18950     
18951     /**
18952      * Normal Block Grammar
18953      */
18954     
18955     block.normal = merge({}, block);
18956     
18957     /**
18958      * GFM Block Grammar
18959      */
18960     
18961     block.gfm = merge({}, block.normal, {
18962       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18963       paragraph: /^/,
18964       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18965     });
18966     
18967     block.gfm.paragraph = replace(block.paragraph)
18968       ('(?!', '(?!'
18969         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18970         + block.list.source.replace('\\1', '\\3') + '|')
18971       ();
18972     
18973     /**
18974      * GFM + Tables Block Grammar
18975      */
18976     
18977     block.tables = merge({}, block.gfm, {
18978       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18979       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18980     });
18981     
18982     /**
18983      * Block Lexer
18984      */
18985     
18986     var Lexer = function (options) {
18987       this.tokens = [];
18988       this.tokens.links = {};
18989       this.options = options || marked.defaults;
18990       this.rules = block.normal;
18991     
18992       if (this.options.gfm) {
18993         if (this.options.tables) {
18994           this.rules = block.tables;
18995         } else {
18996           this.rules = block.gfm;
18997         }
18998       }
18999     }
19000     
19001     /**
19002      * Expose Block Rules
19003      */
19004     
19005     Lexer.rules = block;
19006     
19007     /**
19008      * Static Lex Method
19009      */
19010     
19011     Lexer.lex = function(src, options) {
19012       var lexer = new Lexer(options);
19013       return lexer.lex(src);
19014     };
19015     
19016     /**
19017      * Preprocessing
19018      */
19019     
19020     Lexer.prototype.lex = function(src) {
19021       src = src
19022         .replace(/\r\n|\r/g, '\n')
19023         .replace(/\t/g, '    ')
19024         .replace(/\u00a0/g, ' ')
19025         .replace(/\u2424/g, '\n');
19026     
19027       return this.token(src, true);
19028     };
19029     
19030     /**
19031      * Lexing
19032      */
19033     
19034     Lexer.prototype.token = function(src, top, bq) {
19035       var src = src.replace(/^ +$/gm, '')
19036         , next
19037         , loose
19038         , cap
19039         , bull
19040         , b
19041         , item
19042         , space
19043         , i
19044         , l;
19045     
19046       while (src) {
19047         // newline
19048         if (cap = this.rules.newline.exec(src)) {
19049           src = src.substring(cap[0].length);
19050           if (cap[0].length > 1) {
19051             this.tokens.push({
19052               type: 'space'
19053             });
19054           }
19055         }
19056     
19057         // code
19058         if (cap = this.rules.code.exec(src)) {
19059           src = src.substring(cap[0].length);
19060           cap = cap[0].replace(/^ {4}/gm, '');
19061           this.tokens.push({
19062             type: 'code',
19063             text: !this.options.pedantic
19064               ? cap.replace(/\n+$/, '')
19065               : cap
19066           });
19067           continue;
19068         }
19069     
19070         // fences (gfm)
19071         if (cap = this.rules.fences.exec(src)) {
19072           src = src.substring(cap[0].length);
19073           this.tokens.push({
19074             type: 'code',
19075             lang: cap[2],
19076             text: cap[3] || ''
19077           });
19078           continue;
19079         }
19080     
19081         // heading
19082         if (cap = this.rules.heading.exec(src)) {
19083           src = src.substring(cap[0].length);
19084           this.tokens.push({
19085             type: 'heading',
19086             depth: cap[1].length,
19087             text: cap[2]
19088           });
19089           continue;
19090         }
19091     
19092         // table no leading pipe (gfm)
19093         if (top && (cap = this.rules.nptable.exec(src))) {
19094           src = src.substring(cap[0].length);
19095     
19096           item = {
19097             type: 'table',
19098             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19099             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19100             cells: cap[3].replace(/\n$/, '').split('\n')
19101           };
19102     
19103           for (i = 0; i < item.align.length; i++) {
19104             if (/^ *-+: *$/.test(item.align[i])) {
19105               item.align[i] = 'right';
19106             } else if (/^ *:-+: *$/.test(item.align[i])) {
19107               item.align[i] = 'center';
19108             } else if (/^ *:-+ *$/.test(item.align[i])) {
19109               item.align[i] = 'left';
19110             } else {
19111               item.align[i] = null;
19112             }
19113           }
19114     
19115           for (i = 0; i < item.cells.length; i++) {
19116             item.cells[i] = item.cells[i].split(/ *\| */);
19117           }
19118     
19119           this.tokens.push(item);
19120     
19121           continue;
19122         }
19123     
19124         // lheading
19125         if (cap = this.rules.lheading.exec(src)) {
19126           src = src.substring(cap[0].length);
19127           this.tokens.push({
19128             type: 'heading',
19129             depth: cap[2] === '=' ? 1 : 2,
19130             text: cap[1]
19131           });
19132           continue;
19133         }
19134     
19135         // hr
19136         if (cap = this.rules.hr.exec(src)) {
19137           src = src.substring(cap[0].length);
19138           this.tokens.push({
19139             type: 'hr'
19140           });
19141           continue;
19142         }
19143     
19144         // blockquote
19145         if (cap = this.rules.blockquote.exec(src)) {
19146           src = src.substring(cap[0].length);
19147     
19148           this.tokens.push({
19149             type: 'blockquote_start'
19150           });
19151     
19152           cap = cap[0].replace(/^ *> ?/gm, '');
19153     
19154           // Pass `top` to keep the current
19155           // "toplevel" state. This is exactly
19156           // how markdown.pl works.
19157           this.token(cap, top, true);
19158     
19159           this.tokens.push({
19160             type: 'blockquote_end'
19161           });
19162     
19163           continue;
19164         }
19165     
19166         // list
19167         if (cap = this.rules.list.exec(src)) {
19168           src = src.substring(cap[0].length);
19169           bull = cap[2];
19170     
19171           this.tokens.push({
19172             type: 'list_start',
19173             ordered: bull.length > 1
19174           });
19175     
19176           // Get each top-level item.
19177           cap = cap[0].match(this.rules.item);
19178     
19179           next = false;
19180           l = cap.length;
19181           i = 0;
19182     
19183           for (; i < l; i++) {
19184             item = cap[i];
19185     
19186             // Remove the list item's bullet
19187             // so it is seen as the next token.
19188             space = item.length;
19189             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19190     
19191             // Outdent whatever the
19192             // list item contains. Hacky.
19193             if (~item.indexOf('\n ')) {
19194               space -= item.length;
19195               item = !this.options.pedantic
19196                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19197                 : item.replace(/^ {1,4}/gm, '');
19198             }
19199     
19200             // Determine whether the next list item belongs here.
19201             // Backpedal if it does not belong in this list.
19202             if (this.options.smartLists && i !== l - 1) {
19203               b = block.bullet.exec(cap[i + 1])[0];
19204               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19205                 src = cap.slice(i + 1).join('\n') + src;
19206                 i = l - 1;
19207               }
19208             }
19209     
19210             // Determine whether item is loose or not.
19211             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19212             // for discount behavior.
19213             loose = next || /\n\n(?!\s*$)/.test(item);
19214             if (i !== l - 1) {
19215               next = item.charAt(item.length - 1) === '\n';
19216               if (!loose) { loose = next; }
19217             }
19218     
19219             this.tokens.push({
19220               type: loose
19221                 ? 'loose_item_start'
19222                 : 'list_item_start'
19223             });
19224     
19225             // Recurse.
19226             this.token(item, false, bq);
19227     
19228             this.tokens.push({
19229               type: 'list_item_end'
19230             });
19231           }
19232     
19233           this.tokens.push({
19234             type: 'list_end'
19235           });
19236     
19237           continue;
19238         }
19239     
19240         // html
19241         if (cap = this.rules.html.exec(src)) {
19242           src = src.substring(cap[0].length);
19243           this.tokens.push({
19244             type: this.options.sanitize
19245               ? 'paragraph'
19246               : 'html',
19247             pre: !this.options.sanitizer
19248               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19249             text: cap[0]
19250           });
19251           continue;
19252         }
19253     
19254         // def
19255         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19256           src = src.substring(cap[0].length);
19257           this.tokens.links[cap[1].toLowerCase()] = {
19258             href: cap[2],
19259             title: cap[3]
19260           };
19261           continue;
19262         }
19263     
19264         // table (gfm)
19265         if (top && (cap = this.rules.table.exec(src))) {
19266           src = src.substring(cap[0].length);
19267     
19268           item = {
19269             type: 'table',
19270             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19271             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19272             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19273           };
19274     
19275           for (i = 0; i < item.align.length; i++) {
19276             if (/^ *-+: *$/.test(item.align[i])) {
19277               item.align[i] = 'right';
19278             } else if (/^ *:-+: *$/.test(item.align[i])) {
19279               item.align[i] = 'center';
19280             } else if (/^ *:-+ *$/.test(item.align[i])) {
19281               item.align[i] = 'left';
19282             } else {
19283               item.align[i] = null;
19284             }
19285           }
19286     
19287           for (i = 0; i < item.cells.length; i++) {
19288             item.cells[i] = item.cells[i]
19289               .replace(/^ *\| *| *\| *$/g, '')
19290               .split(/ *\| */);
19291           }
19292     
19293           this.tokens.push(item);
19294     
19295           continue;
19296         }
19297     
19298         // top-level paragraph
19299         if (top && (cap = this.rules.paragraph.exec(src))) {
19300           src = src.substring(cap[0].length);
19301           this.tokens.push({
19302             type: 'paragraph',
19303             text: cap[1].charAt(cap[1].length - 1) === '\n'
19304               ? cap[1].slice(0, -1)
19305               : cap[1]
19306           });
19307           continue;
19308         }
19309     
19310         // text
19311         if (cap = this.rules.text.exec(src)) {
19312           // Top-level should never reach here.
19313           src = src.substring(cap[0].length);
19314           this.tokens.push({
19315             type: 'text',
19316             text: cap[0]
19317           });
19318           continue;
19319         }
19320     
19321         if (src) {
19322           throw new
19323             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19324         }
19325       }
19326     
19327       return this.tokens;
19328     };
19329     
19330     /**
19331      * Inline-Level Grammar
19332      */
19333     
19334     var inline = {
19335       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19336       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19337       url: noop,
19338       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19339       link: /^!?\[(inside)\]\(href\)/,
19340       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19341       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19342       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19343       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19344       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19345       br: /^ {2,}\n(?!\s*$)/,
19346       del: noop,
19347       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19348     };
19349     
19350     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19351     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19352     
19353     inline.link = replace(inline.link)
19354       ('inside', inline._inside)
19355       ('href', inline._href)
19356       ();
19357     
19358     inline.reflink = replace(inline.reflink)
19359       ('inside', inline._inside)
19360       ();
19361     
19362     /**
19363      * Normal Inline Grammar
19364      */
19365     
19366     inline.normal = merge({}, inline);
19367     
19368     /**
19369      * Pedantic Inline Grammar
19370      */
19371     
19372     inline.pedantic = merge({}, inline.normal, {
19373       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19374       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19375     });
19376     
19377     /**
19378      * GFM Inline Grammar
19379      */
19380     
19381     inline.gfm = merge({}, inline.normal, {
19382       escape: replace(inline.escape)('])', '~|])')(),
19383       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19384       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19385       text: replace(inline.text)
19386         (']|', '~]|')
19387         ('|', '|https?://|')
19388         ()
19389     });
19390     
19391     /**
19392      * GFM + Line Breaks Inline Grammar
19393      */
19394     
19395     inline.breaks = merge({}, inline.gfm, {
19396       br: replace(inline.br)('{2,}', '*')(),
19397       text: replace(inline.gfm.text)('{2,}', '*')()
19398     });
19399     
19400     /**
19401      * Inline Lexer & Compiler
19402      */
19403     
19404     var InlineLexer  = function (links, options) {
19405       this.options = options || marked.defaults;
19406       this.links = links;
19407       this.rules = inline.normal;
19408       this.renderer = this.options.renderer || new Renderer;
19409       this.renderer.options = this.options;
19410     
19411       if (!this.links) {
19412         throw new
19413           Error('Tokens array requires a `links` property.');
19414       }
19415     
19416       if (this.options.gfm) {
19417         if (this.options.breaks) {
19418           this.rules = inline.breaks;
19419         } else {
19420           this.rules = inline.gfm;
19421         }
19422       } else if (this.options.pedantic) {
19423         this.rules = inline.pedantic;
19424       }
19425     }
19426     
19427     /**
19428      * Expose Inline Rules
19429      */
19430     
19431     InlineLexer.rules = inline;
19432     
19433     /**
19434      * Static Lexing/Compiling Method
19435      */
19436     
19437     InlineLexer.output = function(src, links, options) {
19438       var inline = new InlineLexer(links, options);
19439       return inline.output(src);
19440     };
19441     
19442     /**
19443      * Lexing/Compiling
19444      */
19445     
19446     InlineLexer.prototype.output = function(src) {
19447       var out = ''
19448         , link
19449         , text
19450         , href
19451         , cap;
19452     
19453       while (src) {
19454         // escape
19455         if (cap = this.rules.escape.exec(src)) {
19456           src = src.substring(cap[0].length);
19457           out += cap[1];
19458           continue;
19459         }
19460     
19461         // autolink
19462         if (cap = this.rules.autolink.exec(src)) {
19463           src = src.substring(cap[0].length);
19464           if (cap[2] === '@') {
19465             text = cap[1].charAt(6) === ':'
19466               ? this.mangle(cap[1].substring(7))
19467               : this.mangle(cap[1]);
19468             href = this.mangle('mailto:') + text;
19469           } else {
19470             text = escape(cap[1]);
19471             href = text;
19472           }
19473           out += this.renderer.link(href, null, text);
19474           continue;
19475         }
19476     
19477         // url (gfm)
19478         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19479           src = src.substring(cap[0].length);
19480           text = escape(cap[1]);
19481           href = text;
19482           out += this.renderer.link(href, null, text);
19483           continue;
19484         }
19485     
19486         // tag
19487         if (cap = this.rules.tag.exec(src)) {
19488           if (!this.inLink && /^<a /i.test(cap[0])) {
19489             this.inLink = true;
19490           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19491             this.inLink = false;
19492           }
19493           src = src.substring(cap[0].length);
19494           out += this.options.sanitize
19495             ? this.options.sanitizer
19496               ? this.options.sanitizer(cap[0])
19497               : escape(cap[0])
19498             : cap[0];
19499           continue;
19500         }
19501     
19502         // link
19503         if (cap = this.rules.link.exec(src)) {
19504           src = src.substring(cap[0].length);
19505           this.inLink = true;
19506           out += this.outputLink(cap, {
19507             href: cap[2],
19508             title: cap[3]
19509           });
19510           this.inLink = false;
19511           continue;
19512         }
19513     
19514         // reflink, nolink
19515         if ((cap = this.rules.reflink.exec(src))
19516             || (cap = this.rules.nolink.exec(src))) {
19517           src = src.substring(cap[0].length);
19518           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19519           link = this.links[link.toLowerCase()];
19520           if (!link || !link.href) {
19521             out += cap[0].charAt(0);
19522             src = cap[0].substring(1) + src;
19523             continue;
19524           }
19525           this.inLink = true;
19526           out += this.outputLink(cap, link);
19527           this.inLink = false;
19528           continue;
19529         }
19530     
19531         // strong
19532         if (cap = this.rules.strong.exec(src)) {
19533           src = src.substring(cap[0].length);
19534           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19535           continue;
19536         }
19537     
19538         // em
19539         if (cap = this.rules.em.exec(src)) {
19540           src = src.substring(cap[0].length);
19541           out += this.renderer.em(this.output(cap[2] || cap[1]));
19542           continue;
19543         }
19544     
19545         // code
19546         if (cap = this.rules.code.exec(src)) {
19547           src = src.substring(cap[0].length);
19548           out += this.renderer.codespan(escape(cap[2], true));
19549           continue;
19550         }
19551     
19552         // br
19553         if (cap = this.rules.br.exec(src)) {
19554           src = src.substring(cap[0].length);
19555           out += this.renderer.br();
19556           continue;
19557         }
19558     
19559         // del (gfm)
19560         if (cap = this.rules.del.exec(src)) {
19561           src = src.substring(cap[0].length);
19562           out += this.renderer.del(this.output(cap[1]));
19563           continue;
19564         }
19565     
19566         // text
19567         if (cap = this.rules.text.exec(src)) {
19568           src = src.substring(cap[0].length);
19569           out += this.renderer.text(escape(this.smartypants(cap[0])));
19570           continue;
19571         }
19572     
19573         if (src) {
19574           throw new
19575             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19576         }
19577       }
19578     
19579       return out;
19580     };
19581     
19582     /**
19583      * Compile Link
19584      */
19585     
19586     InlineLexer.prototype.outputLink = function(cap, link) {
19587       var href = escape(link.href)
19588         , title = link.title ? escape(link.title) : null;
19589     
19590       return cap[0].charAt(0) !== '!'
19591         ? this.renderer.link(href, title, this.output(cap[1]))
19592         : this.renderer.image(href, title, escape(cap[1]));
19593     };
19594     
19595     /**
19596      * Smartypants Transformations
19597      */
19598     
19599     InlineLexer.prototype.smartypants = function(text) {
19600       if (!this.options.smartypants)  { return text; }
19601       return text
19602         // em-dashes
19603         .replace(/---/g, '\u2014')
19604         // en-dashes
19605         .replace(/--/g, '\u2013')
19606         // opening singles
19607         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19608         // closing singles & apostrophes
19609         .replace(/'/g, '\u2019')
19610         // opening doubles
19611         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19612         // closing doubles
19613         .replace(/"/g, '\u201d')
19614         // ellipses
19615         .replace(/\.{3}/g, '\u2026');
19616     };
19617     
19618     /**
19619      * Mangle Links
19620      */
19621     
19622     InlineLexer.prototype.mangle = function(text) {
19623       if (!this.options.mangle) { return text; }
19624       var out = ''
19625         , l = text.length
19626         , i = 0
19627         , ch;
19628     
19629       for (; i < l; i++) {
19630         ch = text.charCodeAt(i);
19631         if (Math.random() > 0.5) {
19632           ch = 'x' + ch.toString(16);
19633         }
19634         out += '&#' + ch + ';';
19635       }
19636     
19637       return out;
19638     };
19639     
19640     /**
19641      * Renderer
19642      */
19643     
19644      /**
19645          * eval:var:Renderer
19646     */
19647     
19648     var Renderer   = function (options) {
19649       this.options = options || {};
19650     }
19651     
19652     Renderer.prototype.code = function(code, lang, escaped) {
19653       if (this.options.highlight) {
19654         var out = this.options.highlight(code, lang);
19655         if (out != null && out !== code) {
19656           escaped = true;
19657           code = out;
19658         }
19659       } else {
19660             // hack!!! - it's already escapeD?
19661             escaped = true;
19662       }
19663     
19664       if (!lang) {
19665         return '<pre><code>'
19666           + (escaped ? code : escape(code, true))
19667           + '\n</code></pre>';
19668       }
19669     
19670       return '<pre><code class="'
19671         + this.options.langPrefix
19672         + escape(lang, true)
19673         + '">'
19674         + (escaped ? code : escape(code, true))
19675         + '\n</code></pre>\n';
19676     };
19677     
19678     Renderer.prototype.blockquote = function(quote) {
19679       return '<blockquote>\n' + quote + '</blockquote>\n';
19680     };
19681     
19682     Renderer.prototype.html = function(html) {
19683       return html;
19684     };
19685     
19686     Renderer.prototype.heading = function(text, level, raw) {
19687       return '<h'
19688         + level
19689         + ' id="'
19690         + this.options.headerPrefix
19691         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19692         + '">'
19693         + text
19694         + '</h'
19695         + level
19696         + '>\n';
19697     };
19698     
19699     Renderer.prototype.hr = function() {
19700       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19701     };
19702     
19703     Renderer.prototype.list = function(body, ordered) {
19704       var type = ordered ? 'ol' : 'ul';
19705       return '<' + type + '>\n' + body + '</' + type + '>\n';
19706     };
19707     
19708     Renderer.prototype.listitem = function(text) {
19709       return '<li>' + text + '</li>\n';
19710     };
19711     
19712     Renderer.prototype.paragraph = function(text) {
19713       return '<p>' + text + '</p>\n';
19714     };
19715     
19716     Renderer.prototype.table = function(header, body) {
19717       return '<table class="table table-striped">\n'
19718         + '<thead>\n'
19719         + header
19720         + '</thead>\n'
19721         + '<tbody>\n'
19722         + body
19723         + '</tbody>\n'
19724         + '</table>\n';
19725     };
19726     
19727     Renderer.prototype.tablerow = function(content) {
19728       return '<tr>\n' + content + '</tr>\n';
19729     };
19730     
19731     Renderer.prototype.tablecell = function(content, flags) {
19732       var type = flags.header ? 'th' : 'td';
19733       var tag = flags.align
19734         ? '<' + type + ' style="text-align:' + flags.align + '">'
19735         : '<' + type + '>';
19736       return tag + content + '</' + type + '>\n';
19737     };
19738     
19739     // span level renderer
19740     Renderer.prototype.strong = function(text) {
19741       return '<strong>' + text + '</strong>';
19742     };
19743     
19744     Renderer.prototype.em = function(text) {
19745       return '<em>' + text + '</em>';
19746     };
19747     
19748     Renderer.prototype.codespan = function(text) {
19749       return '<code>' + text + '</code>';
19750     };
19751     
19752     Renderer.prototype.br = function() {
19753       return this.options.xhtml ? '<br/>' : '<br>';
19754     };
19755     
19756     Renderer.prototype.del = function(text) {
19757       return '<del>' + text + '</del>';
19758     };
19759     
19760     Renderer.prototype.link = function(href, title, text) {
19761       if (this.options.sanitize) {
19762         try {
19763           var prot = decodeURIComponent(unescape(href))
19764             .replace(/[^\w:]/g, '')
19765             .toLowerCase();
19766         } catch (e) {
19767           return '';
19768         }
19769         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19770           return '';
19771         }
19772       }
19773       var out = '<a href="' + href + '"';
19774       if (title) {
19775         out += ' title="' + title + '"';
19776       }
19777       out += '>' + text + '</a>';
19778       return out;
19779     };
19780     
19781     Renderer.prototype.image = function(href, title, text) {
19782       var out = '<img src="' + href + '" alt="' + text + '"';
19783       if (title) {
19784         out += ' title="' + title + '"';
19785       }
19786       out += this.options.xhtml ? '/>' : '>';
19787       return out;
19788     };
19789     
19790     Renderer.prototype.text = function(text) {
19791       return text;
19792     };
19793     
19794     /**
19795      * Parsing & Compiling
19796      */
19797          /**
19798          * eval:var:Parser
19799     */
19800     
19801     var Parser= function (options) {
19802       this.tokens = [];
19803       this.token = null;
19804       this.options = options || marked.defaults;
19805       this.options.renderer = this.options.renderer || new Renderer;
19806       this.renderer = this.options.renderer;
19807       this.renderer.options = this.options;
19808     }
19809     
19810     /**
19811      * Static Parse Method
19812      */
19813     
19814     Parser.parse = function(src, options, renderer) {
19815       var parser = new Parser(options, renderer);
19816       return parser.parse(src);
19817     };
19818     
19819     /**
19820      * Parse Loop
19821      */
19822     
19823     Parser.prototype.parse = function(src) {
19824       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19825       this.tokens = src.reverse();
19826     
19827       var out = '';
19828       while (this.next()) {
19829         out += this.tok();
19830       }
19831     
19832       return out;
19833     };
19834     
19835     /**
19836      * Next Token
19837      */
19838     
19839     Parser.prototype.next = function() {
19840       return this.token = this.tokens.pop();
19841     };
19842     
19843     /**
19844      * Preview Next Token
19845      */
19846     
19847     Parser.prototype.peek = function() {
19848       return this.tokens[this.tokens.length - 1] || 0;
19849     };
19850     
19851     /**
19852      * Parse Text Tokens
19853      */
19854     
19855     Parser.prototype.parseText = function() {
19856       var body = this.token.text;
19857     
19858       while (this.peek().type === 'text') {
19859         body += '\n' + this.next().text;
19860       }
19861     
19862       return this.inline.output(body);
19863     };
19864     
19865     /**
19866      * Parse Current Token
19867      */
19868     
19869     Parser.prototype.tok = function() {
19870       switch (this.token.type) {
19871         case 'space': {
19872           return '';
19873         }
19874         case 'hr': {
19875           return this.renderer.hr();
19876         }
19877         case 'heading': {
19878           return this.renderer.heading(
19879             this.inline.output(this.token.text),
19880             this.token.depth,
19881             this.token.text);
19882         }
19883         case 'code': {
19884           return this.renderer.code(this.token.text,
19885             this.token.lang,
19886             this.token.escaped);
19887         }
19888         case 'table': {
19889           var header = ''
19890             , body = ''
19891             , i
19892             , row
19893             , cell
19894             , flags
19895             , j;
19896     
19897           // header
19898           cell = '';
19899           for (i = 0; i < this.token.header.length; i++) {
19900             flags = { header: true, align: this.token.align[i] };
19901             cell += this.renderer.tablecell(
19902               this.inline.output(this.token.header[i]),
19903               { header: true, align: this.token.align[i] }
19904             );
19905           }
19906           header += this.renderer.tablerow(cell);
19907     
19908           for (i = 0; i < this.token.cells.length; i++) {
19909             row = this.token.cells[i];
19910     
19911             cell = '';
19912             for (j = 0; j < row.length; j++) {
19913               cell += this.renderer.tablecell(
19914                 this.inline.output(row[j]),
19915                 { header: false, align: this.token.align[j] }
19916               );
19917             }
19918     
19919             body += this.renderer.tablerow(cell);
19920           }
19921           return this.renderer.table(header, body);
19922         }
19923         case 'blockquote_start': {
19924           var body = '';
19925     
19926           while (this.next().type !== 'blockquote_end') {
19927             body += this.tok();
19928           }
19929     
19930           return this.renderer.blockquote(body);
19931         }
19932         case 'list_start': {
19933           var body = ''
19934             , ordered = this.token.ordered;
19935     
19936           while (this.next().type !== 'list_end') {
19937             body += this.tok();
19938           }
19939     
19940           return this.renderer.list(body, ordered);
19941         }
19942         case 'list_item_start': {
19943           var body = '';
19944     
19945           while (this.next().type !== 'list_item_end') {
19946             body += this.token.type === 'text'
19947               ? this.parseText()
19948               : this.tok();
19949           }
19950     
19951           return this.renderer.listitem(body);
19952         }
19953         case 'loose_item_start': {
19954           var body = '';
19955     
19956           while (this.next().type !== 'list_item_end') {
19957             body += this.tok();
19958           }
19959     
19960           return this.renderer.listitem(body);
19961         }
19962         case 'html': {
19963           var html = !this.token.pre && !this.options.pedantic
19964             ? this.inline.output(this.token.text)
19965             : this.token.text;
19966           return this.renderer.html(html);
19967         }
19968         case 'paragraph': {
19969           return this.renderer.paragraph(this.inline.output(this.token.text));
19970         }
19971         case 'text': {
19972           return this.renderer.paragraph(this.parseText());
19973         }
19974       }
19975     };
19976   
19977     
19978     /**
19979      * Marked
19980      */
19981          /**
19982          * eval:var:marked
19983     */
19984     var marked = function (src, opt, callback) {
19985       if (callback || typeof opt === 'function') {
19986         if (!callback) {
19987           callback = opt;
19988           opt = null;
19989         }
19990     
19991         opt = merge({}, marked.defaults, opt || {});
19992     
19993         var highlight = opt.highlight
19994           , tokens
19995           , pending
19996           , i = 0;
19997     
19998         try {
19999           tokens = Lexer.lex(src, opt)
20000         } catch (e) {
20001           return callback(e);
20002         }
20003     
20004         pending = tokens.length;
20005          /**
20006          * eval:var:done
20007     */
20008         var done = function(err) {
20009           if (err) {
20010             opt.highlight = highlight;
20011             return callback(err);
20012           }
20013     
20014           var out;
20015     
20016           try {
20017             out = Parser.parse(tokens, opt);
20018           } catch (e) {
20019             err = e;
20020           }
20021     
20022           opt.highlight = highlight;
20023     
20024           return err
20025             ? callback(err)
20026             : callback(null, out);
20027         };
20028     
20029         if (!highlight || highlight.length < 3) {
20030           return done();
20031         }
20032     
20033         delete opt.highlight;
20034     
20035         if (!pending) { return done(); }
20036     
20037         for (; i < tokens.length; i++) {
20038           (function(token) {
20039             if (token.type !== 'code') {
20040               return --pending || done();
20041             }
20042             return highlight(token.text, token.lang, function(err, code) {
20043               if (err) { return done(err); }
20044               if (code == null || code === token.text) {
20045                 return --pending || done();
20046               }
20047               token.text = code;
20048               token.escaped = true;
20049               --pending || done();
20050             });
20051           })(tokens[i]);
20052         }
20053     
20054         return;
20055       }
20056       try {
20057         if (opt) { opt = merge({}, marked.defaults, opt); }
20058         return Parser.parse(Lexer.lex(src, opt), opt);
20059       } catch (e) {
20060         e.message += '\nPlease report this to https://github.com/chjj/marked.';
20061         if ((opt || marked.defaults).silent) {
20062           return '<p>An error occured:</p><pre>'
20063             + escape(e.message + '', true)
20064             + '</pre>';
20065         }
20066         throw e;
20067       }
20068     }
20069     
20070     /**
20071      * Options
20072      */
20073     
20074     marked.options =
20075     marked.setOptions = function(opt) {
20076       merge(marked.defaults, opt);
20077       return marked;
20078     };
20079     
20080     marked.defaults = {
20081       gfm: true,
20082       tables: true,
20083       breaks: false,
20084       pedantic: false,
20085       sanitize: false,
20086       sanitizer: null,
20087       mangle: true,
20088       smartLists: false,
20089       silent: false,
20090       highlight: null,
20091       langPrefix: 'lang-',
20092       smartypants: false,
20093       headerPrefix: '',
20094       renderer: new Renderer,
20095       xhtml: false
20096     };
20097     
20098     /**
20099      * Expose
20100      */
20101     
20102     marked.Parser = Parser;
20103     marked.parser = Parser.parse;
20104     
20105     marked.Renderer = Renderer;
20106     
20107     marked.Lexer = Lexer;
20108     marked.lexer = Lexer.lex;
20109     
20110     marked.InlineLexer = InlineLexer;
20111     marked.inlineLexer = InlineLexer.output;
20112     
20113     marked.parse = marked;
20114     
20115     Roo.Markdown.marked = marked;
20116
20117 })();/*
20118  * Based on:
20119  * Ext JS Library 1.1.1
20120  * Copyright(c) 2006-2007, Ext JS, LLC.
20121  *
20122  * Originally Released Under LGPL - original licence link has changed is not relivant.
20123  *
20124  * Fork - LGPL
20125  * <script type="text/javascript">
20126  */
20127
20128
20129
20130 /*
20131  * These classes are derivatives of the similarly named classes in the YUI Library.
20132  * The original license:
20133  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20134  * Code licensed under the BSD License:
20135  * http://developer.yahoo.net/yui/license.txt
20136  */
20137
20138 (function() {
20139
20140 var Event=Roo.EventManager;
20141 var Dom=Roo.lib.Dom;
20142
20143 /**
20144  * @class Roo.dd.DragDrop
20145  * @extends Roo.util.Observable
20146  * Defines the interface and base operation of items that that can be
20147  * dragged or can be drop targets.  It was designed to be extended, overriding
20148  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20149  * Up to three html elements can be associated with a DragDrop instance:
20150  * <ul>
20151  * <li>linked element: the element that is passed into the constructor.
20152  * This is the element which defines the boundaries for interaction with
20153  * other DragDrop objects.</li>
20154  * <li>handle element(s): The drag operation only occurs if the element that
20155  * was clicked matches a handle element.  By default this is the linked
20156  * element, but there are times that you will want only a portion of the
20157  * linked element to initiate the drag operation, and the setHandleElId()
20158  * method provides a way to define this.</li>
20159  * <li>drag element: this represents the element that would be moved along
20160  * with the cursor during a drag operation.  By default, this is the linked
20161  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
20162  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20163  * </li>
20164  * </ul>
20165  * This class should not be instantiated until the onload event to ensure that
20166  * the associated elements are available.
20167  * The following would define a DragDrop obj that would interact with any
20168  * other DragDrop obj in the "group1" group:
20169  * <pre>
20170  *  dd = new Roo.dd.DragDrop("div1", "group1");
20171  * </pre>
20172  * Since none of the event handlers have been implemented, nothing would
20173  * actually happen if you were to run the code above.  Normally you would
20174  * override this class or one of the default implementations, but you can
20175  * also override the methods you want on an instance of the class...
20176  * <pre>
20177  *  dd.onDragDrop = function(e, id) {
20178  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
20179  *  }
20180  * </pre>
20181  * @constructor
20182  * @param {String} id of the element that is linked to this instance
20183  * @param {String} sGroup the group of related DragDrop objects
20184  * @param {object} config an object containing configurable attributes
20185  *                Valid properties for DragDrop:
20186  *                    padding, isTarget, maintainOffset, primaryButtonOnly
20187  */
20188 Roo.dd.DragDrop = function(id, sGroup, config) {
20189     if (id) {
20190         this.init(id, sGroup, config);
20191     }
20192     
20193 };
20194
20195 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20196
20197     /**
20198      * The id of the element associated with this object.  This is what we
20199      * refer to as the "linked element" because the size and position of
20200      * this element is used to determine when the drag and drop objects have
20201      * interacted.
20202      * @property id
20203      * @type String
20204      */
20205     id: null,
20206
20207     /**
20208      * Configuration attributes passed into the constructor
20209      * @property config
20210      * @type object
20211      */
20212     config: null,
20213
20214     /**
20215      * The id of the element that will be dragged.  By default this is same
20216      * as the linked element , but could be changed to another element. Ex:
20217      * Roo.dd.DDProxy
20218      * @property dragElId
20219      * @type String
20220      * @private
20221      */
20222     dragElId: null,
20223
20224     /**
20225      * the id of the element that initiates the drag operation.  By default
20226      * this is the linked element, but could be changed to be a child of this
20227      * element.  This lets us do things like only starting the drag when the
20228      * header element within the linked html element is clicked.
20229      * @property handleElId
20230      * @type String
20231      * @private
20232      */
20233     handleElId: null,
20234
20235     /**
20236      * An associative array of HTML tags that will be ignored if clicked.
20237      * @property invalidHandleTypes
20238      * @type {string: string}
20239      */
20240     invalidHandleTypes: null,
20241
20242     /**
20243      * An associative array of ids for elements that will be ignored if clicked
20244      * @property invalidHandleIds
20245      * @type {string: string}
20246      */
20247     invalidHandleIds: null,
20248
20249     /**
20250      * An indexted array of css class names for elements that will be ignored
20251      * if clicked.
20252      * @property invalidHandleClasses
20253      * @type string[]
20254      */
20255     invalidHandleClasses: null,
20256
20257     /**
20258      * The linked element's absolute X position at the time the drag was
20259      * started
20260      * @property startPageX
20261      * @type int
20262      * @private
20263      */
20264     startPageX: 0,
20265
20266     /**
20267      * The linked element's absolute X position at the time the drag was
20268      * started
20269      * @property startPageY
20270      * @type int
20271      * @private
20272      */
20273     startPageY: 0,
20274
20275     /**
20276      * The group defines a logical collection of DragDrop objects that are
20277      * related.  Instances only get events when interacting with other
20278      * DragDrop object in the same group.  This lets us define multiple
20279      * groups using a single DragDrop subclass if we want.
20280      * @property groups
20281      * @type {string: string}
20282      */
20283     groups: null,
20284
20285     /**
20286      * Individual drag/drop instances can be locked.  This will prevent
20287      * onmousedown start drag.
20288      * @property locked
20289      * @type boolean
20290      * @private
20291      */
20292     locked: false,
20293
20294     /**
20295      * Lock this instance
20296      * @method lock
20297      */
20298     lock: function() { this.locked = true; },
20299
20300     /**
20301      * Unlock this instace
20302      * @method unlock
20303      */
20304     unlock: function() { this.locked = false; },
20305
20306     /**
20307      * By default, all insances can be a drop target.  This can be disabled by
20308      * setting isTarget to false.
20309      * @method isTarget
20310      * @type boolean
20311      */
20312     isTarget: true,
20313
20314     /**
20315      * The padding configured for this drag and drop object for calculating
20316      * the drop zone intersection with this object.
20317      * @method padding
20318      * @type int[]
20319      */
20320     padding: null,
20321
20322     /**
20323      * Cached reference to the linked element
20324      * @property _domRef
20325      * @private
20326      */
20327     _domRef: null,
20328
20329     /**
20330      * Internal typeof flag
20331      * @property __ygDragDrop
20332      * @private
20333      */
20334     __ygDragDrop: true,
20335
20336     /**
20337      * Set to true when horizontal contraints are applied
20338      * @property constrainX
20339      * @type boolean
20340      * @private
20341      */
20342     constrainX: false,
20343
20344     /**
20345      * Set to true when vertical contraints are applied
20346      * @property constrainY
20347      * @type boolean
20348      * @private
20349      */
20350     constrainY: false,
20351
20352     /**
20353      * The left constraint
20354      * @property minX
20355      * @type int
20356      * @private
20357      */
20358     minX: 0,
20359
20360     /**
20361      * The right constraint
20362      * @property maxX
20363      * @type int
20364      * @private
20365      */
20366     maxX: 0,
20367
20368     /**
20369      * The up constraint
20370      * @property minY
20371      * @type int
20372      * @type int
20373      * @private
20374      */
20375     minY: 0,
20376
20377     /**
20378      * The down constraint
20379      * @property maxY
20380      * @type int
20381      * @private
20382      */
20383     maxY: 0,
20384
20385     /**
20386      * Maintain offsets when we resetconstraints.  Set to true when you want
20387      * the position of the element relative to its parent to stay the same
20388      * when the page changes
20389      *
20390      * @property maintainOffset
20391      * @type boolean
20392      */
20393     maintainOffset: false,
20394
20395     /**
20396      * Array of pixel locations the element will snap to if we specified a
20397      * horizontal graduation/interval.  This array is generated automatically
20398      * when you define a tick interval.
20399      * @property xTicks
20400      * @type int[]
20401      */
20402     xTicks: null,
20403
20404     /**
20405      * Array of pixel locations the element will snap to if we specified a
20406      * vertical graduation/interval.  This array is generated automatically
20407      * when you define a tick interval.
20408      * @property yTicks
20409      * @type int[]
20410      */
20411     yTicks: null,
20412
20413     /**
20414      * By default the drag and drop instance will only respond to the primary
20415      * button click (left button for a right-handed mouse).  Set to true to
20416      * allow drag and drop to start with any mouse click that is propogated
20417      * by the browser
20418      * @property primaryButtonOnly
20419      * @type boolean
20420      */
20421     primaryButtonOnly: true,
20422
20423     /**
20424      * The availabe property is false until the linked dom element is accessible.
20425      * @property available
20426      * @type boolean
20427      */
20428     available: false,
20429
20430     /**
20431      * By default, drags can only be initiated if the mousedown occurs in the
20432      * region the linked element is.  This is done in part to work around a
20433      * bug in some browsers that mis-report the mousedown if the previous
20434      * mouseup happened outside of the window.  This property is set to true
20435      * if outer handles are defined.
20436      *
20437      * @property hasOuterHandles
20438      * @type boolean
20439      * @default false
20440      */
20441     hasOuterHandles: false,
20442
20443     /**
20444      * Code that executes immediately before the startDrag event
20445      * @method b4StartDrag
20446      * @private
20447      */
20448     b4StartDrag: function(x, y) { },
20449
20450     /**
20451      * Abstract method called after a drag/drop object is clicked
20452      * and the drag or mousedown time thresholds have beeen met.
20453      * @method startDrag
20454      * @param {int} X click location
20455      * @param {int} Y click location
20456      */
20457     startDrag: function(x, y) { /* override this */ },
20458
20459     /**
20460      * Code that executes immediately before the onDrag event
20461      * @method b4Drag
20462      * @private
20463      */
20464     b4Drag: function(e) { },
20465
20466     /**
20467      * Abstract method called during the onMouseMove event while dragging an
20468      * object.
20469      * @method onDrag
20470      * @param {Event} e the mousemove event
20471      */
20472     onDrag: function(e) { /* override this */ },
20473
20474     /**
20475      * Abstract method called when this element fist begins hovering over
20476      * another DragDrop obj
20477      * @method onDragEnter
20478      * @param {Event} e the mousemove event
20479      * @param {String|DragDrop[]} id In POINT mode, the element
20480      * id this is hovering over.  In INTERSECT mode, an array of one or more
20481      * dragdrop items being hovered over.
20482      */
20483     onDragEnter: function(e, id) { /* override this */ },
20484
20485     /**
20486      * Code that executes immediately before the onDragOver event
20487      * @method b4DragOver
20488      * @private
20489      */
20490     b4DragOver: function(e) { },
20491
20492     /**
20493      * Abstract method called when this element is hovering over another
20494      * DragDrop obj
20495      * @method onDragOver
20496      * @param {Event} e the mousemove event
20497      * @param {String|DragDrop[]} id In POINT mode, the element
20498      * id this is hovering over.  In INTERSECT mode, an array of dd items
20499      * being hovered over.
20500      */
20501     onDragOver: function(e, id) { /* override this */ },
20502
20503     /**
20504      * Code that executes immediately before the onDragOut event
20505      * @method b4DragOut
20506      * @private
20507      */
20508     b4DragOut: function(e) { },
20509
20510     /**
20511      * Abstract method called when we are no longer hovering over an element
20512      * @method onDragOut
20513      * @param {Event} e the mousemove event
20514      * @param {String|DragDrop[]} id In POINT mode, the element
20515      * id this was hovering over.  In INTERSECT mode, an array of dd items
20516      * that the mouse is no longer over.
20517      */
20518     onDragOut: function(e, id) { /* override this */ },
20519
20520     /**
20521      * Code that executes immediately before the onDragDrop event
20522      * @method b4DragDrop
20523      * @private
20524      */
20525     b4DragDrop: function(e) { },
20526
20527     /**
20528      * Abstract method called when this item is dropped on another DragDrop
20529      * obj
20530      * @method onDragDrop
20531      * @param {Event} e the mouseup event
20532      * @param {String|DragDrop[]} id In POINT mode, the element
20533      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20534      * was dropped on.
20535      */
20536     onDragDrop: function(e, id) { /* override this */ },
20537
20538     /**
20539      * Abstract method called when this item is dropped on an area with no
20540      * drop target
20541      * @method onInvalidDrop
20542      * @param {Event} e the mouseup event
20543      */
20544     onInvalidDrop: function(e) { /* override this */ },
20545
20546     /**
20547      * Code that executes immediately before the endDrag event
20548      * @method b4EndDrag
20549      * @private
20550      */
20551     b4EndDrag: function(e) { },
20552
20553     /**
20554      * Fired when we are done dragging the object
20555      * @method endDrag
20556      * @param {Event} e the mouseup event
20557      */
20558     endDrag: function(e) { /* override this */ },
20559
20560     /**
20561      * Code executed immediately before the onMouseDown event
20562      * @method b4MouseDown
20563      * @param {Event} e the mousedown event
20564      * @private
20565      */
20566     b4MouseDown: function(e) {  },
20567
20568     /**
20569      * Event handler that fires when a drag/drop obj gets a mousedown
20570      * @method onMouseDown
20571      * @param {Event} e the mousedown event
20572      */
20573     onMouseDown: function(e) { /* override this */ },
20574
20575     /**
20576      * Event handler that fires when a drag/drop obj gets a mouseup
20577      * @method onMouseUp
20578      * @param {Event} e the mouseup event
20579      */
20580     onMouseUp: function(e) { /* override this */ },
20581
20582     /**
20583      * Override the onAvailable method to do what is needed after the initial
20584      * position was determined.
20585      * @method onAvailable
20586      */
20587     onAvailable: function () {
20588     },
20589
20590     /*
20591      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20592      * @type Object
20593      */
20594     defaultPadding : {left:0, right:0, top:0, bottom:0},
20595
20596     /*
20597      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20598  *
20599  * Usage:
20600  <pre><code>
20601  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20602                 { dragElId: "existingProxyDiv" });
20603  dd.startDrag = function(){
20604      this.constrainTo("parent-id");
20605  };
20606  </code></pre>
20607  * Or you can initalize it using the {@link Roo.Element} object:
20608  <pre><code>
20609  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20610      startDrag : function(){
20611          this.constrainTo("parent-id");
20612      }
20613  });
20614  </code></pre>
20615      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20616      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20617      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20618      * an object containing the sides to pad. For example: {right:10, bottom:10}
20619      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20620      */
20621     constrainTo : function(constrainTo, pad, inContent){
20622         if(typeof pad == "number"){
20623             pad = {left: pad, right:pad, top:pad, bottom:pad};
20624         }
20625         pad = pad || this.defaultPadding;
20626         var b = Roo.get(this.getEl()).getBox();
20627         var ce = Roo.get(constrainTo);
20628         var s = ce.getScroll();
20629         var c, cd = ce.dom;
20630         if(cd == document.body){
20631             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20632         }else{
20633             xy = ce.getXY();
20634             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20635         }
20636
20637
20638         var topSpace = b.y - c.y;
20639         var leftSpace = b.x - c.x;
20640
20641         this.resetConstraints();
20642         this.setXConstraint(leftSpace - (pad.left||0), // left
20643                 c.width - leftSpace - b.width - (pad.right||0) //right
20644         );
20645         this.setYConstraint(topSpace - (pad.top||0), //top
20646                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20647         );
20648     },
20649
20650     /**
20651      * Returns a reference to the linked element
20652      * @method getEl
20653      * @return {HTMLElement} the html element
20654      */
20655     getEl: function() {
20656         if (!this._domRef) {
20657             this._domRef = Roo.getDom(this.id);
20658         }
20659
20660         return this._domRef;
20661     },
20662
20663     /**
20664      * Returns a reference to the actual element to drag.  By default this is
20665      * the same as the html element, but it can be assigned to another
20666      * element. An example of this can be found in Roo.dd.DDProxy
20667      * @method getDragEl
20668      * @return {HTMLElement} the html element
20669      */
20670     getDragEl: function() {
20671         return Roo.getDom(this.dragElId);
20672     },
20673
20674     /**
20675      * Sets up the DragDrop object.  Must be called in the constructor of any
20676      * Roo.dd.DragDrop subclass
20677      * @method init
20678      * @param id the id of the linked element
20679      * @param {String} sGroup the group of related items
20680      * @param {object} config configuration attributes
20681      */
20682     init: function(id, sGroup, config) {
20683         this.initTarget(id, sGroup, config);
20684         if (!Roo.isTouch) {
20685             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20686         }
20687         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20688         // Event.on(this.id, "selectstart", Event.preventDefault);
20689     },
20690
20691     /**
20692      * Initializes Targeting functionality only... the object does not
20693      * get a mousedown handler.
20694      * @method initTarget
20695      * @param id the id of the linked element
20696      * @param {String} sGroup the group of related items
20697      * @param {object} config configuration attributes
20698      */
20699     initTarget: function(id, sGroup, config) {
20700
20701         // configuration attributes
20702         this.config = config || {};
20703
20704         // create a local reference to the drag and drop manager
20705         this.DDM = Roo.dd.DDM;
20706         // initialize the groups array
20707         this.groups = {};
20708
20709         // assume that we have an element reference instead of an id if the
20710         // parameter is not a string
20711         if (typeof id !== "string") {
20712             id = Roo.id(id);
20713         }
20714
20715         // set the id
20716         this.id = id;
20717
20718         // add to an interaction group
20719         this.addToGroup((sGroup) ? sGroup : "default");
20720
20721         // We don't want to register this as the handle with the manager
20722         // so we just set the id rather than calling the setter.
20723         this.handleElId = id;
20724
20725         // the linked element is the element that gets dragged by default
20726         this.setDragElId(id);
20727
20728         // by default, clicked anchors will not start drag operations.
20729         this.invalidHandleTypes = { A: "A" };
20730         this.invalidHandleIds = {};
20731         this.invalidHandleClasses = [];
20732
20733         this.applyConfig();
20734
20735         this.handleOnAvailable();
20736     },
20737
20738     /**
20739      * Applies the configuration parameters that were passed into the constructor.
20740      * This is supposed to happen at each level through the inheritance chain.  So
20741      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20742      * DragDrop in order to get all of the parameters that are available in
20743      * each object.
20744      * @method applyConfig
20745      */
20746     applyConfig: function() {
20747
20748         // configurable properties:
20749         //    padding, isTarget, maintainOffset, primaryButtonOnly
20750         this.padding           = this.config.padding || [0, 0, 0, 0];
20751         this.isTarget          = (this.config.isTarget !== false);
20752         this.maintainOffset    = (this.config.maintainOffset);
20753         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20754
20755     },
20756
20757     /**
20758      * Executed when the linked element is available
20759      * @method handleOnAvailable
20760      * @private
20761      */
20762     handleOnAvailable: function() {
20763         this.available = true;
20764         this.resetConstraints();
20765         this.onAvailable();
20766     },
20767
20768      /**
20769      * Configures the padding for the target zone in px.  Effectively expands
20770      * (or reduces) the virtual object size for targeting calculations.
20771      * Supports css-style shorthand; if only one parameter is passed, all sides
20772      * will have that padding, and if only two are passed, the top and bottom
20773      * will have the first param, the left and right the second.
20774      * @method setPadding
20775      * @param {int} iTop    Top pad
20776      * @param {int} iRight  Right pad
20777      * @param {int} iBot    Bot pad
20778      * @param {int} iLeft   Left pad
20779      */
20780     setPadding: function(iTop, iRight, iBot, iLeft) {
20781         // this.padding = [iLeft, iRight, iTop, iBot];
20782         if (!iRight && 0 !== iRight) {
20783             this.padding = [iTop, iTop, iTop, iTop];
20784         } else if (!iBot && 0 !== iBot) {
20785             this.padding = [iTop, iRight, iTop, iRight];
20786         } else {
20787             this.padding = [iTop, iRight, iBot, iLeft];
20788         }
20789     },
20790
20791     /**
20792      * Stores the initial placement of the linked element.
20793      * @method setInitialPosition
20794      * @param {int} diffX   the X offset, default 0
20795      * @param {int} diffY   the Y offset, default 0
20796      */
20797     setInitPosition: function(diffX, diffY) {
20798         var el = this.getEl();
20799
20800         if (!this.DDM.verifyEl(el)) {
20801             return;
20802         }
20803
20804         var dx = diffX || 0;
20805         var dy = diffY || 0;
20806
20807         var p = Dom.getXY( el );
20808
20809         this.initPageX = p[0] - dx;
20810         this.initPageY = p[1] - dy;
20811
20812         this.lastPageX = p[0];
20813         this.lastPageY = p[1];
20814
20815
20816         this.setStartPosition(p);
20817     },
20818
20819     /**
20820      * Sets the start position of the element.  This is set when the obj
20821      * is initialized, the reset when a drag is started.
20822      * @method setStartPosition
20823      * @param pos current position (from previous lookup)
20824      * @private
20825      */
20826     setStartPosition: function(pos) {
20827         var p = pos || Dom.getXY( this.getEl() );
20828         this.deltaSetXY = null;
20829
20830         this.startPageX = p[0];
20831         this.startPageY = p[1];
20832     },
20833
20834     /**
20835      * Add this instance to a group of related drag/drop objects.  All
20836      * instances belong to at least one group, and can belong to as many
20837      * groups as needed.
20838      * @method addToGroup
20839      * @param sGroup {string} the name of the group
20840      */
20841     addToGroup: function(sGroup) {
20842         this.groups[sGroup] = true;
20843         this.DDM.regDragDrop(this, sGroup);
20844     },
20845
20846     /**
20847      * Remove's this instance from the supplied interaction group
20848      * @method removeFromGroup
20849      * @param {string}  sGroup  The group to drop
20850      */
20851     removeFromGroup: function(sGroup) {
20852         if (this.groups[sGroup]) {
20853             delete this.groups[sGroup];
20854         }
20855
20856         this.DDM.removeDDFromGroup(this, sGroup);
20857     },
20858
20859     /**
20860      * Allows you to specify that an element other than the linked element
20861      * will be moved with the cursor during a drag
20862      * @method setDragElId
20863      * @param id {string} the id of the element that will be used to initiate the drag
20864      */
20865     setDragElId: function(id) {
20866         this.dragElId = id;
20867     },
20868
20869     /**
20870      * Allows you to specify a child of the linked element that should be
20871      * used to initiate the drag operation.  An example of this would be if
20872      * you have a content div with text and links.  Clicking anywhere in the
20873      * content area would normally start the drag operation.  Use this method
20874      * to specify that an element inside of the content div is the element
20875      * that starts the drag operation.
20876      * @method setHandleElId
20877      * @param id {string} the id of the element that will be used to
20878      * initiate the drag.
20879      */
20880     setHandleElId: function(id) {
20881         if (typeof id !== "string") {
20882             id = Roo.id(id);
20883         }
20884         this.handleElId = id;
20885         this.DDM.regHandle(this.id, id);
20886     },
20887
20888     /**
20889      * Allows you to set an element outside of the linked element as a drag
20890      * handle
20891      * @method setOuterHandleElId
20892      * @param id the id of the element that will be used to initiate the drag
20893      */
20894     setOuterHandleElId: function(id) {
20895         if (typeof id !== "string") {
20896             id = Roo.id(id);
20897         }
20898         Event.on(id, "mousedown",
20899                 this.handleMouseDown, this);
20900         this.setHandleElId(id);
20901
20902         this.hasOuterHandles = true;
20903     },
20904
20905     /**
20906      * Remove all drag and drop hooks for this element
20907      * @method unreg
20908      */
20909     unreg: function() {
20910         Event.un(this.id, "mousedown",
20911                 this.handleMouseDown);
20912         Event.un(this.id, "touchstart",
20913                 this.handleMouseDown);
20914         this._domRef = null;
20915         this.DDM._remove(this);
20916     },
20917
20918     destroy : function(){
20919         this.unreg();
20920     },
20921
20922     /**
20923      * Returns true if this instance is locked, or the drag drop mgr is locked
20924      * (meaning that all drag/drop is disabled on the page.)
20925      * @method isLocked
20926      * @return {boolean} true if this obj or all drag/drop is locked, else
20927      * false
20928      */
20929     isLocked: function() {
20930         return (this.DDM.isLocked() || this.locked);
20931     },
20932
20933     /**
20934      * Fired when this object is clicked
20935      * @method handleMouseDown
20936      * @param {Event} e
20937      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20938      * @private
20939      */
20940     handleMouseDown: function(e, oDD){
20941      
20942         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20943             //Roo.log('not touch/ button !=0');
20944             return;
20945         }
20946         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20947             return; // double touch..
20948         }
20949         
20950
20951         if (this.isLocked()) {
20952             //Roo.log('locked');
20953             return;
20954         }
20955
20956         this.DDM.refreshCache(this.groups);
20957 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20958         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20959         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20960             //Roo.log('no outer handes or not over target');
20961                 // do nothing.
20962         } else {
20963 //            Roo.log('check validator');
20964             if (this.clickValidator(e)) {
20965 //                Roo.log('validate success');
20966                 // set the initial element position
20967                 this.setStartPosition();
20968
20969
20970                 this.b4MouseDown(e);
20971                 this.onMouseDown(e);
20972
20973                 this.DDM.handleMouseDown(e, this);
20974
20975                 this.DDM.stopEvent(e);
20976             } else {
20977
20978
20979             }
20980         }
20981     },
20982
20983     clickValidator: function(e) {
20984         var target = e.getTarget();
20985         return ( this.isValidHandleChild(target) &&
20986                     (this.id == this.handleElId ||
20987                         this.DDM.handleWasClicked(target, this.id)) );
20988     },
20989
20990     /**
20991      * Allows you to specify a tag name that should not start a drag operation
20992      * when clicked.  This is designed to facilitate embedding links within a
20993      * drag handle that do something other than start the drag.
20994      * @method addInvalidHandleType
20995      * @param {string} tagName the type of element to exclude
20996      */
20997     addInvalidHandleType: function(tagName) {
20998         var type = tagName.toUpperCase();
20999         this.invalidHandleTypes[type] = type;
21000     },
21001
21002     /**
21003      * Lets you to specify an element id for a child of a drag handle
21004      * that should not initiate a drag
21005      * @method addInvalidHandleId
21006      * @param {string} id the element id of the element you wish to ignore
21007      */
21008     addInvalidHandleId: function(id) {
21009         if (typeof id !== "string") {
21010             id = Roo.id(id);
21011         }
21012         this.invalidHandleIds[id] = id;
21013     },
21014
21015     /**
21016      * Lets you specify a css class of elements that will not initiate a drag
21017      * @method addInvalidHandleClass
21018      * @param {string} cssClass the class of the elements you wish to ignore
21019      */
21020     addInvalidHandleClass: function(cssClass) {
21021         this.invalidHandleClasses.push(cssClass);
21022     },
21023
21024     /**
21025      * Unsets an excluded tag name set by addInvalidHandleType
21026      * @method removeInvalidHandleType
21027      * @param {string} tagName the type of element to unexclude
21028      */
21029     removeInvalidHandleType: function(tagName) {
21030         var type = tagName.toUpperCase();
21031         // this.invalidHandleTypes[type] = null;
21032         delete this.invalidHandleTypes[type];
21033     },
21034
21035     /**
21036      * Unsets an invalid handle id
21037      * @method removeInvalidHandleId
21038      * @param {string} id the id of the element to re-enable
21039      */
21040     removeInvalidHandleId: function(id) {
21041         if (typeof id !== "string") {
21042             id = Roo.id(id);
21043         }
21044         delete this.invalidHandleIds[id];
21045     },
21046
21047     /**
21048      * Unsets an invalid css class
21049      * @method removeInvalidHandleClass
21050      * @param {string} cssClass the class of the element(s) you wish to
21051      * re-enable
21052      */
21053     removeInvalidHandleClass: function(cssClass) {
21054         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21055             if (this.invalidHandleClasses[i] == cssClass) {
21056                 delete this.invalidHandleClasses[i];
21057             }
21058         }
21059     },
21060
21061     /**
21062      * Checks the tag exclusion list to see if this click should be ignored
21063      * @method isValidHandleChild
21064      * @param {HTMLElement} node the HTMLElement to evaluate
21065      * @return {boolean} true if this is a valid tag type, false if not
21066      */
21067     isValidHandleChild: function(node) {
21068
21069         var valid = true;
21070         // var n = (node.nodeName == "#text") ? node.parentNode : node;
21071         var nodeName;
21072         try {
21073             nodeName = node.nodeName.toUpperCase();
21074         } catch(e) {
21075             nodeName = node.nodeName;
21076         }
21077         valid = valid && !this.invalidHandleTypes[nodeName];
21078         valid = valid && !this.invalidHandleIds[node.id];
21079
21080         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21081             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21082         }
21083
21084
21085         return valid;
21086
21087     },
21088
21089     /**
21090      * Create the array of horizontal tick marks if an interval was specified
21091      * in setXConstraint().
21092      * @method setXTicks
21093      * @private
21094      */
21095     setXTicks: function(iStartX, iTickSize) {
21096         this.xTicks = [];
21097         this.xTickSize = iTickSize;
21098
21099         var tickMap = {};
21100
21101         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21102             if (!tickMap[i]) {
21103                 this.xTicks[this.xTicks.length] = i;
21104                 tickMap[i] = true;
21105             }
21106         }
21107
21108         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21109             if (!tickMap[i]) {
21110                 this.xTicks[this.xTicks.length] = i;
21111                 tickMap[i] = true;
21112             }
21113         }
21114
21115         this.xTicks.sort(this.DDM.numericSort) ;
21116     },
21117
21118     /**
21119      * Create the array of vertical tick marks if an interval was specified in
21120      * setYConstraint().
21121      * @method setYTicks
21122      * @private
21123      */
21124     setYTicks: function(iStartY, iTickSize) {
21125         this.yTicks = [];
21126         this.yTickSize = iTickSize;
21127
21128         var tickMap = {};
21129
21130         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21131             if (!tickMap[i]) {
21132                 this.yTicks[this.yTicks.length] = i;
21133                 tickMap[i] = true;
21134             }
21135         }
21136
21137         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21138             if (!tickMap[i]) {
21139                 this.yTicks[this.yTicks.length] = i;
21140                 tickMap[i] = true;
21141             }
21142         }
21143
21144         this.yTicks.sort(this.DDM.numericSort) ;
21145     },
21146
21147     /**
21148      * By default, the element can be dragged any place on the screen.  Use
21149      * this method to limit the horizontal travel of the element.  Pass in
21150      * 0,0 for the parameters if you want to lock the drag to the y axis.
21151      * @method setXConstraint
21152      * @param {int} iLeft the number of pixels the element can move to the left
21153      * @param {int} iRight the number of pixels the element can move to the
21154      * right
21155      * @param {int} iTickSize optional parameter for specifying that the
21156      * element
21157      * should move iTickSize pixels at a time.
21158      */
21159     setXConstraint: function(iLeft, iRight, iTickSize) {
21160         this.leftConstraint = iLeft;
21161         this.rightConstraint = iRight;
21162
21163         this.minX = this.initPageX - iLeft;
21164         this.maxX = this.initPageX + iRight;
21165         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21166
21167         this.constrainX = true;
21168     },
21169
21170     /**
21171      * Clears any constraints applied to this instance.  Also clears ticks
21172      * since they can't exist independent of a constraint at this time.
21173      * @method clearConstraints
21174      */
21175     clearConstraints: function() {
21176         this.constrainX = false;
21177         this.constrainY = false;
21178         this.clearTicks();
21179     },
21180
21181     /**
21182      * Clears any tick interval defined for this instance
21183      * @method clearTicks
21184      */
21185     clearTicks: function() {
21186         this.xTicks = null;
21187         this.yTicks = null;
21188         this.xTickSize = 0;
21189         this.yTickSize = 0;
21190     },
21191
21192     /**
21193      * By default, the element can be dragged any place on the screen.  Set
21194      * this to limit the vertical travel of the element.  Pass in 0,0 for the
21195      * parameters if you want to lock the drag to the x axis.
21196      * @method setYConstraint
21197      * @param {int} iUp the number of pixels the element can move up
21198      * @param {int} iDown the number of pixels the element can move down
21199      * @param {int} iTickSize optional parameter for specifying that the
21200      * element should move iTickSize pixels at a time.
21201      */
21202     setYConstraint: function(iUp, iDown, iTickSize) {
21203         this.topConstraint = iUp;
21204         this.bottomConstraint = iDown;
21205
21206         this.minY = this.initPageY - iUp;
21207         this.maxY = this.initPageY + iDown;
21208         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21209
21210         this.constrainY = true;
21211
21212     },
21213
21214     /**
21215      * resetConstraints must be called if you manually reposition a dd element.
21216      * @method resetConstraints
21217      * @param {boolean} maintainOffset
21218      */
21219     resetConstraints: function() {
21220
21221
21222         // Maintain offsets if necessary
21223         if (this.initPageX || this.initPageX === 0) {
21224             // figure out how much this thing has moved
21225             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21226             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21227
21228             this.setInitPosition(dx, dy);
21229
21230         // This is the first time we have detected the element's position
21231         } else {
21232             this.setInitPosition();
21233         }
21234
21235         if (this.constrainX) {
21236             this.setXConstraint( this.leftConstraint,
21237                                  this.rightConstraint,
21238                                  this.xTickSize        );
21239         }
21240
21241         if (this.constrainY) {
21242             this.setYConstraint( this.topConstraint,
21243                                  this.bottomConstraint,
21244                                  this.yTickSize         );
21245         }
21246     },
21247
21248     /**
21249      * Normally the drag element is moved pixel by pixel, but we can specify
21250      * that it move a number of pixels at a time.  This method resolves the
21251      * location when we have it set up like this.
21252      * @method getTick
21253      * @param {int} val where we want to place the object
21254      * @param {int[]} tickArray sorted array of valid points
21255      * @return {int} the closest tick
21256      * @private
21257      */
21258     getTick: function(val, tickArray) {
21259
21260         if (!tickArray) {
21261             // If tick interval is not defined, it is effectively 1 pixel,
21262             // so we return the value passed to us.
21263             return val;
21264         } else if (tickArray[0] >= val) {
21265             // The value is lower than the first tick, so we return the first
21266             // tick.
21267             return tickArray[0];
21268         } else {
21269             for (var i=0, len=tickArray.length; i<len; ++i) {
21270                 var next = i + 1;
21271                 if (tickArray[next] && tickArray[next] >= val) {
21272                     var diff1 = val - tickArray[i];
21273                     var diff2 = tickArray[next] - val;
21274                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21275                 }
21276             }
21277
21278             // The value is larger than the last tick, so we return the last
21279             // tick.
21280             return tickArray[tickArray.length - 1];
21281         }
21282     },
21283
21284     /**
21285      * toString method
21286      * @method toString
21287      * @return {string} string representation of the dd obj
21288      */
21289     toString: function() {
21290         return ("DragDrop " + this.id);
21291     }
21292
21293 });
21294
21295 })();
21296 /*
21297  * Based on:
21298  * Ext JS Library 1.1.1
21299  * Copyright(c) 2006-2007, Ext JS, LLC.
21300  *
21301  * Originally Released Under LGPL - original licence link has changed is not relivant.
21302  *
21303  * Fork - LGPL
21304  * <script type="text/javascript">
21305  */
21306
21307
21308 /**
21309  * The drag and drop utility provides a framework for building drag and drop
21310  * applications.  In addition to enabling drag and drop for specific elements,
21311  * the drag and drop elements are tracked by the manager class, and the
21312  * interactions between the various elements are tracked during the drag and
21313  * the implementing code is notified about these important moments.
21314  */
21315
21316 // Only load the library once.  Rewriting the manager class would orphan
21317 // existing drag and drop instances.
21318 if (!Roo.dd.DragDropMgr) {
21319
21320 /**
21321  * @class Roo.dd.DragDropMgr
21322  * DragDropMgr is a singleton that tracks the element interaction for
21323  * all DragDrop items in the window.  Generally, you will not call
21324  * this class directly, but it does have helper methods that could
21325  * be useful in your DragDrop implementations.
21326  * @static
21327  */
21328 Roo.dd.DragDropMgr = function() {
21329
21330     var Event = Roo.EventManager;
21331
21332     return {
21333
21334         /**
21335          * Two dimensional Array of registered DragDrop objects.  The first
21336          * dimension is the DragDrop item group, the second the DragDrop
21337          * object.
21338          * @property ids
21339          * @type {string: string}
21340          * @private
21341          * @static
21342          */
21343         ids: {},
21344
21345         /**
21346          * Array of element ids defined as drag handles.  Used to determine
21347          * if the element that generated the mousedown event is actually the
21348          * handle and not the html element itself.
21349          * @property handleIds
21350          * @type {string: string}
21351          * @private
21352          * @static
21353          */
21354         handleIds: {},
21355
21356         /**
21357          * the DragDrop object that is currently being dragged
21358          * @property dragCurrent
21359          * @type DragDrop
21360          * @private
21361          * @static
21362          **/
21363         dragCurrent: null,
21364
21365         /**
21366          * the DragDrop object(s) that are being hovered over
21367          * @property dragOvers
21368          * @type Array
21369          * @private
21370          * @static
21371          */
21372         dragOvers: {},
21373
21374         /**
21375          * the X distance between the cursor and the object being dragged
21376          * @property deltaX
21377          * @type int
21378          * @private
21379          * @static
21380          */
21381         deltaX: 0,
21382
21383         /**
21384          * the Y distance between the cursor and the object being dragged
21385          * @property deltaY
21386          * @type int
21387          * @private
21388          * @static
21389          */
21390         deltaY: 0,
21391
21392         /**
21393          * Flag to determine if we should prevent the default behavior of the
21394          * events we define. By default this is true, but this can be set to
21395          * false if you need the default behavior (not recommended)
21396          * @property preventDefault
21397          * @type boolean
21398          * @static
21399          */
21400         preventDefault: true,
21401
21402         /**
21403          * Flag to determine if we should stop the propagation of the events
21404          * we generate. This is true by default but you may want to set it to
21405          * false if the html element contains other features that require the
21406          * mouse click.
21407          * @property stopPropagation
21408          * @type boolean
21409          * @static
21410          */
21411         stopPropagation: true,
21412
21413         /**
21414          * Internal flag that is set to true when drag and drop has been
21415          * intialized
21416          * @property initialized
21417          * @private
21418          * @static
21419          */
21420         initalized: false,
21421
21422         /**
21423          * All drag and drop can be disabled.
21424          * @property locked
21425          * @private
21426          * @static
21427          */
21428         locked: false,
21429
21430         /**
21431          * Called the first time an element is registered.
21432          * @method init
21433          * @private
21434          * @static
21435          */
21436         init: function() {
21437             this.initialized = true;
21438         },
21439
21440         /**
21441          * In point mode, drag and drop interaction is defined by the
21442          * location of the cursor during the drag/drop
21443          * @property POINT
21444          * @type int
21445          * @static
21446          */
21447         POINT: 0,
21448
21449         /**
21450          * In intersect mode, drag and drop interactio nis defined by the
21451          * overlap of two or more drag and drop objects.
21452          * @property INTERSECT
21453          * @type int
21454          * @static
21455          */
21456         INTERSECT: 1,
21457
21458         /**
21459          * The current drag and drop mode.  Default: POINT
21460          * @property mode
21461          * @type int
21462          * @static
21463          */
21464         mode: 0,
21465
21466         /**
21467          * Runs method on all drag and drop objects
21468          * @method _execOnAll
21469          * @private
21470          * @static
21471          */
21472         _execOnAll: function(sMethod, args) {
21473             for (var i in this.ids) {
21474                 for (var j in this.ids[i]) {
21475                     var oDD = this.ids[i][j];
21476                     if (! this.isTypeOfDD(oDD)) {
21477                         continue;
21478                     }
21479                     oDD[sMethod].apply(oDD, args);
21480                 }
21481             }
21482         },
21483
21484         /**
21485          * Drag and drop initialization.  Sets up the global event handlers
21486          * @method _onLoad
21487          * @private
21488          * @static
21489          */
21490         _onLoad: function() {
21491
21492             this.init();
21493
21494             if (!Roo.isTouch) {
21495                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21496                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21497             }
21498             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21499             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21500             
21501             Event.on(window,   "unload",    this._onUnload, this, true);
21502             Event.on(window,   "resize",    this._onResize, this, true);
21503             // Event.on(window,   "mouseout",    this._test);
21504
21505         },
21506
21507         /**
21508          * Reset constraints on all drag and drop objs
21509          * @method _onResize
21510          * @private
21511          * @static
21512          */
21513         _onResize: function(e) {
21514             this._execOnAll("resetConstraints", []);
21515         },
21516
21517         /**
21518          * Lock all drag and drop functionality
21519          * @method lock
21520          * @static
21521          */
21522         lock: function() { this.locked = true; },
21523
21524         /**
21525          * Unlock all drag and drop functionality
21526          * @method unlock
21527          * @static
21528          */
21529         unlock: function() { this.locked = false; },
21530
21531         /**
21532          * Is drag and drop locked?
21533          * @method isLocked
21534          * @return {boolean} True if drag and drop is locked, false otherwise.
21535          * @static
21536          */
21537         isLocked: function() { return this.locked; },
21538
21539         /**
21540          * Location cache that is set for all drag drop objects when a drag is
21541          * initiated, cleared when the drag is finished.
21542          * @property locationCache
21543          * @private
21544          * @static
21545          */
21546         locationCache: {},
21547
21548         /**
21549          * Set useCache to false if you want to force object the lookup of each
21550          * drag and drop linked element constantly during a drag.
21551          * @property useCache
21552          * @type boolean
21553          * @static
21554          */
21555         useCache: true,
21556
21557         /**
21558          * The number of pixels that the mouse needs to move after the
21559          * mousedown before the drag is initiated.  Default=3;
21560          * @property clickPixelThresh
21561          * @type int
21562          * @static
21563          */
21564         clickPixelThresh: 3,
21565
21566         /**
21567          * The number of milliseconds after the mousedown event to initiate the
21568          * drag if we don't get a mouseup event. Default=1000
21569          * @property clickTimeThresh
21570          * @type int
21571          * @static
21572          */
21573         clickTimeThresh: 350,
21574
21575         /**
21576          * Flag that indicates that either the drag pixel threshold or the
21577          * mousdown time threshold has been met
21578          * @property dragThreshMet
21579          * @type boolean
21580          * @private
21581          * @static
21582          */
21583         dragThreshMet: false,
21584
21585         /**
21586          * Timeout used for the click time threshold
21587          * @property clickTimeout
21588          * @type Object
21589          * @private
21590          * @static
21591          */
21592         clickTimeout: null,
21593
21594         /**
21595          * The X position of the mousedown event stored for later use when a
21596          * drag threshold is met.
21597          * @property startX
21598          * @type int
21599          * @private
21600          * @static
21601          */
21602         startX: 0,
21603
21604         /**
21605          * The Y position of the mousedown event stored for later use when a
21606          * drag threshold is met.
21607          * @property startY
21608          * @type int
21609          * @private
21610          * @static
21611          */
21612         startY: 0,
21613
21614         /**
21615          * Each DragDrop instance must be registered with the DragDropMgr.
21616          * This is executed in DragDrop.init()
21617          * @method regDragDrop
21618          * @param {DragDrop} oDD the DragDrop object to register
21619          * @param {String} sGroup the name of the group this element belongs to
21620          * @static
21621          */
21622         regDragDrop: function(oDD, sGroup) {
21623             if (!this.initialized) { this.init(); }
21624
21625             if (!this.ids[sGroup]) {
21626                 this.ids[sGroup] = {};
21627             }
21628             this.ids[sGroup][oDD.id] = oDD;
21629         },
21630
21631         /**
21632          * Removes the supplied dd instance from the supplied group. Executed
21633          * by DragDrop.removeFromGroup, so don't call this function directly.
21634          * @method removeDDFromGroup
21635          * @private
21636          * @static
21637          */
21638         removeDDFromGroup: function(oDD, sGroup) {
21639             if (!this.ids[sGroup]) {
21640                 this.ids[sGroup] = {};
21641             }
21642
21643             var obj = this.ids[sGroup];
21644             if (obj && obj[oDD.id]) {
21645                 delete obj[oDD.id];
21646             }
21647         },
21648
21649         /**
21650          * Unregisters a drag and drop item.  This is executed in
21651          * DragDrop.unreg, use that method instead of calling this directly.
21652          * @method _remove
21653          * @private
21654          * @static
21655          */
21656         _remove: function(oDD) {
21657             for (var g in oDD.groups) {
21658                 if (g && this.ids[g][oDD.id]) {
21659                     delete this.ids[g][oDD.id];
21660                 }
21661             }
21662             delete this.handleIds[oDD.id];
21663         },
21664
21665         /**
21666          * Each DragDrop handle element must be registered.  This is done
21667          * automatically when executing DragDrop.setHandleElId()
21668          * @method regHandle
21669          * @param {String} sDDId the DragDrop id this element is a handle for
21670          * @param {String} sHandleId the id of the element that is the drag
21671          * handle
21672          * @static
21673          */
21674         regHandle: function(sDDId, sHandleId) {
21675             if (!this.handleIds[sDDId]) {
21676                 this.handleIds[sDDId] = {};
21677             }
21678             this.handleIds[sDDId][sHandleId] = sHandleId;
21679         },
21680
21681         /**
21682          * Utility function to determine if a given element has been
21683          * registered as a drag drop item.
21684          * @method isDragDrop
21685          * @param {String} id the element id to check
21686          * @return {boolean} true if this element is a DragDrop item,
21687          * false otherwise
21688          * @static
21689          */
21690         isDragDrop: function(id) {
21691             return ( this.getDDById(id) ) ? true : false;
21692         },
21693
21694         /**
21695          * Returns the drag and drop instances that are in all groups the
21696          * passed in instance belongs to.
21697          * @method getRelated
21698          * @param {DragDrop} p_oDD the obj to get related data for
21699          * @param {boolean} bTargetsOnly if true, only return targetable objs
21700          * @return {DragDrop[]} the related instances
21701          * @static
21702          */
21703         getRelated: function(p_oDD, bTargetsOnly) {
21704             var oDDs = [];
21705             for (var i in p_oDD.groups) {
21706                 for (j in this.ids[i]) {
21707                     var dd = this.ids[i][j];
21708                     if (! this.isTypeOfDD(dd)) {
21709                         continue;
21710                     }
21711                     if (!bTargetsOnly || dd.isTarget) {
21712                         oDDs[oDDs.length] = dd;
21713                     }
21714                 }
21715             }
21716
21717             return oDDs;
21718         },
21719
21720         /**
21721          * Returns true if the specified dd target is a legal target for
21722          * the specifice drag obj
21723          * @method isLegalTarget
21724          * @param {DragDrop} the drag obj
21725          * @param {DragDrop} the target
21726          * @return {boolean} true if the target is a legal target for the
21727          * dd obj
21728          * @static
21729          */
21730         isLegalTarget: function (oDD, oTargetDD) {
21731             var targets = this.getRelated(oDD, true);
21732             for (var i=0, len=targets.length;i<len;++i) {
21733                 if (targets[i].id == oTargetDD.id) {
21734                     return true;
21735                 }
21736             }
21737
21738             return false;
21739         },
21740
21741         /**
21742          * My goal is to be able to transparently determine if an object is
21743          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21744          * returns "object", oDD.constructor.toString() always returns
21745          * "DragDrop" and not the name of the subclass.  So for now it just
21746          * evaluates a well-known variable in DragDrop.
21747          * @method isTypeOfDD
21748          * @param {Object} the object to evaluate
21749          * @return {boolean} true if typeof oDD = DragDrop
21750          * @static
21751          */
21752         isTypeOfDD: function (oDD) {
21753             return (oDD && oDD.__ygDragDrop);
21754         },
21755
21756         /**
21757          * Utility function to determine if a given element has been
21758          * registered as a drag drop handle for the given Drag Drop object.
21759          * @method isHandle
21760          * @param {String} id the element id to check
21761          * @return {boolean} true if this element is a DragDrop handle, false
21762          * otherwise
21763          * @static
21764          */
21765         isHandle: function(sDDId, sHandleId) {
21766             return ( this.handleIds[sDDId] &&
21767                             this.handleIds[sDDId][sHandleId] );
21768         },
21769
21770         /**
21771          * Returns the DragDrop instance for a given id
21772          * @method getDDById
21773          * @param {String} id the id of the DragDrop object
21774          * @return {DragDrop} the drag drop object, null if it is not found
21775          * @static
21776          */
21777         getDDById: function(id) {
21778             for (var i in this.ids) {
21779                 if (this.ids[i][id]) {
21780                     return this.ids[i][id];
21781                 }
21782             }
21783             return null;
21784         },
21785
21786         /**
21787          * Fired after a registered DragDrop object gets the mousedown event.
21788          * Sets up the events required to track the object being dragged
21789          * @method handleMouseDown
21790          * @param {Event} e the event
21791          * @param oDD the DragDrop object being dragged
21792          * @private
21793          * @static
21794          */
21795         handleMouseDown: function(e, oDD) {
21796             if(Roo.QuickTips){
21797                 Roo.QuickTips.disable();
21798             }
21799             this.currentTarget = e.getTarget();
21800
21801             this.dragCurrent = oDD;
21802
21803             var el = oDD.getEl();
21804
21805             // track start position
21806             this.startX = e.getPageX();
21807             this.startY = e.getPageY();
21808
21809             this.deltaX = this.startX - el.offsetLeft;
21810             this.deltaY = this.startY - el.offsetTop;
21811
21812             this.dragThreshMet = false;
21813
21814             this.clickTimeout = setTimeout(
21815                     function() {
21816                         var DDM = Roo.dd.DDM;
21817                         DDM.startDrag(DDM.startX, DDM.startY);
21818                     },
21819                     this.clickTimeThresh );
21820         },
21821
21822         /**
21823          * Fired when either the drag pixel threshol or the mousedown hold
21824          * time threshold has been met.
21825          * @method startDrag
21826          * @param x {int} the X position of the original mousedown
21827          * @param y {int} the Y position of the original mousedown
21828          * @static
21829          */
21830         startDrag: function(x, y) {
21831             clearTimeout(this.clickTimeout);
21832             if (this.dragCurrent) {
21833                 this.dragCurrent.b4StartDrag(x, y);
21834                 this.dragCurrent.startDrag(x, y);
21835             }
21836             this.dragThreshMet = true;
21837         },
21838
21839         /**
21840          * Internal function to handle the mouseup event.  Will be invoked
21841          * from the context of the document.
21842          * @method handleMouseUp
21843          * @param {Event} e the event
21844          * @private
21845          * @static
21846          */
21847         handleMouseUp: function(e) {
21848
21849             if(Roo.QuickTips){
21850                 Roo.QuickTips.enable();
21851             }
21852             if (! this.dragCurrent) {
21853                 return;
21854             }
21855
21856             clearTimeout(this.clickTimeout);
21857
21858             if (this.dragThreshMet) {
21859                 this.fireEvents(e, true);
21860             } else {
21861             }
21862
21863             this.stopDrag(e);
21864
21865             this.stopEvent(e);
21866         },
21867
21868         /**
21869          * Utility to stop event propagation and event default, if these
21870          * features are turned on.
21871          * @method stopEvent
21872          * @param {Event} e the event as returned by this.getEvent()
21873          * @static
21874          */
21875         stopEvent: function(e){
21876             if(this.stopPropagation) {
21877                 e.stopPropagation();
21878             }
21879
21880             if (this.preventDefault) {
21881                 e.preventDefault();
21882             }
21883         },
21884
21885         /**
21886          * Internal function to clean up event handlers after the drag
21887          * operation is complete
21888          * @method stopDrag
21889          * @param {Event} e the event
21890          * @private
21891          * @static
21892          */
21893         stopDrag: function(e) {
21894             // Fire the drag end event for the item that was dragged
21895             if (this.dragCurrent) {
21896                 if (this.dragThreshMet) {
21897                     this.dragCurrent.b4EndDrag(e);
21898                     this.dragCurrent.endDrag(e);
21899                 }
21900
21901                 this.dragCurrent.onMouseUp(e);
21902             }
21903
21904             this.dragCurrent = null;
21905             this.dragOvers = {};
21906         },
21907
21908         /**
21909          * Internal function to handle the mousemove event.  Will be invoked
21910          * from the context of the html element.
21911          *
21912          * @TODO figure out what we can do about mouse events lost when the
21913          * user drags objects beyond the window boundary.  Currently we can
21914          * detect this in internet explorer by verifying that the mouse is
21915          * down during the mousemove event.  Firefox doesn't give us the
21916          * button state on the mousemove event.
21917          * @method handleMouseMove
21918          * @param {Event} e the event
21919          * @private
21920          * @static
21921          */
21922         handleMouseMove: function(e) {
21923             if (! this.dragCurrent) {
21924                 return true;
21925             }
21926
21927             // var button = e.which || e.button;
21928
21929             // check for IE mouseup outside of page boundary
21930             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21931                 this.stopEvent(e);
21932                 return this.handleMouseUp(e);
21933             }
21934
21935             if (!this.dragThreshMet) {
21936                 var diffX = Math.abs(this.startX - e.getPageX());
21937                 var diffY = Math.abs(this.startY - e.getPageY());
21938                 if (diffX > this.clickPixelThresh ||
21939                             diffY > this.clickPixelThresh) {
21940                     this.startDrag(this.startX, this.startY);
21941                 }
21942             }
21943
21944             if (this.dragThreshMet) {
21945                 this.dragCurrent.b4Drag(e);
21946                 this.dragCurrent.onDrag(e);
21947                 if(!this.dragCurrent.moveOnly){
21948                     this.fireEvents(e, false);
21949                 }
21950             }
21951
21952             this.stopEvent(e);
21953
21954             return true;
21955         },
21956
21957         /**
21958          * Iterates over all of the DragDrop elements to find ones we are
21959          * hovering over or dropping on
21960          * @method fireEvents
21961          * @param {Event} e the event
21962          * @param {boolean} isDrop is this a drop op or a mouseover op?
21963          * @private
21964          * @static
21965          */
21966         fireEvents: function(e, isDrop) {
21967             var dc = this.dragCurrent;
21968
21969             // If the user did the mouse up outside of the window, we could
21970             // get here even though we have ended the drag.
21971             if (!dc || dc.isLocked()) {
21972                 return;
21973             }
21974
21975             var pt = e.getPoint();
21976
21977             // cache the previous dragOver array
21978             var oldOvers = [];
21979
21980             var outEvts   = [];
21981             var overEvts  = [];
21982             var dropEvts  = [];
21983             var enterEvts = [];
21984
21985             // Check to see if the object(s) we were hovering over is no longer
21986             // being hovered over so we can fire the onDragOut event
21987             for (var i in this.dragOvers) {
21988
21989                 var ddo = this.dragOvers[i];
21990
21991                 if (! this.isTypeOfDD(ddo)) {
21992                     continue;
21993                 }
21994
21995                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21996                     outEvts.push( ddo );
21997                 }
21998
21999                 oldOvers[i] = true;
22000                 delete this.dragOvers[i];
22001             }
22002
22003             for (var sGroup in dc.groups) {
22004
22005                 if ("string" != typeof sGroup) {
22006                     continue;
22007                 }
22008
22009                 for (i in this.ids[sGroup]) {
22010                     var oDD = this.ids[sGroup][i];
22011                     if (! this.isTypeOfDD(oDD)) {
22012                         continue;
22013                     }
22014
22015                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
22016                         if (this.isOverTarget(pt, oDD, this.mode)) {
22017                             // look for drop interactions
22018                             if (isDrop) {
22019                                 dropEvts.push( oDD );
22020                             // look for drag enter and drag over interactions
22021                             } else {
22022
22023                                 // initial drag over: dragEnter fires
22024                                 if (!oldOvers[oDD.id]) {
22025                                     enterEvts.push( oDD );
22026                                 // subsequent drag overs: dragOver fires
22027                                 } else {
22028                                     overEvts.push( oDD );
22029                                 }
22030
22031                                 this.dragOvers[oDD.id] = oDD;
22032                             }
22033                         }
22034                     }
22035                 }
22036             }
22037
22038             if (this.mode) {
22039                 if (outEvts.length) {
22040                     dc.b4DragOut(e, outEvts);
22041                     dc.onDragOut(e, outEvts);
22042                 }
22043
22044                 if (enterEvts.length) {
22045                     dc.onDragEnter(e, enterEvts);
22046                 }
22047
22048                 if (overEvts.length) {
22049                     dc.b4DragOver(e, overEvts);
22050                     dc.onDragOver(e, overEvts);
22051                 }
22052
22053                 if (dropEvts.length) {
22054                     dc.b4DragDrop(e, dropEvts);
22055                     dc.onDragDrop(e, dropEvts);
22056                 }
22057
22058             } else {
22059                 // fire dragout events
22060                 var len = 0;
22061                 for (i=0, len=outEvts.length; i<len; ++i) {
22062                     dc.b4DragOut(e, outEvts[i].id);
22063                     dc.onDragOut(e, outEvts[i].id);
22064                 }
22065
22066                 // fire enter events
22067                 for (i=0,len=enterEvts.length; i<len; ++i) {
22068                     // dc.b4DragEnter(e, oDD.id);
22069                     dc.onDragEnter(e, enterEvts[i].id);
22070                 }
22071
22072                 // fire over events
22073                 for (i=0,len=overEvts.length; i<len; ++i) {
22074                     dc.b4DragOver(e, overEvts[i].id);
22075                     dc.onDragOver(e, overEvts[i].id);
22076                 }
22077
22078                 // fire drop events
22079                 for (i=0, len=dropEvts.length; i<len; ++i) {
22080                     dc.b4DragDrop(e, dropEvts[i].id);
22081                     dc.onDragDrop(e, dropEvts[i].id);
22082                 }
22083
22084             }
22085
22086             // notify about a drop that did not find a target
22087             if (isDrop && !dropEvts.length) {
22088                 dc.onInvalidDrop(e);
22089             }
22090
22091         },
22092
22093         /**
22094          * Helper function for getting the best match from the list of drag
22095          * and drop objects returned by the drag and drop events when we are
22096          * in INTERSECT mode.  It returns either the first object that the
22097          * cursor is over, or the object that has the greatest overlap with
22098          * the dragged element.
22099          * @method getBestMatch
22100          * @param  {DragDrop[]} dds The array of drag and drop objects
22101          * targeted
22102          * @return {DragDrop}       The best single match
22103          * @static
22104          */
22105         getBestMatch: function(dds) {
22106             var winner = null;
22107             // Return null if the input is not what we expect
22108             //if (!dds || !dds.length || dds.length == 0) {
22109                // winner = null;
22110             // If there is only one item, it wins
22111             //} else if (dds.length == 1) {
22112
22113             var len = dds.length;
22114
22115             if (len == 1) {
22116                 winner = dds[0];
22117             } else {
22118                 // Loop through the targeted items
22119                 for (var i=0; i<len; ++i) {
22120                     var dd = dds[i];
22121                     // If the cursor is over the object, it wins.  If the
22122                     // cursor is over multiple matches, the first one we come
22123                     // to wins.
22124                     if (dd.cursorIsOver) {
22125                         winner = dd;
22126                         break;
22127                     // Otherwise the object with the most overlap wins
22128                     } else {
22129                         if (!winner ||
22130                             winner.overlap.getArea() < dd.overlap.getArea()) {
22131                             winner = dd;
22132                         }
22133                     }
22134                 }
22135             }
22136
22137             return winner;
22138         },
22139
22140         /**
22141          * Refreshes the cache of the top-left and bottom-right points of the
22142          * drag and drop objects in the specified group(s).  This is in the
22143          * format that is stored in the drag and drop instance, so typical
22144          * usage is:
22145          * <code>
22146          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22147          * </code>
22148          * Alternatively:
22149          * <code>
22150          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22151          * </code>
22152          * @TODO this really should be an indexed array.  Alternatively this
22153          * method could accept both.
22154          * @method refreshCache
22155          * @param {Object} groups an associative array of groups to refresh
22156          * @static
22157          */
22158         refreshCache: function(groups) {
22159             for (var sGroup in groups) {
22160                 if ("string" != typeof sGroup) {
22161                     continue;
22162                 }
22163                 for (var i in this.ids[sGroup]) {
22164                     var oDD = this.ids[sGroup][i];
22165
22166                     if (this.isTypeOfDD(oDD)) {
22167                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22168                         var loc = this.getLocation(oDD);
22169                         if (loc) {
22170                             this.locationCache[oDD.id] = loc;
22171                         } else {
22172                             delete this.locationCache[oDD.id];
22173                             // this will unregister the drag and drop object if
22174                             // the element is not in a usable state
22175                             // oDD.unreg();
22176                         }
22177                     }
22178                 }
22179             }
22180         },
22181
22182         /**
22183          * This checks to make sure an element exists and is in the DOM.  The
22184          * main purpose is to handle cases where innerHTML is used to remove
22185          * drag and drop objects from the DOM.  IE provides an 'unspecified
22186          * error' when trying to access the offsetParent of such an element
22187          * @method verifyEl
22188          * @param {HTMLElement} el the element to check
22189          * @return {boolean} true if the element looks usable
22190          * @static
22191          */
22192         verifyEl: function(el) {
22193             if (el) {
22194                 var parent;
22195                 if(Roo.isIE){
22196                     try{
22197                         parent = el.offsetParent;
22198                     }catch(e){}
22199                 }else{
22200                     parent = el.offsetParent;
22201                 }
22202                 if (parent) {
22203                     return true;
22204                 }
22205             }
22206
22207             return false;
22208         },
22209
22210         /**
22211          * Returns a Region object containing the drag and drop element's position
22212          * and size, including the padding configured for it
22213          * @method getLocation
22214          * @param {DragDrop} oDD the drag and drop object to get the
22215          *                       location for
22216          * @return {Roo.lib.Region} a Region object representing the total area
22217          *                             the element occupies, including any padding
22218          *                             the instance is configured for.
22219          * @static
22220          */
22221         getLocation: function(oDD) {
22222             if (! this.isTypeOfDD(oDD)) {
22223                 return null;
22224             }
22225
22226             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22227
22228             try {
22229                 pos= Roo.lib.Dom.getXY(el);
22230             } catch (e) { }
22231
22232             if (!pos) {
22233                 return null;
22234             }
22235
22236             x1 = pos[0];
22237             x2 = x1 + el.offsetWidth;
22238             y1 = pos[1];
22239             y2 = y1 + el.offsetHeight;
22240
22241             t = y1 - oDD.padding[0];
22242             r = x2 + oDD.padding[1];
22243             b = y2 + oDD.padding[2];
22244             l = x1 - oDD.padding[3];
22245
22246             return new Roo.lib.Region( t, r, b, l );
22247         },
22248
22249         /**
22250          * Checks the cursor location to see if it over the target
22251          * @method isOverTarget
22252          * @param {Roo.lib.Point} pt The point to evaluate
22253          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22254          * @return {boolean} true if the mouse is over the target
22255          * @private
22256          * @static
22257          */
22258         isOverTarget: function(pt, oTarget, intersect) {
22259             // use cache if available
22260             var loc = this.locationCache[oTarget.id];
22261             if (!loc || !this.useCache) {
22262                 loc = this.getLocation(oTarget);
22263                 this.locationCache[oTarget.id] = loc;
22264
22265             }
22266
22267             if (!loc) {
22268                 return false;
22269             }
22270
22271             oTarget.cursorIsOver = loc.contains( pt );
22272
22273             // DragDrop is using this as a sanity check for the initial mousedown
22274             // in this case we are done.  In POINT mode, if the drag obj has no
22275             // contraints, we are also done. Otherwise we need to evaluate the
22276             // location of the target as related to the actual location of the
22277             // dragged element.
22278             var dc = this.dragCurrent;
22279             if (!dc || !dc.getTargetCoord ||
22280                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22281                 return oTarget.cursorIsOver;
22282             }
22283
22284             oTarget.overlap = null;
22285
22286             // Get the current location of the drag element, this is the
22287             // location of the mouse event less the delta that represents
22288             // where the original mousedown happened on the element.  We
22289             // need to consider constraints and ticks as well.
22290             var pos = dc.getTargetCoord(pt.x, pt.y);
22291
22292             var el = dc.getDragEl();
22293             var curRegion = new Roo.lib.Region( pos.y,
22294                                                    pos.x + el.offsetWidth,
22295                                                    pos.y + el.offsetHeight,
22296                                                    pos.x );
22297
22298             var overlap = curRegion.intersect(loc);
22299
22300             if (overlap) {
22301                 oTarget.overlap = overlap;
22302                 return (intersect) ? true : oTarget.cursorIsOver;
22303             } else {
22304                 return false;
22305             }
22306         },
22307
22308         /**
22309          * unload event handler
22310          * @method _onUnload
22311          * @private
22312          * @static
22313          */
22314         _onUnload: function(e, me) {
22315             Roo.dd.DragDropMgr.unregAll();
22316         },
22317
22318         /**
22319          * Cleans up the drag and drop events and objects.
22320          * @method unregAll
22321          * @private
22322          * @static
22323          */
22324         unregAll: function() {
22325
22326             if (this.dragCurrent) {
22327                 this.stopDrag();
22328                 this.dragCurrent = null;
22329             }
22330
22331             this._execOnAll("unreg", []);
22332
22333             for (i in this.elementCache) {
22334                 delete this.elementCache[i];
22335             }
22336
22337             this.elementCache = {};
22338             this.ids = {};
22339         },
22340
22341         /**
22342          * A cache of DOM elements
22343          * @property elementCache
22344          * @private
22345          * @static
22346          */
22347         elementCache: {},
22348
22349         /**
22350          * Get the wrapper for the DOM element specified
22351          * @method getElWrapper
22352          * @param {String} id the id of the element to get
22353          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22354          * @private
22355          * @deprecated This wrapper isn't that useful
22356          * @static
22357          */
22358         getElWrapper: function(id) {
22359             var oWrapper = this.elementCache[id];
22360             if (!oWrapper || !oWrapper.el) {
22361                 oWrapper = this.elementCache[id] =
22362                     new this.ElementWrapper(Roo.getDom(id));
22363             }
22364             return oWrapper;
22365         },
22366
22367         /**
22368          * Returns the actual DOM element
22369          * @method getElement
22370          * @param {String} id the id of the elment to get
22371          * @return {Object} The element
22372          * @deprecated use Roo.getDom instead
22373          * @static
22374          */
22375         getElement: function(id) {
22376             return Roo.getDom(id);
22377         },
22378
22379         /**
22380          * Returns the style property for the DOM element (i.e.,
22381          * document.getElById(id).style)
22382          * @method getCss
22383          * @param {String} id the id of the elment to get
22384          * @return {Object} The style property of the element
22385          * @deprecated use Roo.getDom instead
22386          * @static
22387          */
22388         getCss: function(id) {
22389             var el = Roo.getDom(id);
22390             return (el) ? el.style : null;
22391         },
22392
22393         /**
22394          * Inner class for cached elements
22395          * @class DragDropMgr.ElementWrapper
22396          * @for DragDropMgr
22397          * @private
22398          * @deprecated
22399          */
22400         ElementWrapper: function(el) {
22401                 /**
22402                  * The element
22403                  * @property el
22404                  */
22405                 this.el = el || null;
22406                 /**
22407                  * The element id
22408                  * @property id
22409                  */
22410                 this.id = this.el && el.id;
22411                 /**
22412                  * A reference to the style property
22413                  * @property css
22414                  */
22415                 this.css = this.el && el.style;
22416             },
22417
22418         /**
22419          * Returns the X position of an html element
22420          * @method getPosX
22421          * @param el the element for which to get the position
22422          * @return {int} the X coordinate
22423          * @for DragDropMgr
22424          * @deprecated use Roo.lib.Dom.getX instead
22425          * @static
22426          */
22427         getPosX: function(el) {
22428             return Roo.lib.Dom.getX(el);
22429         },
22430
22431         /**
22432          * Returns the Y position of an html element
22433          * @method getPosY
22434          * @param el the element for which to get the position
22435          * @return {int} the Y coordinate
22436          * @deprecated use Roo.lib.Dom.getY instead
22437          * @static
22438          */
22439         getPosY: function(el) {
22440             return Roo.lib.Dom.getY(el);
22441         },
22442
22443         /**
22444          * Swap two nodes.  In IE, we use the native method, for others we
22445          * emulate the IE behavior
22446          * @method swapNode
22447          * @param n1 the first node to swap
22448          * @param n2 the other node to swap
22449          * @static
22450          */
22451         swapNode: function(n1, n2) {
22452             if (n1.swapNode) {
22453                 n1.swapNode(n2);
22454             } else {
22455                 var p = n2.parentNode;
22456                 var s = n2.nextSibling;
22457
22458                 if (s == n1) {
22459                     p.insertBefore(n1, n2);
22460                 } else if (n2 == n1.nextSibling) {
22461                     p.insertBefore(n2, n1);
22462                 } else {
22463                     n1.parentNode.replaceChild(n2, n1);
22464                     p.insertBefore(n1, s);
22465                 }
22466             }
22467         },
22468
22469         /**
22470          * Returns the current scroll position
22471          * @method getScroll
22472          * @private
22473          * @static
22474          */
22475         getScroll: function () {
22476             var t, l, dde=document.documentElement, db=document.body;
22477             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22478                 t = dde.scrollTop;
22479                 l = dde.scrollLeft;
22480             } else if (db) {
22481                 t = db.scrollTop;
22482                 l = db.scrollLeft;
22483             } else {
22484
22485             }
22486             return { top: t, left: l };
22487         },
22488
22489         /**
22490          * Returns the specified element style property
22491          * @method getStyle
22492          * @param {HTMLElement} el          the element
22493          * @param {string}      styleProp   the style property
22494          * @return {string} The value of the style property
22495          * @deprecated use Roo.lib.Dom.getStyle
22496          * @static
22497          */
22498         getStyle: function(el, styleProp) {
22499             return Roo.fly(el).getStyle(styleProp);
22500         },
22501
22502         /**
22503          * Gets the scrollTop
22504          * @method getScrollTop
22505          * @return {int} the document's scrollTop
22506          * @static
22507          */
22508         getScrollTop: function () { return this.getScroll().top; },
22509
22510         /**
22511          * Gets the scrollLeft
22512          * @method getScrollLeft
22513          * @return {int} the document's scrollTop
22514          * @static
22515          */
22516         getScrollLeft: function () { return this.getScroll().left; },
22517
22518         /**
22519          * Sets the x/y position of an element to the location of the
22520          * target element.
22521          * @method moveToEl
22522          * @param {HTMLElement} moveEl      The element to move
22523          * @param {HTMLElement} targetEl    The position reference element
22524          * @static
22525          */
22526         moveToEl: function (moveEl, targetEl) {
22527             var aCoord = Roo.lib.Dom.getXY(targetEl);
22528             Roo.lib.Dom.setXY(moveEl, aCoord);
22529         },
22530
22531         /**
22532          * Numeric array sort function
22533          * @method numericSort
22534          * @static
22535          */
22536         numericSort: function(a, b) { return (a - b); },
22537
22538         /**
22539          * Internal counter
22540          * @property _timeoutCount
22541          * @private
22542          * @static
22543          */
22544         _timeoutCount: 0,
22545
22546         /**
22547          * Trying to make the load order less important.  Without this we get
22548          * an error if this file is loaded before the Event Utility.
22549          * @method _addListeners
22550          * @private
22551          * @static
22552          */
22553         _addListeners: function() {
22554             var DDM = Roo.dd.DDM;
22555             if ( Roo.lib.Event && document ) {
22556                 DDM._onLoad();
22557             } else {
22558                 if (DDM._timeoutCount > 2000) {
22559                 } else {
22560                     setTimeout(DDM._addListeners, 10);
22561                     if (document && document.body) {
22562                         DDM._timeoutCount += 1;
22563                     }
22564                 }
22565             }
22566         },
22567
22568         /**
22569          * Recursively searches the immediate parent and all child nodes for
22570          * the handle element in order to determine wheter or not it was
22571          * clicked.
22572          * @method handleWasClicked
22573          * @param node the html element to inspect
22574          * @static
22575          */
22576         handleWasClicked: function(node, id) {
22577             if (this.isHandle(id, node.id)) {
22578                 return true;
22579             } else {
22580                 // check to see if this is a text node child of the one we want
22581                 var p = node.parentNode;
22582
22583                 while (p) {
22584                     if (this.isHandle(id, p.id)) {
22585                         return true;
22586                     } else {
22587                         p = p.parentNode;
22588                     }
22589                 }
22590             }
22591
22592             return false;
22593         }
22594
22595     };
22596
22597 }();
22598
22599 // shorter alias, save a few bytes
22600 Roo.dd.DDM = Roo.dd.DragDropMgr;
22601 Roo.dd.DDM._addListeners();
22602
22603 }/*
22604  * Based on:
22605  * Ext JS Library 1.1.1
22606  * Copyright(c) 2006-2007, Ext JS, LLC.
22607  *
22608  * Originally Released Under LGPL - original licence link has changed is not relivant.
22609  *
22610  * Fork - LGPL
22611  * <script type="text/javascript">
22612  */
22613
22614 /**
22615  * @class Roo.dd.DD
22616  * A DragDrop implementation where the linked element follows the
22617  * mouse cursor during a drag.
22618  * @extends Roo.dd.DragDrop
22619  * @constructor
22620  * @param {String} id the id of the linked element
22621  * @param {String} sGroup the group of related DragDrop items
22622  * @param {object} config an object containing configurable attributes
22623  *                Valid properties for DD:
22624  *                    scroll
22625  */
22626 Roo.dd.DD = function(id, sGroup, config) {
22627     if (id) {
22628         this.init(id, sGroup, config);
22629     }
22630 };
22631
22632 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22633
22634     /**
22635      * When set to true, the utility automatically tries to scroll the browser
22636      * window wehn a drag and drop element is dragged near the viewport boundary.
22637      * Defaults to true.
22638      * @property scroll
22639      * @type boolean
22640      */
22641     scroll: true,
22642
22643     /**
22644      * Sets the pointer offset to the distance between the linked element's top
22645      * left corner and the location the element was clicked
22646      * @method autoOffset
22647      * @param {int} iPageX the X coordinate of the click
22648      * @param {int} iPageY the Y coordinate of the click
22649      */
22650     autoOffset: function(iPageX, iPageY) {
22651         var x = iPageX - this.startPageX;
22652         var y = iPageY - this.startPageY;
22653         this.setDelta(x, y);
22654     },
22655
22656     /**
22657      * Sets the pointer offset.  You can call this directly to force the
22658      * offset to be in a particular location (e.g., pass in 0,0 to set it
22659      * to the center of the object)
22660      * @method setDelta
22661      * @param {int} iDeltaX the distance from the left
22662      * @param {int} iDeltaY the distance from the top
22663      */
22664     setDelta: function(iDeltaX, iDeltaY) {
22665         this.deltaX = iDeltaX;
22666         this.deltaY = iDeltaY;
22667     },
22668
22669     /**
22670      * Sets the drag element to the location of the mousedown or click event,
22671      * maintaining the cursor location relative to the location on the element
22672      * that was clicked.  Override this if you want to place the element in a
22673      * location other than where the cursor is.
22674      * @method setDragElPos
22675      * @param {int} iPageX the X coordinate of the mousedown or drag event
22676      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22677      */
22678     setDragElPos: function(iPageX, iPageY) {
22679         // the first time we do this, we are going to check to make sure
22680         // the element has css positioning
22681
22682         var el = this.getDragEl();
22683         this.alignElWithMouse(el, iPageX, iPageY);
22684     },
22685
22686     /**
22687      * Sets the element to the location of the mousedown or click event,
22688      * maintaining the cursor location relative to the location on the element
22689      * that was clicked.  Override this if you want to place the element in a
22690      * location other than where the cursor is.
22691      * @method alignElWithMouse
22692      * @param {HTMLElement} el the element to move
22693      * @param {int} iPageX the X coordinate of the mousedown or drag event
22694      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22695      */
22696     alignElWithMouse: function(el, iPageX, iPageY) {
22697         var oCoord = this.getTargetCoord(iPageX, iPageY);
22698         var fly = el.dom ? el : Roo.fly(el);
22699         if (!this.deltaSetXY) {
22700             var aCoord = [oCoord.x, oCoord.y];
22701             fly.setXY(aCoord);
22702             var newLeft = fly.getLeft(true);
22703             var newTop  = fly.getTop(true);
22704             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22705         } else {
22706             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22707         }
22708
22709         this.cachePosition(oCoord.x, oCoord.y);
22710         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22711         return oCoord;
22712     },
22713
22714     /**
22715      * Saves the most recent position so that we can reset the constraints and
22716      * tick marks on-demand.  We need to know this so that we can calculate the
22717      * number of pixels the element is offset from its original position.
22718      * @method cachePosition
22719      * @param iPageX the current x position (optional, this just makes it so we
22720      * don't have to look it up again)
22721      * @param iPageY the current y position (optional, this just makes it so we
22722      * don't have to look it up again)
22723      */
22724     cachePosition: function(iPageX, iPageY) {
22725         if (iPageX) {
22726             this.lastPageX = iPageX;
22727             this.lastPageY = iPageY;
22728         } else {
22729             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22730             this.lastPageX = aCoord[0];
22731             this.lastPageY = aCoord[1];
22732         }
22733     },
22734
22735     /**
22736      * Auto-scroll the window if the dragged object has been moved beyond the
22737      * visible window boundary.
22738      * @method autoScroll
22739      * @param {int} x the drag element's x position
22740      * @param {int} y the drag element's y position
22741      * @param {int} h the height of the drag element
22742      * @param {int} w the width of the drag element
22743      * @private
22744      */
22745     autoScroll: function(x, y, h, w) {
22746
22747         if (this.scroll) {
22748             // The client height
22749             var clientH = Roo.lib.Dom.getViewWidth();
22750
22751             // The client width
22752             var clientW = Roo.lib.Dom.getViewHeight();
22753
22754             // The amt scrolled down
22755             var st = this.DDM.getScrollTop();
22756
22757             // The amt scrolled right
22758             var sl = this.DDM.getScrollLeft();
22759
22760             // Location of the bottom of the element
22761             var bot = h + y;
22762
22763             // Location of the right of the element
22764             var right = w + x;
22765
22766             // The distance from the cursor to the bottom of the visible area,
22767             // adjusted so that we don't scroll if the cursor is beyond the
22768             // element drag constraints
22769             var toBot = (clientH + st - y - this.deltaY);
22770
22771             // The distance from the cursor to the right of the visible area
22772             var toRight = (clientW + sl - x - this.deltaX);
22773
22774
22775             // How close to the edge the cursor must be before we scroll
22776             // var thresh = (document.all) ? 100 : 40;
22777             var thresh = 40;
22778
22779             // How many pixels to scroll per autoscroll op.  This helps to reduce
22780             // clunky scrolling. IE is more sensitive about this ... it needs this
22781             // value to be higher.
22782             var scrAmt = (document.all) ? 80 : 30;
22783
22784             // Scroll down if we are near the bottom of the visible page and the
22785             // obj extends below the crease
22786             if ( bot > clientH && toBot < thresh ) {
22787                 window.scrollTo(sl, st + scrAmt);
22788             }
22789
22790             // Scroll up if the window is scrolled down and the top of the object
22791             // goes above the top border
22792             if ( y < st && st > 0 && y - st < thresh ) {
22793                 window.scrollTo(sl, st - scrAmt);
22794             }
22795
22796             // Scroll right if the obj is beyond the right border and the cursor is
22797             // near the border.
22798             if ( right > clientW && toRight < thresh ) {
22799                 window.scrollTo(sl + scrAmt, st);
22800             }
22801
22802             // Scroll left if the window has been scrolled to the right and the obj
22803             // extends past the left border
22804             if ( x < sl && sl > 0 && x - sl < thresh ) {
22805                 window.scrollTo(sl - scrAmt, st);
22806             }
22807         }
22808     },
22809
22810     /**
22811      * Finds the location the element should be placed if we want to move
22812      * it to where the mouse location less the click offset would place us.
22813      * @method getTargetCoord
22814      * @param {int} iPageX the X coordinate of the click
22815      * @param {int} iPageY the Y coordinate of the click
22816      * @return an object that contains the coordinates (Object.x and Object.y)
22817      * @private
22818      */
22819     getTargetCoord: function(iPageX, iPageY) {
22820
22821
22822         var x = iPageX - this.deltaX;
22823         var y = iPageY - this.deltaY;
22824
22825         if (this.constrainX) {
22826             if (x < this.minX) { x = this.minX; }
22827             if (x > this.maxX) { x = this.maxX; }
22828         }
22829
22830         if (this.constrainY) {
22831             if (y < this.minY) { y = this.minY; }
22832             if (y > this.maxY) { y = this.maxY; }
22833         }
22834
22835         x = this.getTick(x, this.xTicks);
22836         y = this.getTick(y, this.yTicks);
22837
22838
22839         return {x:x, y:y};
22840     },
22841
22842     /*
22843      * Sets up config options specific to this class. Overrides
22844      * Roo.dd.DragDrop, but all versions of this method through the
22845      * inheritance chain are called
22846      */
22847     applyConfig: function() {
22848         Roo.dd.DD.superclass.applyConfig.call(this);
22849         this.scroll = (this.config.scroll !== false);
22850     },
22851
22852     /*
22853      * Event that fires prior to the onMouseDown event.  Overrides
22854      * Roo.dd.DragDrop.
22855      */
22856     b4MouseDown: function(e) {
22857         // this.resetConstraints();
22858         this.autoOffset(e.getPageX(),
22859                             e.getPageY());
22860     },
22861
22862     /*
22863      * Event that fires prior to the onDrag event.  Overrides
22864      * Roo.dd.DragDrop.
22865      */
22866     b4Drag: function(e) {
22867         this.setDragElPos(e.getPageX(),
22868                             e.getPageY());
22869     },
22870
22871     toString: function() {
22872         return ("DD " + this.id);
22873     }
22874
22875     //////////////////////////////////////////////////////////////////////////
22876     // Debugging ygDragDrop events that can be overridden
22877     //////////////////////////////////////////////////////////////////////////
22878     /*
22879     startDrag: function(x, y) {
22880     },
22881
22882     onDrag: function(e) {
22883     },
22884
22885     onDragEnter: function(e, id) {
22886     },
22887
22888     onDragOver: function(e, id) {
22889     },
22890
22891     onDragOut: function(e, id) {
22892     },
22893
22894     onDragDrop: function(e, id) {
22895     },
22896
22897     endDrag: function(e) {
22898     }
22899
22900     */
22901
22902 });/*
22903  * Based on:
22904  * Ext JS Library 1.1.1
22905  * Copyright(c) 2006-2007, Ext JS, LLC.
22906  *
22907  * Originally Released Under LGPL - original licence link has changed is not relivant.
22908  *
22909  * Fork - LGPL
22910  * <script type="text/javascript">
22911  */
22912
22913 /**
22914  * @class Roo.dd.DDProxy
22915  * A DragDrop implementation that inserts an empty, bordered div into
22916  * the document that follows the cursor during drag operations.  At the time of
22917  * the click, the frame div is resized to the dimensions of the linked html
22918  * element, and moved to the exact location of the linked element.
22919  *
22920  * References to the "frame" element refer to the single proxy element that
22921  * was created to be dragged in place of all DDProxy elements on the
22922  * page.
22923  *
22924  * @extends Roo.dd.DD
22925  * @constructor
22926  * @param {String} id the id of the linked html element
22927  * @param {String} sGroup the group of related DragDrop objects
22928  * @param {object} config an object containing configurable attributes
22929  *                Valid properties for DDProxy in addition to those in DragDrop:
22930  *                   resizeFrame, centerFrame, dragElId
22931  */
22932 Roo.dd.DDProxy = function(id, sGroup, config) {
22933     if (id) {
22934         this.init(id, sGroup, config);
22935         this.initFrame();
22936     }
22937 };
22938
22939 /**
22940  * The default drag frame div id
22941  * @property Roo.dd.DDProxy.dragElId
22942  * @type String
22943  * @static
22944  */
22945 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22946
22947 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22948
22949     /**
22950      * By default we resize the drag frame to be the same size as the element
22951      * we want to drag (this is to get the frame effect).  We can turn it off
22952      * if we want a different behavior.
22953      * @property resizeFrame
22954      * @type boolean
22955      */
22956     resizeFrame: true,
22957
22958     /**
22959      * By default the frame is positioned exactly where the drag element is, so
22960      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22961      * you do not have constraints on the obj is to have the drag frame centered
22962      * around the cursor.  Set centerFrame to true for this effect.
22963      * @property centerFrame
22964      * @type boolean
22965      */
22966     centerFrame: false,
22967
22968     /**
22969      * Creates the proxy element if it does not yet exist
22970      * @method createFrame
22971      */
22972     createFrame: function() {
22973         var self = this;
22974         var body = document.body;
22975
22976         if (!body || !body.firstChild) {
22977             setTimeout( function() { self.createFrame(); }, 50 );
22978             return;
22979         }
22980
22981         var div = this.getDragEl();
22982
22983         if (!div) {
22984             div    = document.createElement("div");
22985             div.id = this.dragElId;
22986             var s  = div.style;
22987
22988             s.position   = "absolute";
22989             s.visibility = "hidden";
22990             s.cursor     = "move";
22991             s.border     = "2px solid #aaa";
22992             s.zIndex     = 999;
22993
22994             // appendChild can blow up IE if invoked prior to the window load event
22995             // while rendering a table.  It is possible there are other scenarios
22996             // that would cause this to happen as well.
22997             body.insertBefore(div, body.firstChild);
22998         }
22999     },
23000
23001     /**
23002      * Initialization for the drag frame element.  Must be called in the
23003      * constructor of all subclasses
23004      * @method initFrame
23005      */
23006     initFrame: function() {
23007         this.createFrame();
23008     },
23009
23010     applyConfig: function() {
23011         Roo.dd.DDProxy.superclass.applyConfig.call(this);
23012
23013         this.resizeFrame = (this.config.resizeFrame !== false);
23014         this.centerFrame = (this.config.centerFrame);
23015         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
23016     },
23017
23018     /**
23019      * Resizes the drag frame to the dimensions of the clicked object, positions
23020      * it over the object, and finally displays it
23021      * @method showFrame
23022      * @param {int} iPageX X click position
23023      * @param {int} iPageY Y click position
23024      * @private
23025      */
23026     showFrame: function(iPageX, iPageY) {
23027         var el = this.getEl();
23028         var dragEl = this.getDragEl();
23029         var s = dragEl.style;
23030
23031         this._resizeProxy();
23032
23033         if (this.centerFrame) {
23034             this.setDelta( Math.round(parseInt(s.width,  10)/2),
23035                            Math.round(parseInt(s.height, 10)/2) );
23036         }
23037
23038         this.setDragElPos(iPageX, iPageY);
23039
23040         Roo.fly(dragEl).show();
23041     },
23042
23043     /**
23044      * The proxy is automatically resized to the dimensions of the linked
23045      * element when a drag is initiated, unless resizeFrame is set to false
23046      * @method _resizeProxy
23047      * @private
23048      */
23049     _resizeProxy: function() {
23050         if (this.resizeFrame) {
23051             var el = this.getEl();
23052             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23053         }
23054     },
23055
23056     // overrides Roo.dd.DragDrop
23057     b4MouseDown: function(e) {
23058         var x = e.getPageX();
23059         var y = e.getPageY();
23060         this.autoOffset(x, y);
23061         this.setDragElPos(x, y);
23062     },
23063
23064     // overrides Roo.dd.DragDrop
23065     b4StartDrag: function(x, y) {
23066         // show the drag frame
23067         this.showFrame(x, y);
23068     },
23069
23070     // overrides Roo.dd.DragDrop
23071     b4EndDrag: function(e) {
23072         Roo.fly(this.getDragEl()).hide();
23073     },
23074
23075     // overrides Roo.dd.DragDrop
23076     // By default we try to move the element to the last location of the frame.
23077     // This is so that the default behavior mirrors that of Roo.dd.DD.
23078     endDrag: function(e) {
23079
23080         var lel = this.getEl();
23081         var del = this.getDragEl();
23082
23083         // Show the drag frame briefly so we can get its position
23084         del.style.visibility = "";
23085
23086         this.beforeMove();
23087         // Hide the linked element before the move to get around a Safari
23088         // rendering bug.
23089         lel.style.visibility = "hidden";
23090         Roo.dd.DDM.moveToEl(lel, del);
23091         del.style.visibility = "hidden";
23092         lel.style.visibility = "";
23093
23094         this.afterDrag();
23095     },
23096
23097     beforeMove : function(){
23098
23099     },
23100
23101     afterDrag : function(){
23102
23103     },
23104
23105     toString: function() {
23106         return ("DDProxy " + this.id);
23107     }
23108
23109 });
23110 /*
23111  * Based on:
23112  * Ext JS Library 1.1.1
23113  * Copyright(c) 2006-2007, Ext JS, LLC.
23114  *
23115  * Originally Released Under LGPL - original licence link has changed is not relivant.
23116  *
23117  * Fork - LGPL
23118  * <script type="text/javascript">
23119  */
23120
23121  /**
23122  * @class Roo.dd.DDTarget
23123  * A DragDrop implementation that does not move, but can be a drop
23124  * target.  You would get the same result by simply omitting implementation
23125  * for the event callbacks, but this way we reduce the processing cost of the
23126  * event listener and the callbacks.
23127  * @extends Roo.dd.DragDrop
23128  * @constructor
23129  * @param {String} id the id of the element that is a drop target
23130  * @param {String} sGroup the group of related DragDrop objects
23131  * @param {object} config an object containing configurable attributes
23132  *                 Valid properties for DDTarget in addition to those in
23133  *                 DragDrop:
23134  *                    none
23135  */
23136 Roo.dd.DDTarget = function(id, sGroup, config) {
23137     if (id) {
23138         this.initTarget(id, sGroup, config);
23139     }
23140     if (config && (config.listeners || config.events)) { 
23141         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
23142             listeners : config.listeners || {}, 
23143             events : config.events || {} 
23144         });    
23145     }
23146 };
23147
23148 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23149 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23150     toString: function() {
23151         return ("DDTarget " + this.id);
23152     }
23153 });
23154 /*
23155  * Based on:
23156  * Ext JS Library 1.1.1
23157  * Copyright(c) 2006-2007, Ext JS, LLC.
23158  *
23159  * Originally Released Under LGPL - original licence link has changed is not relivant.
23160  *
23161  * Fork - LGPL
23162  * <script type="text/javascript">
23163  */
23164  
23165
23166 /**
23167  * @class Roo.dd.ScrollManager
23168  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23169  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23170  * @static
23171  */
23172 Roo.dd.ScrollManager = function(){
23173     var ddm = Roo.dd.DragDropMgr;
23174     var els = {};
23175     var dragEl = null;
23176     var proc = {};
23177     
23178     
23179     
23180     var onStop = function(e){
23181         dragEl = null;
23182         clearProc();
23183     };
23184     
23185     var triggerRefresh = function(){
23186         if(ddm.dragCurrent){
23187              ddm.refreshCache(ddm.dragCurrent.groups);
23188         }
23189     };
23190     
23191     var doScroll = function(){
23192         if(ddm.dragCurrent){
23193             var dds = Roo.dd.ScrollManager;
23194             if(!dds.animate){
23195                 if(proc.el.scroll(proc.dir, dds.increment)){
23196                     triggerRefresh();
23197                 }
23198             }else{
23199                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23200             }
23201         }
23202     };
23203     
23204     var clearProc = function(){
23205         if(proc.id){
23206             clearInterval(proc.id);
23207         }
23208         proc.id = 0;
23209         proc.el = null;
23210         proc.dir = "";
23211     };
23212     
23213     var startProc = function(el, dir){
23214          Roo.log('scroll startproc');
23215         clearProc();
23216         proc.el = el;
23217         proc.dir = dir;
23218         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23219     };
23220     
23221     var onFire = function(e, isDrop){
23222        
23223         if(isDrop || !ddm.dragCurrent){ return; }
23224         var dds = Roo.dd.ScrollManager;
23225         if(!dragEl || dragEl != ddm.dragCurrent){
23226             dragEl = ddm.dragCurrent;
23227             // refresh regions on drag start
23228             dds.refreshCache();
23229         }
23230         
23231         var xy = Roo.lib.Event.getXY(e);
23232         var pt = new Roo.lib.Point(xy[0], xy[1]);
23233         for(var id in els){
23234             var el = els[id], r = el._region;
23235             if(r && r.contains(pt) && el.isScrollable()){
23236                 if(r.bottom - pt.y <= dds.thresh){
23237                     if(proc.el != el){
23238                         startProc(el, "down");
23239                     }
23240                     return;
23241                 }else if(r.right - pt.x <= dds.thresh){
23242                     if(proc.el != el){
23243                         startProc(el, "left");
23244                     }
23245                     return;
23246                 }else if(pt.y - r.top <= dds.thresh){
23247                     if(proc.el != el){
23248                         startProc(el, "up");
23249                     }
23250                     return;
23251                 }else if(pt.x - r.left <= dds.thresh){
23252                     if(proc.el != el){
23253                         startProc(el, "right");
23254                     }
23255                     return;
23256                 }
23257             }
23258         }
23259         clearProc();
23260     };
23261     
23262     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23263     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23264     
23265     return {
23266         /**
23267          * Registers new overflow element(s) to auto scroll
23268          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23269          */
23270         register : function(el){
23271             if(el instanceof Array){
23272                 for(var i = 0, len = el.length; i < len; i++) {
23273                         this.register(el[i]);
23274                 }
23275             }else{
23276                 el = Roo.get(el);
23277                 els[el.id] = el;
23278             }
23279             Roo.dd.ScrollManager.els = els;
23280         },
23281         
23282         /**
23283          * Unregisters overflow element(s) so they are no longer scrolled
23284          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23285          */
23286         unregister : function(el){
23287             if(el instanceof Array){
23288                 for(var i = 0, len = el.length; i < len; i++) {
23289                         this.unregister(el[i]);
23290                 }
23291             }else{
23292                 el = Roo.get(el);
23293                 delete els[el.id];
23294             }
23295         },
23296         
23297         /**
23298          * The number of pixels from the edge of a container the pointer needs to be to 
23299          * trigger scrolling (defaults to 25)
23300          * @type Number
23301          */
23302         thresh : 25,
23303         
23304         /**
23305          * The number of pixels to scroll in each scroll increment (defaults to 50)
23306          * @type Number
23307          */
23308         increment : 100,
23309         
23310         /**
23311          * The frequency of scrolls in milliseconds (defaults to 500)
23312          * @type Number
23313          */
23314         frequency : 500,
23315         
23316         /**
23317          * True to animate the scroll (defaults to true)
23318          * @type Boolean
23319          */
23320         animate: true,
23321         
23322         /**
23323          * The animation duration in seconds - 
23324          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23325          * @type Number
23326          */
23327         animDuration: .4,
23328         
23329         /**
23330          * Manually trigger a cache refresh.
23331          */
23332         refreshCache : function(){
23333             for(var id in els){
23334                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23335                     els[id]._region = els[id].getRegion();
23336                 }
23337             }
23338         }
23339     };
23340 }();/*
23341  * Based on:
23342  * Ext JS Library 1.1.1
23343  * Copyright(c) 2006-2007, Ext JS, LLC.
23344  *
23345  * Originally Released Under LGPL - original licence link has changed is not relivant.
23346  *
23347  * Fork - LGPL
23348  * <script type="text/javascript">
23349  */
23350  
23351
23352 /**
23353  * @class Roo.dd.Registry
23354  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23355  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23356  * @static
23357  */
23358 Roo.dd.Registry = function(){
23359     var elements = {}; 
23360     var handles = {}; 
23361     var autoIdSeed = 0;
23362
23363     var getId = function(el, autogen){
23364         if(typeof el == "string"){
23365             return el;
23366         }
23367         var id = el.id;
23368         if(!id && autogen !== false){
23369             id = "roodd-" + (++autoIdSeed);
23370             el.id = id;
23371         }
23372         return id;
23373     };
23374     
23375     return {
23376     /**
23377      * Register a drag drop element
23378      * @param {String|HTMLElement} element The id or DOM node to register
23379      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23380      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23381      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23382      * populated in the data object (if applicable):
23383      * <pre>
23384 Value      Description<br />
23385 ---------  ------------------------------------------<br />
23386 handles    Array of DOM nodes that trigger dragging<br />
23387            for the element being registered<br />
23388 isHandle   True if the element passed in triggers<br />
23389            dragging itself, else false
23390 </pre>
23391      */
23392         register : function(el, data){
23393             data = data || {};
23394             if(typeof el == "string"){
23395                 el = document.getElementById(el);
23396             }
23397             data.ddel = el;
23398             elements[getId(el)] = data;
23399             if(data.isHandle !== false){
23400                 handles[data.ddel.id] = data;
23401             }
23402             if(data.handles){
23403                 var hs = data.handles;
23404                 for(var i = 0, len = hs.length; i < len; i++){
23405                         handles[getId(hs[i])] = data;
23406                 }
23407             }
23408         },
23409
23410     /**
23411      * Unregister a drag drop element
23412      * @param {String|HTMLElement}  element The id or DOM node to unregister
23413      */
23414         unregister : function(el){
23415             var id = getId(el, false);
23416             var data = elements[id];
23417             if(data){
23418                 delete elements[id];
23419                 if(data.handles){
23420                     var hs = data.handles;
23421                     for(var i = 0, len = hs.length; i < len; i++){
23422                         delete handles[getId(hs[i], false)];
23423                     }
23424                 }
23425             }
23426         },
23427
23428     /**
23429      * Returns the handle registered for a DOM Node by id
23430      * @param {String|HTMLElement} id The DOM node or id to look up
23431      * @return {Object} handle The custom handle data
23432      */
23433         getHandle : function(id){
23434             if(typeof id != "string"){ // must be element?
23435                 id = id.id;
23436             }
23437             return handles[id];
23438         },
23439
23440     /**
23441      * Returns the handle that is registered for the DOM node that is the target of the event
23442      * @param {Event} e The event
23443      * @return {Object} handle The custom handle data
23444      */
23445         getHandleFromEvent : function(e){
23446             var t = Roo.lib.Event.getTarget(e);
23447             return t ? handles[t.id] : null;
23448         },
23449
23450     /**
23451      * Returns a custom data object that is registered for a DOM node by id
23452      * @param {String|HTMLElement} id The DOM node or id to look up
23453      * @return {Object} data The custom data
23454      */
23455         getTarget : function(id){
23456             if(typeof id != "string"){ // must be element?
23457                 id = id.id;
23458             }
23459             return elements[id];
23460         },
23461
23462     /**
23463      * Returns a custom data object that is registered for the DOM node that is the target of the event
23464      * @param {Event} e The event
23465      * @return {Object} data The custom data
23466      */
23467         getTargetFromEvent : function(e){
23468             var t = Roo.lib.Event.getTarget(e);
23469             return t ? elements[t.id] || handles[t.id] : null;
23470         }
23471     };
23472 }();/*
23473  * Based on:
23474  * Ext JS Library 1.1.1
23475  * Copyright(c) 2006-2007, Ext JS, LLC.
23476  *
23477  * Originally Released Under LGPL - original licence link has changed is not relivant.
23478  *
23479  * Fork - LGPL
23480  * <script type="text/javascript">
23481  */
23482  
23483
23484 /**
23485  * @class Roo.dd.StatusProxy
23486  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23487  * default drag proxy used by all Roo.dd components.
23488  * @constructor
23489  * @param {Object} config
23490  */
23491 Roo.dd.StatusProxy = function(config){
23492     Roo.apply(this, config);
23493     this.id = this.id || Roo.id();
23494     this.el = new Roo.Layer({
23495         dh: {
23496             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23497                 {tag: "div", cls: "x-dd-drop-icon"},
23498                 {tag: "div", cls: "x-dd-drag-ghost"}
23499             ]
23500         }, 
23501         shadow: !config || config.shadow !== false
23502     });
23503     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23504     this.dropStatus = this.dropNotAllowed;
23505 };
23506
23507 Roo.dd.StatusProxy.prototype = {
23508     /**
23509      * @cfg {String} dropAllowed
23510      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23511      */
23512     dropAllowed : "x-dd-drop-ok",
23513     /**
23514      * @cfg {String} dropNotAllowed
23515      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23516      */
23517     dropNotAllowed : "x-dd-drop-nodrop",
23518
23519     /**
23520      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23521      * over the current target element.
23522      * @param {String} cssClass The css class for the new drop status indicator image
23523      */
23524     setStatus : function(cssClass){
23525         cssClass = cssClass || this.dropNotAllowed;
23526         if(this.dropStatus != cssClass){
23527             this.el.replaceClass(this.dropStatus, cssClass);
23528             this.dropStatus = cssClass;
23529         }
23530     },
23531
23532     /**
23533      * Resets the status indicator to the default dropNotAllowed value
23534      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23535      */
23536     reset : function(clearGhost){
23537         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23538         this.dropStatus = this.dropNotAllowed;
23539         if(clearGhost){
23540             this.ghost.update("");
23541         }
23542     },
23543
23544     /**
23545      * Updates the contents of the ghost element
23546      * @param {String} html The html that will replace the current innerHTML of the ghost element
23547      */
23548     update : function(html){
23549         if(typeof html == "string"){
23550             this.ghost.update(html);
23551         }else{
23552             this.ghost.update("");
23553             html.style.margin = "0";
23554             this.ghost.dom.appendChild(html);
23555         }
23556         // ensure float = none set?? cant remember why though.
23557         var el = this.ghost.dom.firstChild;
23558                 if(el){
23559                         Roo.fly(el).setStyle('float', 'none');
23560                 }
23561     },
23562     
23563     /**
23564      * Returns the underlying proxy {@link Roo.Layer}
23565      * @return {Roo.Layer} el
23566     */
23567     getEl : function(){
23568         return this.el;
23569     },
23570
23571     /**
23572      * Returns the ghost element
23573      * @return {Roo.Element} el
23574      */
23575     getGhost : function(){
23576         return this.ghost;
23577     },
23578
23579     /**
23580      * Hides the proxy
23581      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23582      */
23583     hide : function(clear){
23584         this.el.hide();
23585         if(clear){
23586             this.reset(true);
23587         }
23588     },
23589
23590     /**
23591      * Stops the repair animation if it's currently running
23592      */
23593     stop : function(){
23594         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23595             this.anim.stop();
23596         }
23597     },
23598
23599     /**
23600      * Displays this proxy
23601      */
23602     show : function(){
23603         this.el.show();
23604     },
23605
23606     /**
23607      * Force the Layer to sync its shadow and shim positions to the element
23608      */
23609     sync : function(){
23610         this.el.sync();
23611     },
23612
23613     /**
23614      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23615      * invalid drop operation by the item being dragged.
23616      * @param {Array} xy The XY position of the element ([x, y])
23617      * @param {Function} callback The function to call after the repair is complete
23618      * @param {Object} scope The scope in which to execute the callback
23619      */
23620     repair : function(xy, callback, scope){
23621         this.callback = callback;
23622         this.scope = scope;
23623         if(xy && this.animRepair !== false){
23624             this.el.addClass("x-dd-drag-repair");
23625             this.el.hideUnders(true);
23626             this.anim = this.el.shift({
23627                 duration: this.repairDuration || .5,
23628                 easing: 'easeOut',
23629                 xy: xy,
23630                 stopFx: true,
23631                 callback: this.afterRepair,
23632                 scope: this
23633             });
23634         }else{
23635             this.afterRepair();
23636         }
23637     },
23638
23639     // private
23640     afterRepair : function(){
23641         this.hide(true);
23642         if(typeof this.callback == "function"){
23643             this.callback.call(this.scope || this);
23644         }
23645         this.callback = null;
23646         this.scope = null;
23647     }
23648 };/*
23649  * Based on:
23650  * Ext JS Library 1.1.1
23651  * Copyright(c) 2006-2007, Ext JS, LLC.
23652  *
23653  * Originally Released Under LGPL - original licence link has changed is not relivant.
23654  *
23655  * Fork - LGPL
23656  * <script type="text/javascript">
23657  */
23658
23659 /**
23660  * @class Roo.dd.DragSource
23661  * @extends Roo.dd.DDProxy
23662  * A simple class that provides the basic implementation needed to make any element draggable.
23663  * @constructor
23664  * @param {String/HTMLElement/Element} el The container element
23665  * @param {Object} config
23666  */
23667 Roo.dd.DragSource = function(el, config){
23668     this.el = Roo.get(el);
23669     this.dragData = {};
23670     
23671     Roo.apply(this, config);
23672     
23673     if(!this.proxy){
23674         this.proxy = new Roo.dd.StatusProxy();
23675     }
23676
23677     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23678           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23679     
23680     this.dragging = false;
23681 };
23682
23683 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23684     /**
23685      * @cfg {String} dropAllowed
23686      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23687      */
23688     dropAllowed : "x-dd-drop-ok",
23689     /**
23690      * @cfg {String} dropNotAllowed
23691      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23692      */
23693     dropNotAllowed : "x-dd-drop-nodrop",
23694
23695     /**
23696      * Returns the data object associated with this drag source
23697      * @return {Object} data An object containing arbitrary data
23698      */
23699     getDragData : function(e){
23700         return this.dragData;
23701     },
23702
23703     // private
23704     onDragEnter : function(e, id){
23705         var target = Roo.dd.DragDropMgr.getDDById(id);
23706         this.cachedTarget = target;
23707         if(this.beforeDragEnter(target, e, id) !== false){
23708             if(target.isNotifyTarget){
23709                 var status = target.notifyEnter(this, e, this.dragData);
23710                 this.proxy.setStatus(status);
23711             }else{
23712                 this.proxy.setStatus(this.dropAllowed);
23713             }
23714             
23715             if(this.afterDragEnter){
23716                 /**
23717                  * An empty function by default, but provided so that you can perform a custom action
23718                  * when the dragged item enters the drop target by providing an implementation.
23719                  * @param {Roo.dd.DragDrop} target The drop target
23720                  * @param {Event} e The event object
23721                  * @param {String} id The id of the dragged element
23722                  * @method afterDragEnter
23723                  */
23724                 this.afterDragEnter(target, e, id);
23725             }
23726         }
23727     },
23728
23729     /**
23730      * An empty function by default, but provided so that you can perform a custom action
23731      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23732      * @param {Roo.dd.DragDrop} target The drop target
23733      * @param {Event} e The event object
23734      * @param {String} id The id of the dragged element
23735      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23736      */
23737     beforeDragEnter : function(target, e, id){
23738         return true;
23739     },
23740
23741     // private
23742     alignElWithMouse: function() {
23743         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23744         this.proxy.sync();
23745     },
23746
23747     // private
23748     onDragOver : function(e, id){
23749         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23750         if(this.beforeDragOver(target, e, id) !== false){
23751             if(target.isNotifyTarget){
23752                 var status = target.notifyOver(this, e, this.dragData);
23753                 this.proxy.setStatus(status);
23754             }
23755
23756             if(this.afterDragOver){
23757                 /**
23758                  * An empty function by default, but provided so that you can perform a custom action
23759                  * while the dragged item is over the drop target by providing an implementation.
23760                  * @param {Roo.dd.DragDrop} target The drop target
23761                  * @param {Event} e The event object
23762                  * @param {String} id The id of the dragged element
23763                  * @method afterDragOver
23764                  */
23765                 this.afterDragOver(target, e, id);
23766             }
23767         }
23768     },
23769
23770     /**
23771      * An empty function by default, but provided so that you can perform a custom action
23772      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23773      * @param {Roo.dd.DragDrop} target The drop target
23774      * @param {Event} e The event object
23775      * @param {String} id The id of the dragged element
23776      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23777      */
23778     beforeDragOver : function(target, e, id){
23779         return true;
23780     },
23781
23782     // private
23783     onDragOut : function(e, id){
23784         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23785         if(this.beforeDragOut(target, e, id) !== false){
23786             if(target.isNotifyTarget){
23787                 target.notifyOut(this, e, this.dragData);
23788             }
23789             this.proxy.reset();
23790             if(this.afterDragOut){
23791                 /**
23792                  * An empty function by default, but provided so that you can perform a custom action
23793                  * after the dragged item is dragged out of the target without dropping.
23794                  * @param {Roo.dd.DragDrop} target The drop target
23795                  * @param {Event} e The event object
23796                  * @param {String} id The id of the dragged element
23797                  * @method afterDragOut
23798                  */
23799                 this.afterDragOut(target, e, id);
23800             }
23801         }
23802         this.cachedTarget = null;
23803     },
23804
23805     /**
23806      * An empty function by default, but provided so that you can perform a custom action before the dragged
23807      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23808      * @param {Roo.dd.DragDrop} target The drop target
23809      * @param {Event} e The event object
23810      * @param {String} id The id of the dragged element
23811      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23812      */
23813     beforeDragOut : function(target, e, id){
23814         return true;
23815     },
23816     
23817     // private
23818     onDragDrop : function(e, id){
23819         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23820         if(this.beforeDragDrop(target, e, id) !== false){
23821             if(target.isNotifyTarget){
23822                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23823                     this.onValidDrop(target, e, id);
23824                 }else{
23825                     this.onInvalidDrop(target, e, id);
23826                 }
23827             }else{
23828                 this.onValidDrop(target, e, id);
23829             }
23830             
23831             if(this.afterDragDrop){
23832                 /**
23833                  * An empty function by default, but provided so that you can perform a custom action
23834                  * after a valid drag drop has occurred by providing an implementation.
23835                  * @param {Roo.dd.DragDrop} target The drop target
23836                  * @param {Event} e The event object
23837                  * @param {String} id The id of the dropped element
23838                  * @method afterDragDrop
23839                  */
23840                 this.afterDragDrop(target, e, id);
23841             }
23842         }
23843         delete this.cachedTarget;
23844     },
23845
23846     /**
23847      * An empty function by default, but provided so that you can perform a custom action before the dragged
23848      * item is dropped onto the target and optionally cancel the onDragDrop.
23849      * @param {Roo.dd.DragDrop} target The drop target
23850      * @param {Event} e The event object
23851      * @param {String} id The id of the dragged element
23852      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23853      */
23854     beforeDragDrop : function(target, e, id){
23855         return true;
23856     },
23857
23858     // private
23859     onValidDrop : function(target, e, id){
23860         this.hideProxy();
23861         if(this.afterValidDrop){
23862             /**
23863              * An empty function by default, but provided so that you can perform a custom action
23864              * after a valid drop has occurred by providing an implementation.
23865              * @param {Object} target The target DD 
23866              * @param {Event} e The event object
23867              * @param {String} id The id of the dropped element
23868              * @method afterInvalidDrop
23869              */
23870             this.afterValidDrop(target, e, id);
23871         }
23872     },
23873
23874     // private
23875     getRepairXY : function(e, data){
23876         return this.el.getXY();  
23877     },
23878
23879     // private
23880     onInvalidDrop : function(target, e, id){
23881         this.beforeInvalidDrop(target, e, id);
23882         if(this.cachedTarget){
23883             if(this.cachedTarget.isNotifyTarget){
23884                 this.cachedTarget.notifyOut(this, e, this.dragData);
23885             }
23886             this.cacheTarget = null;
23887         }
23888         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23889
23890         if(this.afterInvalidDrop){
23891             /**
23892              * An empty function by default, but provided so that you can perform a custom action
23893              * after an invalid drop has occurred by providing an implementation.
23894              * @param {Event} e The event object
23895              * @param {String} id The id of the dropped element
23896              * @method afterInvalidDrop
23897              */
23898             this.afterInvalidDrop(e, id);
23899         }
23900     },
23901
23902     // private
23903     afterRepair : function(){
23904         if(Roo.enableFx){
23905             this.el.highlight(this.hlColor || "c3daf9");
23906         }
23907         this.dragging = false;
23908     },
23909
23910     /**
23911      * An empty function by default, but provided so that you can perform a custom action after an invalid
23912      * drop has occurred.
23913      * @param {Roo.dd.DragDrop} target The drop target
23914      * @param {Event} e The event object
23915      * @param {String} id The id of the dragged element
23916      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23917      */
23918     beforeInvalidDrop : function(target, e, id){
23919         return true;
23920     },
23921
23922     // private
23923     handleMouseDown : function(e){
23924         if(this.dragging) {
23925             return;
23926         }
23927         var data = this.getDragData(e);
23928         if(data && this.onBeforeDrag(data, e) !== false){
23929             this.dragData = data;
23930             this.proxy.stop();
23931             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23932         } 
23933     },
23934
23935     /**
23936      * An empty function by default, but provided so that you can perform a custom action before the initial
23937      * drag event begins and optionally cancel it.
23938      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23939      * @param {Event} e The event object
23940      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23941      */
23942     onBeforeDrag : function(data, e){
23943         return true;
23944     },
23945
23946     /**
23947      * An empty function by default, but provided so that you can perform a custom action once the initial
23948      * drag event has begun.  The drag cannot be canceled from this function.
23949      * @param {Number} x The x position of the click on the dragged object
23950      * @param {Number} y The y position of the click on the dragged object
23951      */
23952     onStartDrag : Roo.emptyFn,
23953
23954     // private - YUI override
23955     startDrag : function(x, y){
23956         this.proxy.reset();
23957         this.dragging = true;
23958         this.proxy.update("");
23959         this.onInitDrag(x, y);
23960         this.proxy.show();
23961     },
23962
23963     // private
23964     onInitDrag : function(x, y){
23965         var clone = this.el.dom.cloneNode(true);
23966         clone.id = Roo.id(); // prevent duplicate ids
23967         this.proxy.update(clone);
23968         this.onStartDrag(x, y);
23969         return true;
23970     },
23971
23972     /**
23973      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23974      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23975      */
23976     getProxy : function(){
23977         return this.proxy;  
23978     },
23979
23980     /**
23981      * Hides the drag source's {@link Roo.dd.StatusProxy}
23982      */
23983     hideProxy : function(){
23984         this.proxy.hide();  
23985         this.proxy.reset(true);
23986         this.dragging = false;
23987     },
23988
23989     // private
23990     triggerCacheRefresh : function(){
23991         Roo.dd.DDM.refreshCache(this.groups);
23992     },
23993
23994     // private - override to prevent hiding
23995     b4EndDrag: function(e) {
23996     },
23997
23998     // private - override to prevent moving
23999     endDrag : function(e){
24000         this.onEndDrag(this.dragData, e);
24001     },
24002
24003     // private
24004     onEndDrag : function(data, e){
24005     },
24006     
24007     // private - pin to cursor
24008     autoOffset : function(x, y) {
24009         this.setDelta(-12, -20);
24010     }    
24011 });/*
24012  * Based on:
24013  * Ext JS Library 1.1.1
24014  * Copyright(c) 2006-2007, Ext JS, LLC.
24015  *
24016  * Originally Released Under LGPL - original licence link has changed is not relivant.
24017  *
24018  * Fork - LGPL
24019  * <script type="text/javascript">
24020  */
24021
24022
24023 /**
24024  * @class Roo.dd.DropTarget
24025  * @extends Roo.dd.DDTarget
24026  * A simple class that provides the basic implementation needed to make any element a drop target that can have
24027  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
24028  * @constructor
24029  * @param {String/HTMLElement/Element} el The container element
24030  * @param {Object} config
24031  */
24032 Roo.dd.DropTarget = function(el, config){
24033     this.el = Roo.get(el);
24034     
24035     var listeners = false; ;
24036     if (config && config.listeners) {
24037         listeners= config.listeners;
24038         delete config.listeners;
24039     }
24040     Roo.apply(this, config);
24041     
24042     if(this.containerScroll){
24043         Roo.dd.ScrollManager.register(this.el);
24044     }
24045     this.addEvents( {
24046          /**
24047          * @scope Roo.dd.DropTarget
24048          */
24049          
24050          /**
24051          * @event enter
24052          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24053          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
24054          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
24055          * 
24056          * IMPORTANT : it should set  this.valid to true|false
24057          * 
24058          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24059          * @param {Event} e The event
24060          * @param {Object} data An object containing arbitrary data supplied by the drag source
24061          */
24062         "enter" : true,
24063         
24064          /**
24065          * @event over
24066          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24067          * This method will be called on every mouse movement while the drag source is over the drop target.
24068          * This default implementation simply returns the dropAllowed config value.
24069          * 
24070          * IMPORTANT : it should set  this.valid to true|false
24071          * 
24072          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24073          * @param {Event} e The event
24074          * @param {Object} data An object containing arbitrary data supplied by the drag source
24075          
24076          */
24077         "over" : true,
24078         /**
24079          * @event out
24080          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24081          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
24082          * overClass (if any) from the drop element.
24083          * 
24084          * 
24085          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24086          * @param {Event} e The event
24087          * @param {Object} data An object containing arbitrary data supplied by the drag source
24088          */
24089          "out" : true,
24090          
24091         /**
24092          * @event drop
24093          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24094          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
24095          * implementation that does something to process the drop event and returns true so that the drag source's
24096          * repair action does not run.
24097          * 
24098          * IMPORTANT : it should set this.success
24099          * 
24100          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24101          * @param {Event} e The event
24102          * @param {Object} data An object containing arbitrary data supplied by the drag source
24103         */
24104          "drop" : true
24105     });
24106             
24107      
24108     Roo.dd.DropTarget.superclass.constructor.call(  this, 
24109         this.el.dom, 
24110         this.ddGroup || this.group,
24111         {
24112             isTarget: true,
24113             listeners : listeners || {} 
24114            
24115         
24116         }
24117     );
24118
24119 };
24120
24121 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24122     /**
24123      * @cfg {String} overClass
24124      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24125      */
24126      /**
24127      * @cfg {String} ddGroup
24128      * The drag drop group to handle drop events for
24129      */
24130      
24131     /**
24132      * @cfg {String} dropAllowed
24133      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24134      */
24135     dropAllowed : "x-dd-drop-ok",
24136     /**
24137      * @cfg {String} dropNotAllowed
24138      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24139      */
24140     dropNotAllowed : "x-dd-drop-nodrop",
24141     /**
24142      * @cfg {boolean} success
24143      * set this after drop listener.. 
24144      */
24145     success : false,
24146     /**
24147      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24148      * if the drop point is valid for over/enter..
24149      */
24150     valid : false,
24151     // private
24152     isTarget : true,
24153
24154     // private
24155     isNotifyTarget : true,
24156     
24157     /**
24158      * @hide
24159      */
24160     notifyEnter : function(dd, e, data)
24161     {
24162         this.valid = true;
24163         this.fireEvent('enter', dd, e, data);
24164         if(this.overClass){
24165             this.el.addClass(this.overClass);
24166         }
24167         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24168             this.valid ? this.dropAllowed : this.dropNotAllowed
24169         );
24170     },
24171
24172     /**
24173      * @hide
24174      */
24175     notifyOver : function(dd, e, data)
24176     {
24177         this.valid = true;
24178         this.fireEvent('over', dd, e, data);
24179         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24180             this.valid ? this.dropAllowed : this.dropNotAllowed
24181         );
24182     },
24183
24184     /**
24185      * @hide
24186      */
24187     notifyOut : function(dd, e, data)
24188     {
24189         this.fireEvent('out', dd, e, data);
24190         if(this.overClass){
24191             this.el.removeClass(this.overClass);
24192         }
24193     },
24194
24195     /**
24196      * @hide
24197      */
24198     notifyDrop : function(dd, e, data)
24199     {
24200         this.success = false;
24201         this.fireEvent('drop', dd, e, data);
24202         return this.success;
24203     }
24204 });/*
24205  * Based on:
24206  * Ext JS Library 1.1.1
24207  * Copyright(c) 2006-2007, Ext JS, LLC.
24208  *
24209  * Originally Released Under LGPL - original licence link has changed is not relivant.
24210  *
24211  * Fork - LGPL
24212  * <script type="text/javascript">
24213  */
24214
24215
24216 /**
24217  * @class Roo.dd.DragZone
24218  * @extends Roo.dd.DragSource
24219  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24220  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24221  * @constructor
24222  * @param {String/HTMLElement/Element} el The container element
24223  * @param {Object} config
24224  */
24225 Roo.dd.DragZone = function(el, config){
24226     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24227     if(this.containerScroll){
24228         Roo.dd.ScrollManager.register(this.el);
24229     }
24230 };
24231
24232 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24233     /**
24234      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24235      * for auto scrolling during drag operations.
24236      */
24237     /**
24238      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24239      * method after a failed drop (defaults to "c3daf9" - light blue)
24240      */
24241
24242     /**
24243      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24244      * for a valid target to drag based on the mouse down. Override this method
24245      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24246      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24247      * @param {EventObject} e The mouse down event
24248      * @return {Object} The dragData
24249      */
24250     getDragData : function(e){
24251         return Roo.dd.Registry.getHandleFromEvent(e);
24252     },
24253     
24254     /**
24255      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24256      * this.dragData.ddel
24257      * @param {Number} x The x position of the click on the dragged object
24258      * @param {Number} y The y position of the click on the dragged object
24259      * @return {Boolean} true to continue the drag, false to cancel
24260      */
24261     onInitDrag : function(x, y){
24262         this.proxy.update(this.dragData.ddel.cloneNode(true));
24263         this.onStartDrag(x, y);
24264         return true;
24265     },
24266     
24267     /**
24268      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24269      */
24270     afterRepair : function(){
24271         if(Roo.enableFx){
24272             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24273         }
24274         this.dragging = false;
24275     },
24276
24277     /**
24278      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24279      * the XY of this.dragData.ddel
24280      * @param {EventObject} e The mouse up event
24281      * @return {Array} The xy location (e.g. [100, 200])
24282      */
24283     getRepairXY : function(e){
24284         return Roo.Element.fly(this.dragData.ddel).getXY();  
24285     }
24286 });/*
24287  * Based on:
24288  * Ext JS Library 1.1.1
24289  * Copyright(c) 2006-2007, Ext JS, LLC.
24290  *
24291  * Originally Released Under LGPL - original licence link has changed is not relivant.
24292  *
24293  * Fork - LGPL
24294  * <script type="text/javascript">
24295  */
24296 /**
24297  * @class Roo.dd.DropZone
24298  * @extends Roo.dd.DropTarget
24299  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24300  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24301  * @constructor
24302  * @param {String/HTMLElement/Element} el The container element
24303  * @param {Object} config
24304  */
24305 Roo.dd.DropZone = function(el, config){
24306     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24307 };
24308
24309 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24310     /**
24311      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24312      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24313      * provide your own custom lookup.
24314      * @param {Event} e The event
24315      * @return {Object} data The custom data
24316      */
24317     getTargetFromEvent : function(e){
24318         return Roo.dd.Registry.getTargetFromEvent(e);
24319     },
24320
24321     /**
24322      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24323      * that it has registered.  This method has no default implementation and should be overridden to provide
24324      * node-specific processing if necessary.
24325      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24326      * {@link #getTargetFromEvent} for this node)
24327      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24328      * @param {Event} e The event
24329      * @param {Object} data An object containing arbitrary data supplied by the drag source
24330      */
24331     onNodeEnter : function(n, dd, e, data){
24332         
24333     },
24334
24335     /**
24336      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24337      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24338      * overridden to provide the proper feedback.
24339      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24340      * {@link #getTargetFromEvent} for this node)
24341      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24342      * @param {Event} e The event
24343      * @param {Object} data An object containing arbitrary data supplied by the drag source
24344      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24345      * underlying {@link Roo.dd.StatusProxy} can be updated
24346      */
24347     onNodeOver : function(n, dd, e, data){
24348         return this.dropAllowed;
24349     },
24350
24351     /**
24352      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24353      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24354      * node-specific processing if necessary.
24355      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24356      * {@link #getTargetFromEvent} for this node)
24357      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24358      * @param {Event} e The event
24359      * @param {Object} data An object containing arbitrary data supplied by the drag source
24360      */
24361     onNodeOut : function(n, dd, e, data){
24362         
24363     },
24364
24365     /**
24366      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24367      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24368      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24369      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24370      * {@link #getTargetFromEvent} for this node)
24371      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24372      * @param {Event} e The event
24373      * @param {Object} data An object containing arbitrary data supplied by the drag source
24374      * @return {Boolean} True if the drop was valid, else false
24375      */
24376     onNodeDrop : function(n, dd, e, data){
24377         return false;
24378     },
24379
24380     /**
24381      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24382      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24383      * it should be overridden to provide the proper feedback if necessary.
24384      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24385      * @param {Event} e The event
24386      * @param {Object} data An object containing arbitrary data supplied by the drag source
24387      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24388      * underlying {@link Roo.dd.StatusProxy} can be updated
24389      */
24390     onContainerOver : function(dd, e, data){
24391         return this.dropNotAllowed;
24392     },
24393
24394     /**
24395      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24396      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24397      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24398      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24399      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24400      * @param {Event} e The event
24401      * @param {Object} data An object containing arbitrary data supplied by the drag source
24402      * @return {Boolean} True if the drop was valid, else false
24403      */
24404     onContainerDrop : function(dd, e, data){
24405         return false;
24406     },
24407
24408     /**
24409      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24410      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24411      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24412      * you should override this method and provide a custom implementation.
24413      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24414      * @param {Event} e The event
24415      * @param {Object} data An object containing arbitrary data supplied by the drag source
24416      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24417      * underlying {@link Roo.dd.StatusProxy} can be updated
24418      */
24419     notifyEnter : function(dd, e, data){
24420         return this.dropNotAllowed;
24421     },
24422
24423     /**
24424      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24425      * This method will be called on every mouse movement while the drag source is over the drop zone.
24426      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24427      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24428      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24429      * registered node, it will call {@link #onContainerOver}.
24430      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24431      * @param {Event} e The event
24432      * @param {Object} data An object containing arbitrary data supplied by the drag source
24433      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24434      * underlying {@link Roo.dd.StatusProxy} can be updated
24435      */
24436     notifyOver : function(dd, e, data){
24437         var n = this.getTargetFromEvent(e);
24438         if(!n){ // not over valid drop target
24439             if(this.lastOverNode){
24440                 this.onNodeOut(this.lastOverNode, dd, e, data);
24441                 this.lastOverNode = null;
24442             }
24443             return this.onContainerOver(dd, e, data);
24444         }
24445         if(this.lastOverNode != n){
24446             if(this.lastOverNode){
24447                 this.onNodeOut(this.lastOverNode, dd, e, data);
24448             }
24449             this.onNodeEnter(n, dd, e, data);
24450             this.lastOverNode = n;
24451         }
24452         return this.onNodeOver(n, dd, e, data);
24453     },
24454
24455     /**
24456      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24457      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24458      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24459      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24460      * @param {Event} e The event
24461      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24462      */
24463     notifyOut : function(dd, e, data){
24464         if(this.lastOverNode){
24465             this.onNodeOut(this.lastOverNode, dd, e, data);
24466             this.lastOverNode = null;
24467         }
24468     },
24469
24470     /**
24471      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24472      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24473      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24474      * otherwise it will call {@link #onContainerDrop}.
24475      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24476      * @param {Event} e The event
24477      * @param {Object} data An object containing arbitrary data supplied by the drag source
24478      * @return {Boolean} True if the drop was valid, else false
24479      */
24480     notifyDrop : function(dd, e, data){
24481         if(this.lastOverNode){
24482             this.onNodeOut(this.lastOverNode, dd, e, data);
24483             this.lastOverNode = null;
24484         }
24485         var n = this.getTargetFromEvent(e);
24486         return n ?
24487             this.onNodeDrop(n, dd, e, data) :
24488             this.onContainerDrop(dd, e, data);
24489     },
24490
24491     // private
24492     triggerCacheRefresh : function(){
24493         Roo.dd.DDM.refreshCache(this.groups);
24494     }  
24495 });/*
24496  * Based on:
24497  * Ext JS Library 1.1.1
24498  * Copyright(c) 2006-2007, Ext JS, LLC.
24499  *
24500  * Originally Released Under LGPL - original licence link has changed is not relivant.
24501  *
24502  * Fork - LGPL
24503  * <script type="text/javascript">
24504  */
24505
24506
24507 /**
24508  * @class Roo.data.SortTypes
24509  * @static
24510  * Defines the default sorting (casting?) comparison functions used when sorting data.
24511  */
24512 Roo.data.SortTypes = {
24513     /**
24514      * Default sort that does nothing
24515      * @param {Mixed} s The value being converted
24516      * @return {Mixed} The comparison value
24517      */
24518     none : function(s){
24519         return s;
24520     },
24521     
24522     /**
24523      * The regular expression used to strip tags
24524      * @type {RegExp}
24525      * @property
24526      */
24527     stripTagsRE : /<\/?[^>]+>/gi,
24528     
24529     /**
24530      * Strips all HTML tags to sort on text only
24531      * @param {Mixed} s The value being converted
24532      * @return {String} The comparison value
24533      */
24534     asText : function(s){
24535         return String(s).replace(this.stripTagsRE, "");
24536     },
24537     
24538     /**
24539      * Strips all HTML tags to sort on text only - Case insensitive
24540      * @param {Mixed} s The value being converted
24541      * @return {String} The comparison value
24542      */
24543     asUCText : function(s){
24544         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24545     },
24546     
24547     /**
24548      * Case insensitive string
24549      * @param {Mixed} s The value being converted
24550      * @return {String} The comparison value
24551      */
24552     asUCString : function(s) {
24553         return String(s).toUpperCase();
24554     },
24555     
24556     /**
24557      * Date sorting
24558      * @param {Mixed} s The value being converted
24559      * @return {Number} The comparison value
24560      */
24561     asDate : function(s) {
24562         if(!s){
24563             return 0;
24564         }
24565         if(s instanceof Date){
24566             return s.getTime();
24567         }
24568         return Date.parse(String(s));
24569     },
24570     
24571     /**
24572      * Float sorting
24573      * @param {Mixed} s The value being converted
24574      * @return {Float} The comparison value
24575      */
24576     asFloat : function(s) {
24577         var val = parseFloat(String(s).replace(/,/g, ""));
24578         if(isNaN(val)) {
24579             val = 0;
24580         }
24581         return val;
24582     },
24583     
24584     /**
24585      * Integer sorting
24586      * @param {Mixed} s The value being converted
24587      * @return {Number} The comparison value
24588      */
24589     asInt : function(s) {
24590         var val = parseInt(String(s).replace(/,/g, ""));
24591         if(isNaN(val)) {
24592             val = 0;
24593         }
24594         return val;
24595     }
24596 };/*
24597  * Based on:
24598  * Ext JS Library 1.1.1
24599  * Copyright(c) 2006-2007, Ext JS, LLC.
24600  *
24601  * Originally Released Under LGPL - original licence link has changed is not relivant.
24602  *
24603  * Fork - LGPL
24604  * <script type="text/javascript">
24605  */
24606
24607 /**
24608 * @class Roo.data.Record
24609  * Instances of this class encapsulate both record <em>definition</em> information, and record
24610  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24611  * to access Records cached in an {@link Roo.data.Store} object.<br>
24612  * <p>
24613  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24614  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24615  * objects.<br>
24616  * <p>
24617  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24618  * @constructor
24619  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24620  * {@link #create}. The parameters are the same.
24621  * @param {Array} data An associative Array of data values keyed by the field name.
24622  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24623  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24624  * not specified an integer id is generated.
24625  */
24626 Roo.data.Record = function(data, id){
24627     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24628     this.data = data;
24629 };
24630
24631 /**
24632  * Generate a constructor for a specific record layout.
24633  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24634  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24635  * Each field definition object may contain the following properties: <ul>
24636  * <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,
24637  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24638  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24639  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24640  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24641  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24642  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24643  * this may be omitted.</p></li>
24644  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24645  * <ul><li>auto (Default, implies no conversion)</li>
24646  * <li>string</li>
24647  * <li>int</li>
24648  * <li>float</li>
24649  * <li>boolean</li>
24650  * <li>date</li></ul></p></li>
24651  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24652  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24653  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24654  * by the Reader into an object that will be stored in the Record. It is passed the
24655  * following parameters:<ul>
24656  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24657  * </ul></p></li>
24658  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24659  * </ul>
24660  * <br>usage:<br><pre><code>
24661 var TopicRecord = Roo.data.Record.create(
24662     {name: 'title', mapping: 'topic_title'},
24663     {name: 'author', mapping: 'username'},
24664     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24665     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24666     {name: 'lastPoster', mapping: 'user2'},
24667     {name: 'excerpt', mapping: 'post_text'}
24668 );
24669
24670 var myNewRecord = new TopicRecord({
24671     title: 'Do my job please',
24672     author: 'noobie',
24673     totalPosts: 1,
24674     lastPost: new Date(),
24675     lastPoster: 'Animal',
24676     excerpt: 'No way dude!'
24677 });
24678 myStore.add(myNewRecord);
24679 </code></pre>
24680  * @method create
24681  * @static
24682  */
24683 Roo.data.Record.create = function(o){
24684     var f = function(){
24685         f.superclass.constructor.apply(this, arguments);
24686     };
24687     Roo.extend(f, Roo.data.Record);
24688     var p = f.prototype;
24689     p.fields = new Roo.util.MixedCollection(false, function(field){
24690         return field.name;
24691     });
24692     for(var i = 0, len = o.length; i < len; i++){
24693         p.fields.add(new Roo.data.Field(o[i]));
24694     }
24695     f.getField = function(name){
24696         return p.fields.get(name);  
24697     };
24698     return f;
24699 };
24700
24701 Roo.data.Record.AUTO_ID = 1000;
24702 Roo.data.Record.EDIT = 'edit';
24703 Roo.data.Record.REJECT = 'reject';
24704 Roo.data.Record.COMMIT = 'commit';
24705
24706 Roo.data.Record.prototype = {
24707     /**
24708      * Readonly flag - true if this record has been modified.
24709      * @type Boolean
24710      */
24711     dirty : false,
24712     editing : false,
24713     error: null,
24714     modified: null,
24715
24716     // private
24717     join : function(store){
24718         this.store = store;
24719     },
24720
24721     /**
24722      * Set the named field to the specified value.
24723      * @param {String} name The name of the field to set.
24724      * @param {Object} value The value to set the field to.
24725      */
24726     set : function(name, value){
24727         if(this.data[name] == value){
24728             return;
24729         }
24730         this.dirty = true;
24731         if(!this.modified){
24732             this.modified = {};
24733         }
24734         if(typeof this.modified[name] == 'undefined'){
24735             this.modified[name] = this.data[name];
24736         }
24737         this.data[name] = value;
24738         if(!this.editing && this.store){
24739             this.store.afterEdit(this);
24740         }       
24741     },
24742
24743     /**
24744      * Get the value of the named field.
24745      * @param {String} name The name of the field to get the value of.
24746      * @return {Object} The value of the field.
24747      */
24748     get : function(name){
24749         return this.data[name]; 
24750     },
24751
24752     // private
24753     beginEdit : function(){
24754         this.editing = true;
24755         this.modified = {}; 
24756     },
24757
24758     // private
24759     cancelEdit : function(){
24760         this.editing = false;
24761         delete this.modified;
24762     },
24763
24764     // private
24765     endEdit : function(){
24766         this.editing = false;
24767         if(this.dirty && this.store){
24768             this.store.afterEdit(this);
24769         }
24770     },
24771
24772     /**
24773      * Usually called by the {@link Roo.data.Store} which owns the Record.
24774      * Rejects all changes made to the Record since either creation, or the last commit operation.
24775      * Modified fields are reverted to their original values.
24776      * <p>
24777      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24778      * of reject operations.
24779      */
24780     reject : function(){
24781         var m = this.modified;
24782         for(var n in m){
24783             if(typeof m[n] != "function"){
24784                 this.data[n] = m[n];
24785             }
24786         }
24787         this.dirty = false;
24788         delete this.modified;
24789         this.editing = false;
24790         if(this.store){
24791             this.store.afterReject(this);
24792         }
24793     },
24794
24795     /**
24796      * Usually called by the {@link Roo.data.Store} which owns the Record.
24797      * Commits all changes made to the Record since either creation, or the last commit operation.
24798      * <p>
24799      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24800      * of commit operations.
24801      */
24802     commit : function(){
24803         this.dirty = false;
24804         delete this.modified;
24805         this.editing = false;
24806         if(this.store){
24807             this.store.afterCommit(this);
24808         }
24809     },
24810
24811     // private
24812     hasError : function(){
24813         return this.error != null;
24814     },
24815
24816     // private
24817     clearError : function(){
24818         this.error = null;
24819     },
24820
24821     /**
24822      * Creates a copy of this record.
24823      * @param {String} id (optional) A new record id if you don't want to use this record's id
24824      * @return {Record}
24825      */
24826     copy : function(newId) {
24827         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24828     }
24829 };/*
24830  * Based on:
24831  * Ext JS Library 1.1.1
24832  * Copyright(c) 2006-2007, Ext JS, LLC.
24833  *
24834  * Originally Released Under LGPL - original licence link has changed is not relivant.
24835  *
24836  * Fork - LGPL
24837  * <script type="text/javascript">
24838  */
24839
24840
24841
24842 /**
24843  * @class Roo.data.Store
24844  * @extends Roo.util.Observable
24845  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24846  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24847  * <p>
24848  * 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
24849  * has no knowledge of the format of the data returned by the Proxy.<br>
24850  * <p>
24851  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24852  * instances from the data object. These records are cached and made available through accessor functions.
24853  * @constructor
24854  * Creates a new Store.
24855  * @param {Object} config A config object containing the objects needed for the Store to access data,
24856  * and read the data into Records.
24857  */
24858 Roo.data.Store = function(config){
24859     this.data = new Roo.util.MixedCollection(false);
24860     this.data.getKey = function(o){
24861         return o.id;
24862     };
24863     this.baseParams = {};
24864     // private
24865     this.paramNames = {
24866         "start" : "start",
24867         "limit" : "limit",
24868         "sort" : "sort",
24869         "dir" : "dir",
24870         "multisort" : "_multisort"
24871     };
24872
24873     if(config && config.data){
24874         this.inlineData = config.data;
24875         delete config.data;
24876     }
24877
24878     Roo.apply(this, config);
24879     
24880     if(this.reader){ // reader passed
24881         this.reader = Roo.factory(this.reader, Roo.data);
24882         this.reader.xmodule = this.xmodule || false;
24883         if(!this.recordType){
24884             this.recordType = this.reader.recordType;
24885         }
24886         if(this.reader.onMetaChange){
24887             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24888         }
24889     }
24890
24891     if(this.recordType){
24892         this.fields = this.recordType.prototype.fields;
24893     }
24894     this.modified = [];
24895
24896     this.addEvents({
24897         /**
24898          * @event datachanged
24899          * Fires when the data cache has changed, and a widget which is using this Store
24900          * as a Record cache should refresh its view.
24901          * @param {Store} this
24902          */
24903         datachanged : true,
24904         /**
24905          * @event metachange
24906          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24907          * @param {Store} this
24908          * @param {Object} meta The JSON metadata
24909          */
24910         metachange : true,
24911         /**
24912          * @event add
24913          * Fires when Records have been added to the Store
24914          * @param {Store} this
24915          * @param {Roo.data.Record[]} records The array of Records added
24916          * @param {Number} index The index at which the record(s) were added
24917          */
24918         add : true,
24919         /**
24920          * @event remove
24921          * Fires when a Record has been removed from the Store
24922          * @param {Store} this
24923          * @param {Roo.data.Record} record The Record that was removed
24924          * @param {Number} index The index at which the record was removed
24925          */
24926         remove : true,
24927         /**
24928          * @event update
24929          * Fires when a Record has been updated
24930          * @param {Store} this
24931          * @param {Roo.data.Record} record The Record that was updated
24932          * @param {String} operation The update operation being performed.  Value may be one of:
24933          * <pre><code>
24934  Roo.data.Record.EDIT
24935  Roo.data.Record.REJECT
24936  Roo.data.Record.COMMIT
24937          * </code></pre>
24938          */
24939         update : true,
24940         /**
24941          * @event clear
24942          * Fires when the data cache has been cleared.
24943          * @param {Store} this
24944          */
24945         clear : true,
24946         /**
24947          * @event beforeload
24948          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24949          * the load action will be canceled.
24950          * @param {Store} this
24951          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24952          */
24953         beforeload : true,
24954         /**
24955          * @event beforeloadadd
24956          * Fires after a new set of Records has been loaded.
24957          * @param {Store} this
24958          * @param {Roo.data.Record[]} records The Records that were loaded
24959          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24960          */
24961         beforeloadadd : true,
24962         /**
24963          * @event load
24964          * Fires after a new set of Records has been loaded, before they are added to the store.
24965          * @param {Store} this
24966          * @param {Roo.data.Record[]} records The Records that were loaded
24967          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24968          * @params {Object} return from reader
24969          */
24970         load : true,
24971         /**
24972          * @event loadexception
24973          * Fires if an exception occurs in the Proxy during loading.
24974          * Called with the signature of the Proxy's "loadexception" event.
24975          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24976          * 
24977          * @param {Proxy} 
24978          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24979          * @param {Object} load options 
24980          * @param {Object} jsonData from your request (normally this contains the Exception)
24981          */
24982         loadexception : true
24983     });
24984     
24985     if(this.proxy){
24986         this.proxy = Roo.factory(this.proxy, Roo.data);
24987         this.proxy.xmodule = this.xmodule || false;
24988         this.relayEvents(this.proxy,  ["loadexception"]);
24989     }
24990     this.sortToggle = {};
24991     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24992
24993     Roo.data.Store.superclass.constructor.call(this);
24994
24995     if(this.inlineData){
24996         this.loadData(this.inlineData);
24997         delete this.inlineData;
24998     }
24999 };
25000
25001 Roo.extend(Roo.data.Store, Roo.util.Observable, {
25002      /**
25003     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
25004     * without a remote query - used by combo/forms at present.
25005     */
25006     
25007     /**
25008     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
25009     */
25010     /**
25011     * @cfg {Array} data Inline data to be loaded when the store is initialized.
25012     */
25013     /**
25014     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
25015     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
25016     */
25017     /**
25018     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
25019     * on any HTTP request
25020     */
25021     /**
25022     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
25023     */
25024     /**
25025     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25026     */
25027     multiSort: false,
25028     /**
25029     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25030     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25031     */
25032     remoteSort : false,
25033
25034     /**
25035     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25036      * loaded or when a record is removed. (defaults to false).
25037     */
25038     pruneModifiedRecords : false,
25039
25040     // private
25041     lastOptions : null,
25042
25043     /**
25044      * Add Records to the Store and fires the add event.
25045      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25046      */
25047     add : function(records){
25048         records = [].concat(records);
25049         for(var i = 0, len = records.length; i < len; i++){
25050             records[i].join(this);
25051         }
25052         var index = this.data.length;
25053         this.data.addAll(records);
25054         this.fireEvent("add", this, records, index);
25055     },
25056
25057     /**
25058      * Remove a Record from the Store and fires the remove event.
25059      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25060      */
25061     remove : function(record){
25062         var index = this.data.indexOf(record);
25063         this.data.removeAt(index);
25064  
25065         if(this.pruneModifiedRecords){
25066             this.modified.remove(record);
25067         }
25068         this.fireEvent("remove", this, record, index);
25069     },
25070
25071     /**
25072      * Remove all Records from the Store and fires the clear event.
25073      */
25074     removeAll : function(){
25075         this.data.clear();
25076         if(this.pruneModifiedRecords){
25077             this.modified = [];
25078         }
25079         this.fireEvent("clear", this);
25080     },
25081
25082     /**
25083      * Inserts Records to the Store at the given index and fires the add event.
25084      * @param {Number} index The start index at which to insert the passed Records.
25085      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25086      */
25087     insert : function(index, records){
25088         records = [].concat(records);
25089         for(var i = 0, len = records.length; i < len; i++){
25090             this.data.insert(index, records[i]);
25091             records[i].join(this);
25092         }
25093         this.fireEvent("add", this, records, index);
25094     },
25095
25096     /**
25097      * Get the index within the cache of the passed Record.
25098      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25099      * @return {Number} The index of the passed Record. Returns -1 if not found.
25100      */
25101     indexOf : function(record){
25102         return this.data.indexOf(record);
25103     },
25104
25105     /**
25106      * Get the index within the cache of the Record with the passed id.
25107      * @param {String} id The id of the Record to find.
25108      * @return {Number} The index of the Record. Returns -1 if not found.
25109      */
25110     indexOfId : function(id){
25111         return this.data.indexOfKey(id);
25112     },
25113
25114     /**
25115      * Get the Record with the specified id.
25116      * @param {String} id The id of the Record to find.
25117      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25118      */
25119     getById : function(id){
25120         return this.data.key(id);
25121     },
25122
25123     /**
25124      * Get the Record at the specified index.
25125      * @param {Number} index The index of the Record to find.
25126      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25127      */
25128     getAt : function(index){
25129         return this.data.itemAt(index);
25130     },
25131
25132     /**
25133      * Returns a range of Records between specified indices.
25134      * @param {Number} startIndex (optional) The starting index (defaults to 0)
25135      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25136      * @return {Roo.data.Record[]} An array of Records
25137      */
25138     getRange : function(start, end){
25139         return this.data.getRange(start, end);
25140     },
25141
25142     // private
25143     storeOptions : function(o){
25144         o = Roo.apply({}, o);
25145         delete o.callback;
25146         delete o.scope;
25147         this.lastOptions = o;
25148     },
25149
25150     /**
25151      * Loads the Record cache from the configured Proxy using the configured Reader.
25152      * <p>
25153      * If using remote paging, then the first load call must specify the <em>start</em>
25154      * and <em>limit</em> properties in the options.params property to establish the initial
25155      * position within the dataset, and the number of Records to cache on each read from the Proxy.
25156      * <p>
25157      * <strong>It is important to note that for remote data sources, loading is asynchronous,
25158      * and this call will return before the new data has been loaded. Perform any post-processing
25159      * in a callback function, or in a "load" event handler.</strong>
25160      * <p>
25161      * @param {Object} options An object containing properties which control loading options:<ul>
25162      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25163      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25164      * <pre>
25165                 {
25166                     data : data,  // array of key=>value data like JsonReader
25167                     total : data.length,
25168                     success : true
25169                     
25170                 }
25171         </pre>
25172             }.</li>
25173      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25174      * passed the following arguments:<ul>
25175      * <li>r : Roo.data.Record[]</li>
25176      * <li>options: Options object from the load call</li>
25177      * <li>success: Boolean success indicator</li></ul></li>
25178      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25179      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25180      * </ul>
25181      */
25182     load : function(options){
25183         options = options || {};
25184         if(this.fireEvent("beforeload", this, options) !== false){
25185             this.storeOptions(options);
25186             var p = Roo.apply(options.params || {}, this.baseParams);
25187             // if meta was not loaded from remote source.. try requesting it.
25188             if (!this.reader.metaFromRemote) {
25189                 p._requestMeta = 1;
25190             }
25191             if(this.sortInfo && this.remoteSort){
25192                 var pn = this.paramNames;
25193                 p[pn["sort"]] = this.sortInfo.field;
25194                 p[pn["dir"]] = this.sortInfo.direction;
25195             }
25196             if (this.multiSort) {
25197                 var pn = this.paramNames;
25198                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25199             }
25200             
25201             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25202         }
25203     },
25204
25205     /**
25206      * Reloads the Record cache from the configured Proxy using the configured Reader and
25207      * the options from the last load operation performed.
25208      * @param {Object} options (optional) An object containing properties which may override the options
25209      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25210      * the most recently used options are reused).
25211      */
25212     reload : function(options){
25213         this.load(Roo.applyIf(options||{}, this.lastOptions));
25214     },
25215
25216     // private
25217     // Called as a callback by the Reader during a load operation.
25218     loadRecords : function(o, options, success){
25219          
25220         if(!o){
25221             if(success !== false){
25222                 this.fireEvent("load", this, [], options, o);
25223             }
25224             if(options.callback){
25225                 options.callback.call(options.scope || this, [], options, false);
25226             }
25227             return;
25228         }
25229         // if data returned failure - throw an exception.
25230         if (o.success === false) {
25231             // show a message if no listener is registered.
25232             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25233                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25234             }
25235             // loadmask wil be hooked into this..
25236             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25237             return;
25238         }
25239         var r = o.records, t = o.totalRecords || r.length;
25240         
25241         this.fireEvent("beforeloadadd", this, r, options, o);
25242         
25243         if(!options || options.add !== true){
25244             if(this.pruneModifiedRecords){
25245                 this.modified = [];
25246             }
25247             for(var i = 0, len = r.length; i < len; i++){
25248                 r[i].join(this);
25249             }
25250             if(this.snapshot){
25251                 this.data = this.snapshot;
25252                 delete this.snapshot;
25253             }
25254             this.data.clear();
25255             this.data.addAll(r);
25256             this.totalLength = t;
25257             this.applySort();
25258             this.fireEvent("datachanged", this);
25259         }else{
25260             this.totalLength = Math.max(t, this.data.length+r.length);
25261             this.add(r);
25262         }
25263         
25264         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25265                 
25266             var e = new Roo.data.Record({});
25267
25268             e.set(this.parent.displayField, this.parent.emptyTitle);
25269             e.set(this.parent.valueField, '');
25270
25271             this.insert(0, e);
25272         }
25273             
25274         this.fireEvent("load", this, r, options, o);
25275         if(options.callback){
25276             options.callback.call(options.scope || this, r, options, true);
25277         }
25278     },
25279
25280
25281     /**
25282      * Loads data from a passed data block. A Reader which understands the format of the data
25283      * must have been configured in the constructor.
25284      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25285      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25286      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25287      */
25288     loadData : function(o, append){
25289         var r = this.reader.readRecords(o);
25290         this.loadRecords(r, {add: append}, true);
25291     },
25292     
25293      /**
25294      * using 'cn' the nested child reader read the child array into it's child stores.
25295      * @param {Object} rec The record with a 'children array
25296      */
25297     loadDataFromChildren : function(rec)
25298     {
25299         this.loadData(this.reader.toLoadData(rec));
25300     },
25301     
25302
25303     /**
25304      * Gets the number of cached records.
25305      * <p>
25306      * <em>If using paging, this may not be the total size of the dataset. If the data object
25307      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25308      * the data set size</em>
25309      */
25310     getCount : function(){
25311         return this.data.length || 0;
25312     },
25313
25314     /**
25315      * Gets the total number of records in the dataset as returned by the server.
25316      * <p>
25317      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25318      * the dataset size</em>
25319      */
25320     getTotalCount : function(){
25321         return this.totalLength || 0;
25322     },
25323
25324     /**
25325      * Returns the sort state of the Store as an object with two properties:
25326      * <pre><code>
25327  field {String} The name of the field by which the Records are sorted
25328  direction {String} The sort order, "ASC" or "DESC"
25329      * </code></pre>
25330      */
25331     getSortState : function(){
25332         return this.sortInfo;
25333     },
25334
25335     // private
25336     applySort : function(){
25337         if(this.sortInfo && !this.remoteSort){
25338             var s = this.sortInfo, f = s.field;
25339             var st = this.fields.get(f).sortType;
25340             var fn = function(r1, r2){
25341                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25342                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25343             };
25344             this.data.sort(s.direction, fn);
25345             if(this.snapshot && this.snapshot != this.data){
25346                 this.snapshot.sort(s.direction, fn);
25347             }
25348         }
25349     },
25350
25351     /**
25352      * Sets the default sort column and order to be used by the next load operation.
25353      * @param {String} fieldName The name of the field to sort by.
25354      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25355      */
25356     setDefaultSort : function(field, dir){
25357         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25358     },
25359
25360     /**
25361      * Sort the Records.
25362      * If remote sorting is used, the sort is performed on the server, and the cache is
25363      * reloaded. If local sorting is used, the cache is sorted internally.
25364      * @param {String} fieldName The name of the field to sort by.
25365      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25366      */
25367     sort : function(fieldName, dir){
25368         var f = this.fields.get(fieldName);
25369         if(!dir){
25370             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25371             
25372             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25373                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25374             }else{
25375                 dir = f.sortDir;
25376             }
25377         }
25378         this.sortToggle[f.name] = dir;
25379         this.sortInfo = {field: f.name, direction: dir};
25380         if(!this.remoteSort){
25381             this.applySort();
25382             this.fireEvent("datachanged", this);
25383         }else{
25384             this.load(this.lastOptions);
25385         }
25386     },
25387
25388     /**
25389      * Calls the specified function for each of the Records in the cache.
25390      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25391      * Returning <em>false</em> aborts and exits the iteration.
25392      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25393      */
25394     each : function(fn, scope){
25395         this.data.each(fn, scope);
25396     },
25397
25398     /**
25399      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25400      * (e.g., during paging).
25401      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25402      */
25403     getModifiedRecords : function(){
25404         return this.modified;
25405     },
25406
25407     // private
25408     createFilterFn : function(property, value, anyMatch){
25409         if(!value.exec){ // not a regex
25410             value = String(value);
25411             if(value.length == 0){
25412                 return false;
25413             }
25414             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25415         }
25416         return function(r){
25417             return value.test(r.data[property]);
25418         };
25419     },
25420
25421     /**
25422      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25423      * @param {String} property A field on your records
25424      * @param {Number} start The record index to start at (defaults to 0)
25425      * @param {Number} end The last record index to include (defaults to length - 1)
25426      * @return {Number} The sum
25427      */
25428     sum : function(property, start, end){
25429         var rs = this.data.items, v = 0;
25430         start = start || 0;
25431         end = (end || end === 0) ? end : rs.length-1;
25432
25433         for(var i = start; i <= end; i++){
25434             v += (rs[i].data[property] || 0);
25435         }
25436         return v;
25437     },
25438
25439     /**
25440      * Filter the records by a specified property.
25441      * @param {String} field A field on your records
25442      * @param {String/RegExp} value Either a string that the field
25443      * should start with or a RegExp to test against the field
25444      * @param {Boolean} anyMatch True to match any part not just the beginning
25445      */
25446     filter : function(property, value, anyMatch){
25447         var fn = this.createFilterFn(property, value, anyMatch);
25448         return fn ? this.filterBy(fn) : this.clearFilter();
25449     },
25450
25451     /**
25452      * Filter by a function. The specified function will be called with each
25453      * record in this data source. If the function returns true the record is included,
25454      * otherwise it is filtered.
25455      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25456      * @param {Object} scope (optional) The scope of the function (defaults to this)
25457      */
25458     filterBy : function(fn, scope){
25459         this.snapshot = this.snapshot || this.data;
25460         this.data = this.queryBy(fn, scope||this);
25461         this.fireEvent("datachanged", this);
25462     },
25463
25464     /**
25465      * Query the records by a specified property.
25466      * @param {String} field A field on your records
25467      * @param {String/RegExp} value Either a string that the field
25468      * should start with or a RegExp to test against the field
25469      * @param {Boolean} anyMatch True to match any part not just the beginning
25470      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25471      */
25472     query : function(property, value, anyMatch){
25473         var fn = this.createFilterFn(property, value, anyMatch);
25474         return fn ? this.queryBy(fn) : this.data.clone();
25475     },
25476
25477     /**
25478      * Query by a function. The specified function will be called with each
25479      * record in this data source. If the function returns true the record is included
25480      * in the results.
25481      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25482      * @param {Object} scope (optional) The scope of the function (defaults to this)
25483       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25484      **/
25485     queryBy : function(fn, scope){
25486         var data = this.snapshot || this.data;
25487         return data.filterBy(fn, scope||this);
25488     },
25489
25490     /**
25491      * Collects unique values for a particular dataIndex from this store.
25492      * @param {String} dataIndex The property to collect
25493      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25494      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25495      * @return {Array} An array of the unique values
25496      **/
25497     collect : function(dataIndex, allowNull, bypassFilter){
25498         var d = (bypassFilter === true && this.snapshot) ?
25499                 this.snapshot.items : this.data.items;
25500         var v, sv, r = [], l = {};
25501         for(var i = 0, len = d.length; i < len; i++){
25502             v = d[i].data[dataIndex];
25503             sv = String(v);
25504             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25505                 l[sv] = true;
25506                 r[r.length] = v;
25507             }
25508         }
25509         return r;
25510     },
25511
25512     /**
25513      * Revert to a view of the Record cache with no filtering applied.
25514      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25515      */
25516     clearFilter : function(suppressEvent){
25517         if(this.snapshot && this.snapshot != this.data){
25518             this.data = this.snapshot;
25519             delete this.snapshot;
25520             if(suppressEvent !== true){
25521                 this.fireEvent("datachanged", this);
25522             }
25523         }
25524     },
25525
25526     // private
25527     afterEdit : function(record){
25528         if(this.modified.indexOf(record) == -1){
25529             this.modified.push(record);
25530         }
25531         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25532     },
25533     
25534     // private
25535     afterReject : function(record){
25536         this.modified.remove(record);
25537         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25538     },
25539
25540     // private
25541     afterCommit : function(record){
25542         this.modified.remove(record);
25543         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25544     },
25545
25546     /**
25547      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25548      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25549      */
25550     commitChanges : function(){
25551         var m = this.modified.slice(0);
25552         this.modified = [];
25553         for(var i = 0, len = m.length; i < len; i++){
25554             m[i].commit();
25555         }
25556     },
25557
25558     /**
25559      * Cancel outstanding changes on all changed records.
25560      */
25561     rejectChanges : function(){
25562         var m = this.modified.slice(0);
25563         this.modified = [];
25564         for(var i = 0, len = m.length; i < len; i++){
25565             m[i].reject();
25566         }
25567     },
25568
25569     onMetaChange : function(meta, rtype, o){
25570         this.recordType = rtype;
25571         this.fields = rtype.prototype.fields;
25572         delete this.snapshot;
25573         this.sortInfo = meta.sortInfo || this.sortInfo;
25574         this.modified = [];
25575         this.fireEvent('metachange', this, this.reader.meta);
25576     },
25577     
25578     moveIndex : function(data, type)
25579     {
25580         var index = this.indexOf(data);
25581         
25582         var newIndex = index + type;
25583         
25584         this.remove(data);
25585         
25586         this.insert(newIndex, data);
25587         
25588     }
25589 });/*
25590  * Based on:
25591  * Ext JS Library 1.1.1
25592  * Copyright(c) 2006-2007, Ext JS, LLC.
25593  *
25594  * Originally Released Under LGPL - original licence link has changed is not relivant.
25595  *
25596  * Fork - LGPL
25597  * <script type="text/javascript">
25598  */
25599
25600 /**
25601  * @class Roo.data.SimpleStore
25602  * @extends Roo.data.Store
25603  * Small helper class to make creating Stores from Array data easier.
25604  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25605  * @cfg {Array} fields An array of field definition objects, or field name strings.
25606  * @cfg {Object} an existing reader (eg. copied from another store)
25607  * @cfg {Array} data The multi-dimensional array of data
25608  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25609  * @cfg {Roo.data.Reader} reader  [not-required] 
25610  * @constructor
25611  * @param {Object} config
25612  */
25613 Roo.data.SimpleStore = function(config)
25614 {
25615     Roo.data.SimpleStore.superclass.constructor.call(this, {
25616         isLocal : true,
25617         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25618                 id: config.id
25619             },
25620             Roo.data.Record.create(config.fields)
25621         ),
25622         proxy : new Roo.data.MemoryProxy(config.data)
25623     });
25624     this.load();
25625 };
25626 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25627  * Based on:
25628  * Ext JS Library 1.1.1
25629  * Copyright(c) 2006-2007, Ext JS, LLC.
25630  *
25631  * Originally Released Under LGPL - original licence link has changed is not relivant.
25632  *
25633  * Fork - LGPL
25634  * <script type="text/javascript">
25635  */
25636
25637 /**
25638 /**
25639  * @extends Roo.data.Store
25640  * @class Roo.data.JsonStore
25641  * Small helper class to make creating Stores for JSON data easier. <br/>
25642 <pre><code>
25643 var store = new Roo.data.JsonStore({
25644     url: 'get-images.php',
25645     root: 'images',
25646     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25647 });
25648 </code></pre>
25649  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25650  * JsonReader and HttpProxy (unless inline data is provided).</b>
25651  * @cfg {Array} fields An array of field definition objects, or field name strings.
25652  * @constructor
25653  * @param {Object} config
25654  */
25655 Roo.data.JsonStore = function(c){
25656     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25657         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25658         reader: new Roo.data.JsonReader(c, c.fields)
25659     }));
25660 };
25661 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25662  * Based on:
25663  * Ext JS Library 1.1.1
25664  * Copyright(c) 2006-2007, Ext JS, LLC.
25665  *
25666  * Originally Released Under LGPL - original licence link has changed is not relivant.
25667  *
25668  * Fork - LGPL
25669  * <script type="text/javascript">
25670  */
25671
25672  
25673 Roo.data.Field = function(config){
25674     if(typeof config == "string"){
25675         config = {name: config};
25676     }
25677     Roo.apply(this, config);
25678     
25679     if(!this.type){
25680         this.type = "auto";
25681     }
25682     
25683     var st = Roo.data.SortTypes;
25684     // named sortTypes are supported, here we look them up
25685     if(typeof this.sortType == "string"){
25686         this.sortType = st[this.sortType];
25687     }
25688     
25689     // set default sortType for strings and dates
25690     if(!this.sortType){
25691         switch(this.type){
25692             case "string":
25693                 this.sortType = st.asUCString;
25694                 break;
25695             case "date":
25696                 this.sortType = st.asDate;
25697                 break;
25698             default:
25699                 this.sortType = st.none;
25700         }
25701     }
25702
25703     // define once
25704     var stripRe = /[\$,%]/g;
25705
25706     // prebuilt conversion function for this field, instead of
25707     // switching every time we're reading a value
25708     if(!this.convert){
25709         var cv, dateFormat = this.dateFormat;
25710         switch(this.type){
25711             case "":
25712             case "auto":
25713             case undefined:
25714                 cv = function(v){ return v; };
25715                 break;
25716             case "string":
25717                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25718                 break;
25719             case "int":
25720                 cv = function(v){
25721                     return v !== undefined && v !== null && v !== '' ?
25722                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25723                     };
25724                 break;
25725             case "float":
25726                 cv = function(v){
25727                     return v !== undefined && v !== null && v !== '' ?
25728                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25729                     };
25730                 break;
25731             case "bool":
25732             case "boolean":
25733                 cv = function(v){ return v === true || v === "true" || v == 1; };
25734                 break;
25735             case "date":
25736                 cv = function(v){
25737                     if(!v){
25738                         return '';
25739                     }
25740                     if(v instanceof Date){
25741                         return v;
25742                     }
25743                     if(dateFormat){
25744                         if(dateFormat == "timestamp"){
25745                             return new Date(v*1000);
25746                         }
25747                         return Date.parseDate(v, dateFormat);
25748                     }
25749                     var parsed = Date.parse(v);
25750                     return parsed ? new Date(parsed) : null;
25751                 };
25752              break;
25753             
25754         }
25755         this.convert = cv;
25756     }
25757 };
25758
25759 Roo.data.Field.prototype = {
25760     dateFormat: null,
25761     defaultValue: "",
25762     mapping: null,
25763     sortType : null,
25764     sortDir : "ASC"
25765 };/*
25766  * Based on:
25767  * Ext JS Library 1.1.1
25768  * Copyright(c) 2006-2007, Ext JS, LLC.
25769  *
25770  * Originally Released Under LGPL - original licence link has changed is not relivant.
25771  *
25772  * Fork - LGPL
25773  * <script type="text/javascript">
25774  */
25775  
25776 // Base class for reading structured data from a data source.  This class is intended to be
25777 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25778
25779 /**
25780  * @class Roo.data.DataReader
25781  * @abstract
25782  * Base class for reading structured data from a data source.  This class is intended to be
25783  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25784  */
25785
25786 Roo.data.DataReader = function(meta, recordType){
25787     
25788     this.meta = meta;
25789     
25790     this.recordType = recordType instanceof Array ? 
25791         Roo.data.Record.create(recordType) : recordType;
25792 };
25793
25794 Roo.data.DataReader.prototype = {
25795     
25796     
25797     readerType : 'Data',
25798      /**
25799      * Create an empty record
25800      * @param {Object} data (optional) - overlay some values
25801      * @return {Roo.data.Record} record created.
25802      */
25803     newRow :  function(d) {
25804         var da =  {};
25805         this.recordType.prototype.fields.each(function(c) {
25806             switch( c.type) {
25807                 case 'int' : da[c.name] = 0; break;
25808                 case 'date' : da[c.name] = new Date(); break;
25809                 case 'float' : da[c.name] = 0.0; break;
25810                 case 'boolean' : da[c.name] = false; break;
25811                 default : da[c.name] = ""; break;
25812             }
25813             
25814         });
25815         return new this.recordType(Roo.apply(da, d));
25816     }
25817     
25818     
25819 };/*
25820  * Based on:
25821  * Ext JS Library 1.1.1
25822  * Copyright(c) 2006-2007, Ext JS, LLC.
25823  *
25824  * Originally Released Under LGPL - original licence link has changed is not relivant.
25825  *
25826  * Fork - LGPL
25827  * <script type="text/javascript">
25828  */
25829
25830 /**
25831  * @class Roo.data.DataProxy
25832  * @extends Roo.util.Observable
25833  * @abstract
25834  * This class is an abstract base class for implementations which provide retrieval of
25835  * unformatted data objects.<br>
25836  * <p>
25837  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25838  * (of the appropriate type which knows how to parse the data object) to provide a block of
25839  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25840  * <p>
25841  * Custom implementations must implement the load method as described in
25842  * {@link Roo.data.HttpProxy#load}.
25843  */
25844 Roo.data.DataProxy = function(){
25845     this.addEvents({
25846         /**
25847          * @event beforeload
25848          * Fires before a network request is made to retrieve a data object.
25849          * @param {Object} This DataProxy object.
25850          * @param {Object} params The params parameter to the load function.
25851          */
25852         beforeload : true,
25853         /**
25854          * @event load
25855          * Fires before the load method's callback is called.
25856          * @param {Object} This DataProxy object.
25857          * @param {Object} o The data object.
25858          * @param {Object} arg The callback argument object passed to the load function.
25859          */
25860         load : true,
25861         /**
25862          * @event loadexception
25863          * Fires if an Exception occurs during data retrieval.
25864          * @param {Object} This DataProxy object.
25865          * @param {Object} o The data object.
25866          * @param {Object} arg The callback argument object passed to the load function.
25867          * @param {Object} e The Exception.
25868          */
25869         loadexception : true
25870     });
25871     Roo.data.DataProxy.superclass.constructor.call(this);
25872 };
25873
25874 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25875
25876     /**
25877      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25878      */
25879 /*
25880  * Based on:
25881  * Ext JS Library 1.1.1
25882  * Copyright(c) 2006-2007, Ext JS, LLC.
25883  *
25884  * Originally Released Under LGPL - original licence link has changed is not relivant.
25885  *
25886  * Fork - LGPL
25887  * <script type="text/javascript">
25888  */
25889 /**
25890  * @class Roo.data.MemoryProxy
25891  * @extends Roo.data.DataProxy
25892  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25893  * to the Reader when its load method is called.
25894  * @constructor
25895  * @param {Object} config  A config object containing the objects needed for the Store to access data,
25896  */
25897 Roo.data.MemoryProxy = function(config){
25898     var data = config;
25899     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
25900         data = config.data;
25901     }
25902     Roo.data.MemoryProxy.superclass.constructor.call(this);
25903     this.data = data;
25904 };
25905
25906 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25907     
25908     /**
25909      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25910      */
25911     /**
25912      * Load data from the requested source (in this case an in-memory
25913      * data object passed to the constructor), read the data object into
25914      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25915      * process that block using the passed callback.
25916      * @param {Object} params This parameter is not used by the MemoryProxy class.
25917      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25918      * object into a block of Roo.data.Records.
25919      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25920      * The function must be passed <ul>
25921      * <li>The Record block object</li>
25922      * <li>The "arg" argument from the load function</li>
25923      * <li>A boolean success indicator</li>
25924      * </ul>
25925      * @param {Object} scope The scope in which to call the callback
25926      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25927      */
25928     load : function(params, reader, callback, scope, arg){
25929         params = params || {};
25930         var result;
25931         try {
25932             result = reader.readRecords(params.data ? params.data :this.data);
25933         }catch(e){
25934             this.fireEvent("loadexception", this, arg, null, e);
25935             callback.call(scope, null, arg, false);
25936             return;
25937         }
25938         callback.call(scope, result, arg, true);
25939     },
25940     
25941     // private
25942     update : function(params, records){
25943         
25944     }
25945 });/*
25946  * Based on:
25947  * Ext JS Library 1.1.1
25948  * Copyright(c) 2006-2007, Ext JS, LLC.
25949  *
25950  * Originally Released Under LGPL - original licence link has changed is not relivant.
25951  *
25952  * Fork - LGPL
25953  * <script type="text/javascript">
25954  */
25955 /**
25956  * @class Roo.data.HttpProxy
25957  * @extends Roo.data.DataProxy
25958  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25959  * configured to reference a certain URL.<br><br>
25960  * <p>
25961  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25962  * from which the running page was served.<br><br>
25963  * <p>
25964  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25965  * <p>
25966  * Be aware that to enable the browser to parse an XML document, the server must set
25967  * the Content-Type header in the HTTP response to "text/xml".
25968  * @constructor
25969  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25970  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25971  * will be used to make the request.
25972  */
25973 Roo.data.HttpProxy = function(conn){
25974     Roo.data.HttpProxy.superclass.constructor.call(this);
25975     // is conn a conn config or a real conn?
25976     this.conn = conn;
25977     this.useAjax = !conn || !conn.events;
25978   
25979 };
25980
25981 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25982     // thse are take from connection...
25983     
25984     /**
25985      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25986      */
25987     /**
25988      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25989      * extra parameters to each request made by this object. (defaults to undefined)
25990      */
25991     /**
25992      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25993      *  to each request made by this object. (defaults to undefined)
25994      */
25995     /**
25996      * @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)
25997      */
25998     /**
25999      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
26000      */
26001      /**
26002      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
26003      * @type Boolean
26004      */
26005   
26006
26007     /**
26008      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
26009      * @type Boolean
26010      */
26011     /**
26012      * Return the {@link Roo.data.Connection} object being used by this Proxy.
26013      * @return {Connection} The Connection object. This object may be used to subscribe to events on
26014      * a finer-grained basis than the DataProxy events.
26015      */
26016     getConnection : function(){
26017         return this.useAjax ? Roo.Ajax : this.conn;
26018     },
26019
26020     /**
26021      * Load data from the configured {@link Roo.data.Connection}, read the data object into
26022      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
26023      * process that block using the passed callback.
26024      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26025      * for the request to the remote server.
26026      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26027      * object into a block of Roo.data.Records.
26028      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26029      * The function must be passed <ul>
26030      * <li>The Record block object</li>
26031      * <li>The "arg" argument from the load function</li>
26032      * <li>A boolean success indicator</li>
26033      * </ul>
26034      * @param {Object} scope The scope in which to call the callback
26035      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26036      */
26037     load : function(params, reader, callback, scope, arg){
26038         if(this.fireEvent("beforeload", this, params) !== false){
26039             var  o = {
26040                 params : params || {},
26041                 request: {
26042                     callback : callback,
26043                     scope : scope,
26044                     arg : arg
26045                 },
26046                 reader: reader,
26047                 callback : this.loadResponse,
26048                 scope: this
26049             };
26050             if(this.useAjax){
26051                 Roo.applyIf(o, this.conn);
26052                 if(this.activeRequest){
26053                     Roo.Ajax.abort(this.activeRequest);
26054                 }
26055                 this.activeRequest = Roo.Ajax.request(o);
26056             }else{
26057                 this.conn.request(o);
26058             }
26059         }else{
26060             callback.call(scope||this, null, arg, false);
26061         }
26062     },
26063
26064     // private
26065     loadResponse : function(o, success, response){
26066         delete this.activeRequest;
26067         if(!success){
26068             this.fireEvent("loadexception", this, o, response);
26069             o.request.callback.call(o.request.scope, null, o.request.arg, false);
26070             return;
26071         }
26072         var result;
26073         try {
26074             result = o.reader.read(response);
26075         }catch(e){
26076             o.success = false;
26077             o.raw = { errorMsg : response.responseText };
26078             this.fireEvent("loadexception", this, o, response, e);
26079             o.request.callback.call(o.request.scope, o, o.request.arg, false);
26080             return;
26081         }
26082         
26083         this.fireEvent("load", this, o, o.request.arg);
26084         o.request.callback.call(o.request.scope, result, o.request.arg, true);
26085     },
26086
26087     // private
26088     update : function(dataSet){
26089
26090     },
26091
26092     // private
26093     updateResponse : function(dataSet){
26094
26095     }
26096 });/*
26097  * Based on:
26098  * Ext JS Library 1.1.1
26099  * Copyright(c) 2006-2007, Ext JS, LLC.
26100  *
26101  * Originally Released Under LGPL - original licence link has changed is not relivant.
26102  *
26103  * Fork - LGPL
26104  * <script type="text/javascript">
26105  */
26106
26107 /**
26108  * @class Roo.data.ScriptTagProxy
26109  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26110  * other than the originating domain of the running page.<br><br>
26111  * <p>
26112  * <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
26113  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26114  * <p>
26115  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26116  * source code that is used as the source inside a &lt;script> tag.<br><br>
26117  * <p>
26118  * In order for the browser to process the returned data, the server must wrap the data object
26119  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26120  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26121  * depending on whether the callback name was passed:
26122  * <p>
26123  * <pre><code>
26124 boolean scriptTag = false;
26125 String cb = request.getParameter("callback");
26126 if (cb != null) {
26127     scriptTag = true;
26128     response.setContentType("text/javascript");
26129 } else {
26130     response.setContentType("application/x-json");
26131 }
26132 Writer out = response.getWriter();
26133 if (scriptTag) {
26134     out.write(cb + "(");
26135 }
26136 out.print(dataBlock.toJsonString());
26137 if (scriptTag) {
26138     out.write(");");
26139 }
26140 </pre></code>
26141  *
26142  * @constructor
26143  * @param {Object} config A configuration object.
26144  */
26145 Roo.data.ScriptTagProxy = function(config){
26146     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26147     Roo.apply(this, config);
26148     this.head = document.getElementsByTagName("head")[0];
26149 };
26150
26151 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26152
26153 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26154     /**
26155      * @cfg {String} url The URL from which to request the data object.
26156      */
26157     /**
26158      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26159      */
26160     timeout : 30000,
26161     /**
26162      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26163      * the server the name of the callback function set up by the load call to process the returned data object.
26164      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26165      * javascript output which calls this named function passing the data object as its only parameter.
26166      */
26167     callbackParam : "callback",
26168     /**
26169      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26170      * name to the request.
26171      */
26172     nocache : true,
26173
26174     /**
26175      * Load data from the configured URL, read the data object into
26176      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26177      * process that block using the passed callback.
26178      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26179      * for the request to the remote server.
26180      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26181      * object into a block of Roo.data.Records.
26182      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26183      * The function must be passed <ul>
26184      * <li>The Record block object</li>
26185      * <li>The "arg" argument from the load function</li>
26186      * <li>A boolean success indicator</li>
26187      * </ul>
26188      * @param {Object} scope The scope in which to call the callback
26189      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26190      */
26191     load : function(params, reader, callback, scope, arg){
26192         if(this.fireEvent("beforeload", this, params) !== false){
26193
26194             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26195
26196             var url = this.url;
26197             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26198             if(this.nocache){
26199                 url += "&_dc=" + (new Date().getTime());
26200             }
26201             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26202             var trans = {
26203                 id : transId,
26204                 cb : "stcCallback"+transId,
26205                 scriptId : "stcScript"+transId,
26206                 params : params,
26207                 arg : arg,
26208                 url : url,
26209                 callback : callback,
26210                 scope : scope,
26211                 reader : reader
26212             };
26213             var conn = this;
26214
26215             window[trans.cb] = function(o){
26216                 conn.handleResponse(o, trans);
26217             };
26218
26219             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26220
26221             if(this.autoAbort !== false){
26222                 this.abort();
26223             }
26224
26225             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26226
26227             var script = document.createElement("script");
26228             script.setAttribute("src", url);
26229             script.setAttribute("type", "text/javascript");
26230             script.setAttribute("id", trans.scriptId);
26231             this.head.appendChild(script);
26232
26233             this.trans = trans;
26234         }else{
26235             callback.call(scope||this, null, arg, false);
26236         }
26237     },
26238
26239     // private
26240     isLoading : function(){
26241         return this.trans ? true : false;
26242     },
26243
26244     /**
26245      * Abort the current server request.
26246      */
26247     abort : function(){
26248         if(this.isLoading()){
26249             this.destroyTrans(this.trans);
26250         }
26251     },
26252
26253     // private
26254     destroyTrans : function(trans, isLoaded){
26255         this.head.removeChild(document.getElementById(trans.scriptId));
26256         clearTimeout(trans.timeoutId);
26257         if(isLoaded){
26258             window[trans.cb] = undefined;
26259             try{
26260                 delete window[trans.cb];
26261             }catch(e){}
26262         }else{
26263             // if hasn't been loaded, wait for load to remove it to prevent script error
26264             window[trans.cb] = function(){
26265                 window[trans.cb] = undefined;
26266                 try{
26267                     delete window[trans.cb];
26268                 }catch(e){}
26269             };
26270         }
26271     },
26272
26273     // private
26274     handleResponse : function(o, trans){
26275         this.trans = false;
26276         this.destroyTrans(trans, true);
26277         var result;
26278         try {
26279             result = trans.reader.readRecords(o);
26280         }catch(e){
26281             this.fireEvent("loadexception", this, o, trans.arg, e);
26282             trans.callback.call(trans.scope||window, null, trans.arg, false);
26283             return;
26284         }
26285         this.fireEvent("load", this, o, trans.arg);
26286         trans.callback.call(trans.scope||window, result, trans.arg, true);
26287     },
26288
26289     // private
26290     handleFailure : function(trans){
26291         this.trans = false;
26292         this.destroyTrans(trans, false);
26293         this.fireEvent("loadexception", this, null, trans.arg);
26294         trans.callback.call(trans.scope||window, null, trans.arg, false);
26295     }
26296 });/*
26297  * Based on:
26298  * Ext JS Library 1.1.1
26299  * Copyright(c) 2006-2007, Ext JS, LLC.
26300  *
26301  * Originally Released Under LGPL - original licence link has changed is not relivant.
26302  *
26303  * Fork - LGPL
26304  * <script type="text/javascript">
26305  */
26306
26307 /**
26308  * @class Roo.data.JsonReader
26309  * @extends Roo.data.DataReader
26310  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26311  * based on mappings in a provided Roo.data.Record constructor.
26312  * 
26313  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26314  * in the reply previously. 
26315  * 
26316  * <p>
26317  * Example code:
26318  * <pre><code>
26319 var RecordDef = Roo.data.Record.create([
26320     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26321     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26322 ]);
26323 var myReader = new Roo.data.JsonReader({
26324     totalProperty: "results",    // The property which contains the total dataset size (optional)
26325     root: "rows",                // The property which contains an Array of row objects
26326     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26327 }, RecordDef);
26328 </code></pre>
26329  * <p>
26330  * This would consume a JSON file like this:
26331  * <pre><code>
26332 { 'results': 2, 'rows': [
26333     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26334     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26335 }
26336 </code></pre>
26337  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26338  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26339  * paged from the remote server.
26340  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26341  * @cfg {String} root name of the property which contains the Array of row objects.
26342  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26343  * @cfg {Array} fields Array of field definition objects
26344  * @constructor
26345  * Create a new JsonReader
26346  * @param {Object} meta Metadata configuration options
26347  * @param {Object} recordType Either an Array of field definition objects,
26348  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26349  */
26350 Roo.data.JsonReader = function(meta, recordType){
26351     
26352     meta = meta || {};
26353     // set some defaults:
26354     Roo.applyIf(meta, {
26355         totalProperty: 'total',
26356         successProperty : 'success',
26357         root : 'data',
26358         id : 'id'
26359     });
26360     
26361     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26362 };
26363 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26364     
26365     readerType : 'Json',
26366     
26367     /**
26368      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26369      * Used by Store query builder to append _requestMeta to params.
26370      * 
26371      */
26372     metaFromRemote : false,
26373     /**
26374      * This method is only used by a DataProxy which has retrieved data from a remote server.
26375      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26376      * @return {Object} data A data block which is used by an Roo.data.Store object as
26377      * a cache of Roo.data.Records.
26378      */
26379     read : function(response){
26380         var json = response.responseText;
26381        
26382         var o = /* eval:var:o */ eval("("+json+")");
26383         if(!o) {
26384             throw {message: "JsonReader.read: Json object not found"};
26385         }
26386         
26387         if(o.metaData){
26388             
26389             delete this.ef;
26390             this.metaFromRemote = true;
26391             this.meta = o.metaData;
26392             this.recordType = Roo.data.Record.create(o.metaData.fields);
26393             this.onMetaChange(this.meta, this.recordType, o);
26394         }
26395         return this.readRecords(o);
26396     },
26397
26398     // private function a store will implement
26399     onMetaChange : function(meta, recordType, o){
26400
26401     },
26402
26403     /**
26404          * @ignore
26405          */
26406     simpleAccess: function(obj, subsc) {
26407         return obj[subsc];
26408     },
26409
26410         /**
26411          * @ignore
26412          */
26413     getJsonAccessor: function(){
26414         var re = /[\[\.]/;
26415         return function(expr) {
26416             try {
26417                 return(re.test(expr))
26418                     ? new Function("obj", "return obj." + expr)
26419                     : function(obj){
26420                         return obj[expr];
26421                     };
26422             } catch(e){}
26423             return Roo.emptyFn;
26424         };
26425     }(),
26426
26427     /**
26428      * Create a data block containing Roo.data.Records from an XML document.
26429      * @param {Object} o An object which contains an Array of row objects in the property specified
26430      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26431      * which contains the total size of the dataset.
26432      * @return {Object} data A data block which is used by an Roo.data.Store object as
26433      * a cache of Roo.data.Records.
26434      */
26435     readRecords : function(o){
26436         /**
26437          * After any data loads, the raw JSON data is available for further custom processing.
26438          * @type Object
26439          */
26440         this.o = o;
26441         var s = this.meta, Record = this.recordType,
26442             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26443
26444 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26445         if (!this.ef) {
26446             if(s.totalProperty) {
26447                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26448                 }
26449                 if(s.successProperty) {
26450                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26451                 }
26452                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26453                 if (s.id) {
26454                         var g = this.getJsonAccessor(s.id);
26455                         this.getId = function(rec) {
26456                                 var r = g(rec);  
26457                                 return (r === undefined || r === "") ? null : r;
26458                         };
26459                 } else {
26460                         this.getId = function(){return null;};
26461                 }
26462             this.ef = [];
26463             for(var jj = 0; jj < fl; jj++){
26464                 f = fi[jj];
26465                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26466                 this.ef[jj] = this.getJsonAccessor(map);
26467             }
26468         }
26469
26470         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26471         if(s.totalProperty){
26472             var vt = parseInt(this.getTotal(o), 10);
26473             if(!isNaN(vt)){
26474                 totalRecords = vt;
26475             }
26476         }
26477         if(s.successProperty){
26478             var vs = this.getSuccess(o);
26479             if(vs === false || vs === 'false'){
26480                 success = false;
26481             }
26482         }
26483         var records = [];
26484         for(var i = 0; i < c; i++){
26485             var n = root[i];
26486             var values = {};
26487             var id = this.getId(n);
26488             for(var j = 0; j < fl; j++){
26489                 f = fi[j];
26490                                 var v = this.ef[j](n);
26491                                 if (!f.convert) {
26492                                         Roo.log('missing convert for ' + f.name);
26493                                         Roo.log(f);
26494                                         continue;
26495                                 }
26496                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26497             }
26498                         if (!Record) {
26499                                 return {
26500                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26501                                         success : false,
26502                                         records : [],
26503                                         totalRecords : 0
26504                                 };
26505                         }
26506             var record = new Record(values, id);
26507             record.json = n;
26508             records[i] = record;
26509         }
26510         return {
26511             raw : o,
26512             success : success,
26513             records : records,
26514             totalRecords : totalRecords
26515         };
26516     },
26517     // used when loading children.. @see loadDataFromChildren
26518     toLoadData: function(rec)
26519     {
26520         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26521         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26522         return { data : data, total : data.length };
26523         
26524     }
26525 });/*
26526  * Based on:
26527  * Ext JS Library 1.1.1
26528  * Copyright(c) 2006-2007, Ext JS, LLC.
26529  *
26530  * Originally Released Under LGPL - original licence link has changed is not relivant.
26531  *
26532  * Fork - LGPL
26533  * <script type="text/javascript">
26534  */
26535
26536 /**
26537  * @class Roo.data.XmlReader
26538  * @extends Roo.data.DataReader
26539  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26540  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26541  * <p>
26542  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26543  * header in the HTTP response must be set to "text/xml".</em>
26544  * <p>
26545  * Example code:
26546  * <pre><code>
26547 var RecordDef = Roo.data.Record.create([
26548    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26549    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26550 ]);
26551 var myReader = new Roo.data.XmlReader({
26552    totalRecords: "results", // The element which contains the total dataset size (optional)
26553    record: "row",           // The repeated element which contains row information
26554    id: "id"                 // The element within the row that provides an ID for the record (optional)
26555 }, RecordDef);
26556 </code></pre>
26557  * <p>
26558  * This would consume an XML file like this:
26559  * <pre><code>
26560 &lt;?xml?>
26561 &lt;dataset>
26562  &lt;results>2&lt;/results>
26563  &lt;row>
26564    &lt;id>1&lt;/id>
26565    &lt;name>Bill&lt;/name>
26566    &lt;occupation>Gardener&lt;/occupation>
26567  &lt;/row>
26568  &lt;row>
26569    &lt;id>2&lt;/id>
26570    &lt;name>Ben&lt;/name>
26571    &lt;occupation>Horticulturalist&lt;/occupation>
26572  &lt;/row>
26573 &lt;/dataset>
26574 </code></pre>
26575  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26576  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26577  * paged from the remote server.
26578  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26579  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26580  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26581  * a record identifier value.
26582  * @constructor
26583  * Create a new XmlReader
26584  * @param {Object} meta Metadata configuration options
26585  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26586  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26587  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26588  */
26589 Roo.data.XmlReader = function(meta, recordType){
26590     meta = meta || {};
26591     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26592 };
26593 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26594     
26595     readerType : 'Xml',
26596     
26597     /**
26598      * This method is only used by a DataProxy which has retrieved data from a remote server.
26599          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26600          * to contain a method called 'responseXML' that returns an XML document object.
26601      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26602      * a cache of Roo.data.Records.
26603      */
26604     read : function(response){
26605         var doc = response.responseXML;
26606         if(!doc) {
26607             throw {message: "XmlReader.read: XML Document not available"};
26608         }
26609         return this.readRecords(doc);
26610     },
26611
26612     /**
26613      * Create a data block containing Roo.data.Records from an XML document.
26614          * @param {Object} doc A parsed XML document.
26615      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26616      * a cache of Roo.data.Records.
26617      */
26618     readRecords : function(doc){
26619         /**
26620          * After any data loads/reads, the raw XML Document is available for further custom processing.
26621          * @type XMLDocument
26622          */
26623         this.xmlData = doc;
26624         var root = doc.documentElement || doc;
26625         var q = Roo.DomQuery;
26626         var recordType = this.recordType, fields = recordType.prototype.fields;
26627         var sid = this.meta.id;
26628         var totalRecords = 0, success = true;
26629         if(this.meta.totalRecords){
26630             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26631         }
26632         
26633         if(this.meta.success){
26634             var sv = q.selectValue(this.meta.success, root, true);
26635             success = sv !== false && sv !== 'false';
26636         }
26637         var records = [];
26638         var ns = q.select(this.meta.record, root);
26639         for(var i = 0, len = ns.length; i < len; i++) {
26640                 var n = ns[i];
26641                 var values = {};
26642                 var id = sid ? q.selectValue(sid, n) : undefined;
26643                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26644                     var f = fields.items[j];
26645                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26646                     v = f.convert(v);
26647                     values[f.name] = v;
26648                 }
26649                 var record = new recordType(values, id);
26650                 record.node = n;
26651                 records[records.length] = record;
26652             }
26653
26654             return {
26655                 success : success,
26656                 records : records,
26657                 totalRecords : totalRecords || records.length
26658             };
26659     }
26660 });/*
26661  * Based on:
26662  * Ext JS Library 1.1.1
26663  * Copyright(c) 2006-2007, Ext JS, LLC.
26664  *
26665  * Originally Released Under LGPL - original licence link has changed is not relivant.
26666  *
26667  * Fork - LGPL
26668  * <script type="text/javascript">
26669  */
26670
26671 /**
26672  * @class Roo.data.ArrayReader
26673  * @extends Roo.data.DataReader
26674  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26675  * Each element of that Array represents a row of data fields. The
26676  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26677  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26678  * <p>
26679  * Example code:.
26680  * <pre><code>
26681 var RecordDef = Roo.data.Record.create([
26682     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26683     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26684 ]);
26685 var myReader = new Roo.data.ArrayReader({
26686     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26687 }, RecordDef);
26688 </code></pre>
26689  * <p>
26690  * This would consume an Array like this:
26691  * <pre><code>
26692 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26693   </code></pre>
26694  
26695  * @constructor
26696  * Create a new JsonReader
26697  * @param {Object} meta Metadata configuration options.
26698  * @param {Object|Array} recordType Either an Array of field definition objects
26699  * 
26700  * @cfg {Array} fields Array of field definition objects
26701  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26702  * as specified to {@link Roo.data.Record#create},
26703  * or an {@link Roo.data.Record} object
26704  *
26705  * 
26706  * created using {@link Roo.data.Record#create}.
26707  */
26708 Roo.data.ArrayReader = function(meta, recordType)
26709 {    
26710     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26711 };
26712
26713 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26714     
26715       /**
26716      * Create a data block containing Roo.data.Records from an XML document.
26717      * @param {Object} o An Array of row objects which represents the dataset.
26718      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26719      * a cache of Roo.data.Records.
26720      */
26721     readRecords : function(o)
26722     {
26723         var sid = this.meta ? this.meta.id : null;
26724         var recordType = this.recordType, fields = recordType.prototype.fields;
26725         var records = [];
26726         var root = o;
26727         for(var i = 0; i < root.length; i++){
26728             var n = root[i];
26729             var values = {};
26730             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26731             for(var j = 0, jlen = fields.length; j < jlen; j++){
26732                 var f = fields.items[j];
26733                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26734                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26735                 v = f.convert(v);
26736                 values[f.name] = v;
26737             }
26738             var record = new recordType(values, id);
26739             record.json = n;
26740             records[records.length] = record;
26741         }
26742         return {
26743             records : records,
26744             totalRecords : records.length
26745         };
26746     },
26747     // used when loading children.. @see loadDataFromChildren
26748     toLoadData: function(rec)
26749     {
26750         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26751         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26752         
26753     }
26754     
26755     
26756 });/*
26757  * Based on:
26758  * Ext JS Library 1.1.1
26759  * Copyright(c) 2006-2007, Ext JS, LLC.
26760  *
26761  * Originally Released Under LGPL - original licence link has changed is not relivant.
26762  *
26763  * Fork - LGPL
26764  * <script type="text/javascript">
26765  */
26766
26767
26768 /**
26769  * @class Roo.data.Tree
26770  * @extends Roo.util.Observable
26771  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26772  * in the tree have most standard DOM functionality.
26773  * @constructor
26774  * @param {Node} root (optional) The root node
26775  */
26776 Roo.data.Tree = function(root){
26777    this.nodeHash = {};
26778    /**
26779     * The root node for this tree
26780     * @type Node
26781     */
26782    this.root = null;
26783    if(root){
26784        this.setRootNode(root);
26785    }
26786    this.addEvents({
26787        /**
26788         * @event append
26789         * Fires when a new child node is appended to a node in this tree.
26790         * @param {Tree} tree The owner tree
26791         * @param {Node} parent The parent node
26792         * @param {Node} node The newly appended node
26793         * @param {Number} index The index of the newly appended node
26794         */
26795        "append" : true,
26796        /**
26797         * @event remove
26798         * Fires when a child node is removed from a node in this tree.
26799         * @param {Tree} tree The owner tree
26800         * @param {Node} parent The parent node
26801         * @param {Node} node The child node removed
26802         */
26803        "remove" : true,
26804        /**
26805         * @event move
26806         * Fires when a node is moved to a new location in the tree
26807         * @param {Tree} tree The owner tree
26808         * @param {Node} node The node moved
26809         * @param {Node} oldParent The old parent of this node
26810         * @param {Node} newParent The new parent of this node
26811         * @param {Number} index The index it was moved to
26812         */
26813        "move" : true,
26814        /**
26815         * @event insert
26816         * Fires when a new child node is inserted in a node in this tree.
26817         * @param {Tree} tree The owner tree
26818         * @param {Node} parent The parent node
26819         * @param {Node} node The child node inserted
26820         * @param {Node} refNode The child node the node was inserted before
26821         */
26822        "insert" : true,
26823        /**
26824         * @event beforeappend
26825         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26826         * @param {Tree} tree The owner tree
26827         * @param {Node} parent The parent node
26828         * @param {Node} node The child node to be appended
26829         */
26830        "beforeappend" : true,
26831        /**
26832         * @event beforeremove
26833         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26834         * @param {Tree} tree The owner tree
26835         * @param {Node} parent The parent node
26836         * @param {Node} node The child node to be removed
26837         */
26838        "beforeremove" : true,
26839        /**
26840         * @event beforemove
26841         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26842         * @param {Tree} tree The owner tree
26843         * @param {Node} node The node being moved
26844         * @param {Node} oldParent The parent of the node
26845         * @param {Node} newParent The new parent the node is moving to
26846         * @param {Number} index The index it is being moved to
26847         */
26848        "beforemove" : true,
26849        /**
26850         * @event beforeinsert
26851         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26852         * @param {Tree} tree The owner tree
26853         * @param {Node} parent The parent node
26854         * @param {Node} node The child node to be inserted
26855         * @param {Node} refNode The child node the node is being inserted before
26856         */
26857        "beforeinsert" : true
26858    });
26859
26860     Roo.data.Tree.superclass.constructor.call(this);
26861 };
26862
26863 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26864     pathSeparator: "/",
26865
26866     proxyNodeEvent : function(){
26867         return this.fireEvent.apply(this, arguments);
26868     },
26869
26870     /**
26871      * Returns the root node for this tree.
26872      * @return {Node}
26873      */
26874     getRootNode : function(){
26875         return this.root;
26876     },
26877
26878     /**
26879      * Sets the root node for this tree.
26880      * @param {Node} node
26881      * @return {Node}
26882      */
26883     setRootNode : function(node){
26884         this.root = node;
26885         node.ownerTree = this;
26886         node.isRoot = true;
26887         this.registerNode(node);
26888         return node;
26889     },
26890
26891     /**
26892      * Gets a node in this tree by its id.
26893      * @param {String} id
26894      * @return {Node}
26895      */
26896     getNodeById : function(id){
26897         return this.nodeHash[id];
26898     },
26899
26900     registerNode : function(node){
26901         this.nodeHash[node.id] = node;
26902     },
26903
26904     unregisterNode : function(node){
26905         delete this.nodeHash[node.id];
26906     },
26907
26908     toString : function(){
26909         return "[Tree"+(this.id?" "+this.id:"")+"]";
26910     }
26911 });
26912
26913 /**
26914  * @class Roo.data.Node
26915  * @extends Roo.util.Observable
26916  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26917  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26918  * @constructor
26919  * @param {Object} attributes The attributes/config for the node
26920  */
26921 Roo.data.Node = function(attributes){
26922     /**
26923      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26924      * @type {Object}
26925      */
26926     this.attributes = attributes || {};
26927     this.leaf = this.attributes.leaf;
26928     /**
26929      * The node id. @type String
26930      */
26931     this.id = this.attributes.id;
26932     if(!this.id){
26933         this.id = Roo.id(null, "ynode-");
26934         this.attributes.id = this.id;
26935     }
26936      
26937     
26938     /**
26939      * All child nodes of this node. @type Array
26940      */
26941     this.childNodes = [];
26942     if(!this.childNodes.indexOf){ // indexOf is a must
26943         this.childNodes.indexOf = function(o){
26944             for(var i = 0, len = this.length; i < len; i++){
26945                 if(this[i] == o) {
26946                     return i;
26947                 }
26948             }
26949             return -1;
26950         };
26951     }
26952     /**
26953      * The parent node for this node. @type Node
26954      */
26955     this.parentNode = null;
26956     /**
26957      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26958      */
26959     this.firstChild = null;
26960     /**
26961      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26962      */
26963     this.lastChild = null;
26964     /**
26965      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26966      */
26967     this.previousSibling = null;
26968     /**
26969      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26970      */
26971     this.nextSibling = null;
26972
26973     this.addEvents({
26974        /**
26975         * @event append
26976         * Fires when a new child node is appended
26977         * @param {Tree} tree The owner tree
26978         * @param {Node} this This node
26979         * @param {Node} node The newly appended node
26980         * @param {Number} index The index of the newly appended node
26981         */
26982        "append" : true,
26983        /**
26984         * @event remove
26985         * Fires when a child node is removed
26986         * @param {Tree} tree The owner tree
26987         * @param {Node} this This node
26988         * @param {Node} node The removed node
26989         */
26990        "remove" : true,
26991        /**
26992         * @event move
26993         * Fires when this node is moved to a new location in the tree
26994         * @param {Tree} tree The owner tree
26995         * @param {Node} this This node
26996         * @param {Node} oldParent The old parent of this node
26997         * @param {Node} newParent The new parent of this node
26998         * @param {Number} index The index it was moved to
26999         */
27000        "move" : true,
27001        /**
27002         * @event insert
27003         * Fires when a new child node is inserted.
27004         * @param {Tree} tree The owner tree
27005         * @param {Node} this This node
27006         * @param {Node} node The child node inserted
27007         * @param {Node} refNode The child node the node was inserted before
27008         */
27009        "insert" : true,
27010        /**
27011         * @event beforeappend
27012         * Fires before a new child is appended, return false to cancel the append.
27013         * @param {Tree} tree The owner tree
27014         * @param {Node} this This node
27015         * @param {Node} node The child node to be appended
27016         */
27017        "beforeappend" : true,
27018        /**
27019         * @event beforeremove
27020         * Fires before a child is removed, return false to cancel the remove.
27021         * @param {Tree} tree The owner tree
27022         * @param {Node} this This node
27023         * @param {Node} node The child node to be removed
27024         */
27025        "beforeremove" : true,
27026        /**
27027         * @event beforemove
27028         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
27029         * @param {Tree} tree The owner tree
27030         * @param {Node} this This node
27031         * @param {Node} oldParent The parent of this node
27032         * @param {Node} newParent The new parent this node is moving to
27033         * @param {Number} index The index it is being moved to
27034         */
27035        "beforemove" : true,
27036        /**
27037         * @event beforeinsert
27038         * Fires before a new child is inserted, return false to cancel the insert.
27039         * @param {Tree} tree The owner tree
27040         * @param {Node} this This node
27041         * @param {Node} node The child node to be inserted
27042         * @param {Node} refNode The child node the node is being inserted before
27043         */
27044        "beforeinsert" : true
27045    });
27046     this.listeners = this.attributes.listeners;
27047     Roo.data.Node.superclass.constructor.call(this);
27048 };
27049
27050 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27051     fireEvent : function(evtName){
27052         // first do standard event for this node
27053         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27054             return false;
27055         }
27056         // then bubble it up to the tree if the event wasn't cancelled
27057         var ot = this.getOwnerTree();
27058         if(ot){
27059             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27060                 return false;
27061             }
27062         }
27063         return true;
27064     },
27065
27066     /**
27067      * Returns true if this node is a leaf
27068      * @return {Boolean}
27069      */
27070     isLeaf : function(){
27071         return this.leaf === true;
27072     },
27073
27074     // private
27075     setFirstChild : function(node){
27076         this.firstChild = node;
27077     },
27078
27079     //private
27080     setLastChild : function(node){
27081         this.lastChild = node;
27082     },
27083
27084
27085     /**
27086      * Returns true if this node is the last child of its parent
27087      * @return {Boolean}
27088      */
27089     isLast : function(){
27090        return (!this.parentNode ? true : this.parentNode.lastChild == this);
27091     },
27092
27093     /**
27094      * Returns true if this node is the first child of its parent
27095      * @return {Boolean}
27096      */
27097     isFirst : function(){
27098        return (!this.parentNode ? true : this.parentNode.firstChild == this);
27099     },
27100
27101     hasChildNodes : function(){
27102         return !this.isLeaf() && this.childNodes.length > 0;
27103     },
27104
27105     /**
27106      * Insert node(s) as the last child node of this node.
27107      * @param {Node/Array} node The node or Array of nodes to append
27108      * @return {Node} The appended node if single append, or null if an array was passed
27109      */
27110     appendChild : function(node){
27111         var multi = false;
27112         if(node instanceof Array){
27113             multi = node;
27114         }else if(arguments.length > 1){
27115             multi = arguments;
27116         }
27117         
27118         // if passed an array or multiple args do them one by one
27119         if(multi){
27120             for(var i = 0, len = multi.length; i < len; i++) {
27121                 this.appendChild(multi[i]);
27122             }
27123         }else{
27124             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27125                 return false;
27126             }
27127             var index = this.childNodes.length;
27128             var oldParent = node.parentNode;
27129             // it's a move, make sure we move it cleanly
27130             if(oldParent){
27131                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27132                     return false;
27133                 }
27134                 oldParent.removeChild(node);
27135             }
27136             
27137             index = this.childNodes.length;
27138             if(index == 0){
27139                 this.setFirstChild(node);
27140             }
27141             this.childNodes.push(node);
27142             node.parentNode = this;
27143             var ps = this.childNodes[index-1];
27144             if(ps){
27145                 node.previousSibling = ps;
27146                 ps.nextSibling = node;
27147             }else{
27148                 node.previousSibling = null;
27149             }
27150             node.nextSibling = null;
27151             this.setLastChild(node);
27152             node.setOwnerTree(this.getOwnerTree());
27153             this.fireEvent("append", this.ownerTree, this, node, index);
27154             if(this.ownerTree) {
27155                 this.ownerTree.fireEvent("appendnode", this, node, index);
27156             }
27157             if(oldParent){
27158                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27159             }
27160             return node;
27161         }
27162     },
27163
27164     /**
27165      * Removes a child node from this node.
27166      * @param {Node} node The node to remove
27167      * @return {Node} The removed node
27168      */
27169     removeChild : function(node){
27170         var index = this.childNodes.indexOf(node);
27171         if(index == -1){
27172             return false;
27173         }
27174         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27175             return false;
27176         }
27177
27178         // remove it from childNodes collection
27179         this.childNodes.splice(index, 1);
27180
27181         // update siblings
27182         if(node.previousSibling){
27183             node.previousSibling.nextSibling = node.nextSibling;
27184         }
27185         if(node.nextSibling){
27186             node.nextSibling.previousSibling = node.previousSibling;
27187         }
27188
27189         // update child refs
27190         if(this.firstChild == node){
27191             this.setFirstChild(node.nextSibling);
27192         }
27193         if(this.lastChild == node){
27194             this.setLastChild(node.previousSibling);
27195         }
27196
27197         node.setOwnerTree(null);
27198         // clear any references from the node
27199         node.parentNode = null;
27200         node.previousSibling = null;
27201         node.nextSibling = null;
27202         this.fireEvent("remove", this.ownerTree, this, node);
27203         return node;
27204     },
27205
27206     /**
27207      * Inserts the first node before the second node in this nodes childNodes collection.
27208      * @param {Node} node The node to insert
27209      * @param {Node} refNode The node to insert before (if null the node is appended)
27210      * @return {Node} The inserted node
27211      */
27212     insertBefore : function(node, refNode){
27213         if(!refNode){ // like standard Dom, refNode can be null for append
27214             return this.appendChild(node);
27215         }
27216         // nothing to do
27217         if(node == refNode){
27218             return false;
27219         }
27220
27221         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27222             return false;
27223         }
27224         var index = this.childNodes.indexOf(refNode);
27225         var oldParent = node.parentNode;
27226         var refIndex = index;
27227
27228         // when moving internally, indexes will change after remove
27229         if(oldParent == this && this.childNodes.indexOf(node) < index){
27230             refIndex--;
27231         }
27232
27233         // it's a move, make sure we move it cleanly
27234         if(oldParent){
27235             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27236                 return false;
27237             }
27238             oldParent.removeChild(node);
27239         }
27240         if(refIndex == 0){
27241             this.setFirstChild(node);
27242         }
27243         this.childNodes.splice(refIndex, 0, node);
27244         node.parentNode = this;
27245         var ps = this.childNodes[refIndex-1];
27246         if(ps){
27247             node.previousSibling = ps;
27248             ps.nextSibling = node;
27249         }else{
27250             node.previousSibling = null;
27251         }
27252         node.nextSibling = refNode;
27253         refNode.previousSibling = node;
27254         node.setOwnerTree(this.getOwnerTree());
27255         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27256         if(oldParent){
27257             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27258         }
27259         return node;
27260     },
27261
27262     /**
27263      * Returns the child node at the specified index.
27264      * @param {Number} index
27265      * @return {Node}
27266      */
27267     item : function(index){
27268         return this.childNodes[index];
27269     },
27270
27271     /**
27272      * Replaces one child node in this node with another.
27273      * @param {Node} newChild The replacement node
27274      * @param {Node} oldChild The node to replace
27275      * @return {Node} The replaced node
27276      */
27277     replaceChild : function(newChild, oldChild){
27278         this.insertBefore(newChild, oldChild);
27279         this.removeChild(oldChild);
27280         return oldChild;
27281     },
27282
27283     /**
27284      * Returns the index of a child node
27285      * @param {Node} node
27286      * @return {Number} The index of the node or -1 if it was not found
27287      */
27288     indexOf : function(child){
27289         return this.childNodes.indexOf(child);
27290     },
27291
27292     /**
27293      * Returns the tree this node is in.
27294      * @return {Tree}
27295      */
27296     getOwnerTree : function(){
27297         // if it doesn't have one, look for one
27298         if(!this.ownerTree){
27299             var p = this;
27300             while(p){
27301                 if(p.ownerTree){
27302                     this.ownerTree = p.ownerTree;
27303                     break;
27304                 }
27305                 p = p.parentNode;
27306             }
27307         }
27308         return this.ownerTree;
27309     },
27310
27311     /**
27312      * Returns depth of this node (the root node has a depth of 0)
27313      * @return {Number}
27314      */
27315     getDepth : function(){
27316         var depth = 0;
27317         var p = this;
27318         while(p.parentNode){
27319             ++depth;
27320             p = p.parentNode;
27321         }
27322         return depth;
27323     },
27324
27325     // private
27326     setOwnerTree : function(tree){
27327         // if it's move, we need to update everyone
27328         if(tree != this.ownerTree){
27329             if(this.ownerTree){
27330                 this.ownerTree.unregisterNode(this);
27331             }
27332             this.ownerTree = tree;
27333             var cs = this.childNodes;
27334             for(var i = 0, len = cs.length; i < len; i++) {
27335                 cs[i].setOwnerTree(tree);
27336             }
27337             if(tree){
27338                 tree.registerNode(this);
27339             }
27340         }
27341     },
27342
27343     /**
27344      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27345      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27346      * @return {String} The path
27347      */
27348     getPath : function(attr){
27349         attr = attr || "id";
27350         var p = this.parentNode;
27351         var b = [this.attributes[attr]];
27352         while(p){
27353             b.unshift(p.attributes[attr]);
27354             p = p.parentNode;
27355         }
27356         var sep = this.getOwnerTree().pathSeparator;
27357         return sep + b.join(sep);
27358     },
27359
27360     /**
27361      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27362      * function call will be the scope provided or the current node. The arguments to the function
27363      * will be the args provided or the current node. If the function returns false at any point,
27364      * the bubble is stopped.
27365      * @param {Function} fn The function to call
27366      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27367      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27368      */
27369     bubble : function(fn, scope, args){
27370         var p = this;
27371         while(p){
27372             if(fn.call(scope || p, args || p) === false){
27373                 break;
27374             }
27375             p = p.parentNode;
27376         }
27377     },
27378
27379     /**
27380      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27381      * function call will be the scope provided or the current node. The arguments to the function
27382      * will be the args provided or the current node. If the function returns false at any point,
27383      * the cascade is stopped on that branch.
27384      * @param {Function} fn The function to call
27385      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27386      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27387      */
27388     cascade : function(fn, scope, args){
27389         if(fn.call(scope || this, args || this) !== false){
27390             var cs = this.childNodes;
27391             for(var i = 0, len = cs.length; i < len; i++) {
27392                 cs[i].cascade(fn, scope, args);
27393             }
27394         }
27395     },
27396
27397     /**
27398      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27399      * function call will be the scope provided or the current node. The arguments to the function
27400      * will be the args provided or the current node. If the function returns false at any point,
27401      * the iteration stops.
27402      * @param {Function} fn The function to call
27403      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27404      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27405      */
27406     eachChild : function(fn, scope, args){
27407         var cs = this.childNodes;
27408         for(var i = 0, len = cs.length; i < len; i++) {
27409                 if(fn.call(scope || this, args || cs[i]) === false){
27410                     break;
27411                 }
27412         }
27413     },
27414
27415     /**
27416      * Finds the first child that has the attribute with the specified value.
27417      * @param {String} attribute The attribute name
27418      * @param {Mixed} value The value to search for
27419      * @return {Node} The found child or null if none was found
27420      */
27421     findChild : function(attribute, value){
27422         var cs = this.childNodes;
27423         for(var i = 0, len = cs.length; i < len; i++) {
27424                 if(cs[i].attributes[attribute] == value){
27425                     return cs[i];
27426                 }
27427         }
27428         return null;
27429     },
27430
27431     /**
27432      * Finds the first child by a custom function. The child matches if the function passed
27433      * returns true.
27434      * @param {Function} fn
27435      * @param {Object} scope (optional)
27436      * @return {Node} The found child or null if none was found
27437      */
27438     findChildBy : function(fn, scope){
27439         var cs = this.childNodes;
27440         for(var i = 0, len = cs.length; i < len; i++) {
27441                 if(fn.call(scope||cs[i], cs[i]) === true){
27442                     return cs[i];
27443                 }
27444         }
27445         return null;
27446     },
27447
27448     /**
27449      * Sorts this nodes children using the supplied sort function
27450      * @param {Function} fn
27451      * @param {Object} scope (optional)
27452      */
27453     sort : function(fn, scope){
27454         var cs = this.childNodes;
27455         var len = cs.length;
27456         if(len > 0){
27457             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27458             cs.sort(sortFn);
27459             for(var i = 0; i < len; i++){
27460                 var n = cs[i];
27461                 n.previousSibling = cs[i-1];
27462                 n.nextSibling = cs[i+1];
27463                 if(i == 0){
27464                     this.setFirstChild(n);
27465                 }
27466                 if(i == len-1){
27467                     this.setLastChild(n);
27468                 }
27469             }
27470         }
27471     },
27472
27473     /**
27474      * Returns true if this node is an ancestor (at any point) of the passed node.
27475      * @param {Node} node
27476      * @return {Boolean}
27477      */
27478     contains : function(node){
27479         return node.isAncestor(this);
27480     },
27481
27482     /**
27483      * Returns true if the passed node is an ancestor (at any point) of this node.
27484      * @param {Node} node
27485      * @return {Boolean}
27486      */
27487     isAncestor : function(node){
27488         var p = this.parentNode;
27489         while(p){
27490             if(p == node){
27491                 return true;
27492             }
27493             p = p.parentNode;
27494         }
27495         return false;
27496     },
27497
27498     toString : function(){
27499         return "[Node"+(this.id?" "+this.id:"")+"]";
27500     }
27501 });/*
27502  * Based on:
27503  * Ext JS Library 1.1.1
27504  * Copyright(c) 2006-2007, Ext JS, LLC.
27505  *
27506  * Originally Released Under LGPL - original licence link has changed is not relivant.
27507  *
27508  * Fork - LGPL
27509  * <script type="text/javascript">
27510  */
27511
27512
27513 /**
27514  * @class Roo.Shadow
27515  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27516  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27517  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27518  * @constructor
27519  * Create a new Shadow
27520  * @param {Object} config The config object
27521  */
27522 Roo.Shadow = function(config){
27523     Roo.apply(this, config);
27524     if(typeof this.mode != "string"){
27525         this.mode = this.defaultMode;
27526     }
27527     var o = this.offset, a = {h: 0};
27528     var rad = Math.floor(this.offset/2);
27529     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27530         case "drop":
27531             a.w = 0;
27532             a.l = a.t = o;
27533             a.t -= 1;
27534             if(Roo.isIE){
27535                 a.l -= this.offset + rad;
27536                 a.t -= this.offset + rad;
27537                 a.w -= rad;
27538                 a.h -= rad;
27539                 a.t += 1;
27540             }
27541         break;
27542         case "sides":
27543             a.w = (o*2);
27544             a.l = -o;
27545             a.t = o-1;
27546             if(Roo.isIE){
27547                 a.l -= (this.offset - rad);
27548                 a.t -= this.offset + rad;
27549                 a.l += 1;
27550                 a.w -= (this.offset - rad)*2;
27551                 a.w -= rad + 1;
27552                 a.h -= 1;
27553             }
27554         break;
27555         case "frame":
27556             a.w = a.h = (o*2);
27557             a.l = a.t = -o;
27558             a.t += 1;
27559             a.h -= 2;
27560             if(Roo.isIE){
27561                 a.l -= (this.offset - rad);
27562                 a.t -= (this.offset - rad);
27563                 a.l += 1;
27564                 a.w -= (this.offset + rad + 1);
27565                 a.h -= (this.offset + rad);
27566                 a.h += 1;
27567             }
27568         break;
27569     };
27570
27571     this.adjusts = a;
27572 };
27573
27574 Roo.Shadow.prototype = {
27575     /**
27576      * @cfg {String} mode
27577      * The shadow display mode.  Supports the following options:<br />
27578      * sides: Shadow displays on both sides and bottom only<br />
27579      * frame: Shadow displays equally on all four sides<br />
27580      * drop: Traditional bottom-right drop shadow (default)
27581      */
27582     mode: false,
27583     /**
27584      * @cfg {String} offset
27585      * The number of pixels to offset the shadow from the element (defaults to 4)
27586      */
27587     offset: 4,
27588
27589     // private
27590     defaultMode: "drop",
27591
27592     /**
27593      * Displays the shadow under the target element
27594      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27595      */
27596     show : function(target){
27597         target = Roo.get(target);
27598         if(!this.el){
27599             this.el = Roo.Shadow.Pool.pull();
27600             if(this.el.dom.nextSibling != target.dom){
27601                 this.el.insertBefore(target);
27602             }
27603         }
27604         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27605         if(Roo.isIE){
27606             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27607         }
27608         this.realign(
27609             target.getLeft(true),
27610             target.getTop(true),
27611             target.getWidth(),
27612             target.getHeight()
27613         );
27614         this.el.dom.style.display = "block";
27615     },
27616
27617     /**
27618      * Returns true if the shadow is visible, else false
27619      */
27620     isVisible : function(){
27621         return this.el ? true : false;  
27622     },
27623
27624     /**
27625      * Direct alignment when values are already available. Show must be called at least once before
27626      * calling this method to ensure it is initialized.
27627      * @param {Number} left The target element left position
27628      * @param {Number} top The target element top position
27629      * @param {Number} width The target element width
27630      * @param {Number} height The target element height
27631      */
27632     realign : function(l, t, w, h){
27633         if(!this.el){
27634             return;
27635         }
27636         var a = this.adjusts, d = this.el.dom, s = d.style;
27637         var iea = 0;
27638         s.left = (l+a.l)+"px";
27639         s.top = (t+a.t)+"px";
27640         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27641  
27642         if(s.width != sws || s.height != shs){
27643             s.width = sws;
27644             s.height = shs;
27645             if(!Roo.isIE){
27646                 var cn = d.childNodes;
27647                 var sww = Math.max(0, (sw-12))+"px";
27648                 cn[0].childNodes[1].style.width = sww;
27649                 cn[1].childNodes[1].style.width = sww;
27650                 cn[2].childNodes[1].style.width = sww;
27651                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27652             }
27653         }
27654     },
27655
27656     /**
27657      * Hides this shadow
27658      */
27659     hide : function(){
27660         if(this.el){
27661             this.el.dom.style.display = "none";
27662             Roo.Shadow.Pool.push(this.el);
27663             delete this.el;
27664         }
27665     },
27666
27667     /**
27668      * Adjust the z-index of this shadow
27669      * @param {Number} zindex The new z-index
27670      */
27671     setZIndex : function(z){
27672         this.zIndex = z;
27673         if(this.el){
27674             this.el.setStyle("z-index", z);
27675         }
27676     }
27677 };
27678
27679 // Private utility class that manages the internal Shadow cache
27680 Roo.Shadow.Pool = function(){
27681     var p = [];
27682     var markup = Roo.isIE ?
27683                  '<div class="x-ie-shadow"></div>' :
27684                  '<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>';
27685     return {
27686         pull : function(){
27687             var sh = p.shift();
27688             if(!sh){
27689                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27690                 sh.autoBoxAdjust = false;
27691             }
27692             return sh;
27693         },
27694
27695         push : function(sh){
27696             p.push(sh);
27697         }
27698     };
27699 }();/*
27700  * Based on:
27701  * Ext JS Library 1.1.1
27702  * Copyright(c) 2006-2007, Ext JS, LLC.
27703  *
27704  * Originally Released Under LGPL - original licence link has changed is not relivant.
27705  *
27706  * Fork - LGPL
27707  * <script type="text/javascript">
27708  */
27709
27710
27711 /**
27712  * @class Roo.SplitBar
27713  * @extends Roo.util.Observable
27714  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27715  * <br><br>
27716  * Usage:
27717  * <pre><code>
27718 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27719                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27720 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27721 split.minSize = 100;
27722 split.maxSize = 600;
27723 split.animate = true;
27724 split.on('moved', splitterMoved);
27725 </code></pre>
27726  * @constructor
27727  * Create a new SplitBar
27728  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27729  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27730  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27731  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27732                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27733                         position of the SplitBar).
27734  */
27735 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27736     
27737     /** @private */
27738     this.el = Roo.get(dragElement, true);
27739     this.el.dom.unselectable = "on";
27740     /** @private */
27741     this.resizingEl = Roo.get(resizingElement, true);
27742
27743     /**
27744      * @private
27745      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27746      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27747      * @type Number
27748      */
27749     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27750     
27751     /**
27752      * The minimum size of the resizing element. (Defaults to 0)
27753      * @type Number
27754      */
27755     this.minSize = 0;
27756     
27757     /**
27758      * The maximum size of the resizing element. (Defaults to 2000)
27759      * @type Number
27760      */
27761     this.maxSize = 2000;
27762     
27763     /**
27764      * Whether to animate the transition to the new size
27765      * @type Boolean
27766      */
27767     this.animate = false;
27768     
27769     /**
27770      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27771      * @type Boolean
27772      */
27773     this.useShim = false;
27774     
27775     /** @private */
27776     this.shim = null;
27777     
27778     if(!existingProxy){
27779         /** @private */
27780         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27781     }else{
27782         this.proxy = Roo.get(existingProxy).dom;
27783     }
27784     /** @private */
27785     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27786     
27787     /** @private */
27788     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27789     
27790     /** @private */
27791     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27792     
27793     /** @private */
27794     this.dragSpecs = {};
27795     
27796     /**
27797      * @private The adapter to use to positon and resize elements
27798      */
27799     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27800     this.adapter.init(this);
27801     
27802     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27803         /** @private */
27804         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27805         this.el.addClass("x-splitbar-h");
27806     }else{
27807         /** @private */
27808         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27809         this.el.addClass("x-splitbar-v");
27810     }
27811     
27812     this.addEvents({
27813         /**
27814          * @event resize
27815          * Fires when the splitter is moved (alias for {@link #event-moved})
27816          * @param {Roo.SplitBar} this
27817          * @param {Number} newSize the new width or height
27818          */
27819         "resize" : true,
27820         /**
27821          * @event moved
27822          * Fires when the splitter is moved
27823          * @param {Roo.SplitBar} this
27824          * @param {Number} newSize the new width or height
27825          */
27826         "moved" : true,
27827         /**
27828          * @event beforeresize
27829          * Fires before the splitter is dragged
27830          * @param {Roo.SplitBar} this
27831          */
27832         "beforeresize" : true,
27833
27834         "beforeapply" : true
27835     });
27836
27837     Roo.util.Observable.call(this);
27838 };
27839
27840 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27841     onStartProxyDrag : function(x, y){
27842         this.fireEvent("beforeresize", this);
27843         if(!this.overlay){
27844             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27845             o.unselectable();
27846             o.enableDisplayMode("block");
27847             // all splitbars share the same overlay
27848             Roo.SplitBar.prototype.overlay = o;
27849         }
27850         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27851         this.overlay.show();
27852         Roo.get(this.proxy).setDisplayed("block");
27853         var size = this.adapter.getElementSize(this);
27854         this.activeMinSize = this.getMinimumSize();;
27855         this.activeMaxSize = this.getMaximumSize();;
27856         var c1 = size - this.activeMinSize;
27857         var c2 = Math.max(this.activeMaxSize - size, 0);
27858         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27859             this.dd.resetConstraints();
27860             this.dd.setXConstraint(
27861                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27862                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27863             );
27864             this.dd.setYConstraint(0, 0);
27865         }else{
27866             this.dd.resetConstraints();
27867             this.dd.setXConstraint(0, 0);
27868             this.dd.setYConstraint(
27869                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27870                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27871             );
27872          }
27873         this.dragSpecs.startSize = size;
27874         this.dragSpecs.startPoint = [x, y];
27875         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27876     },
27877     
27878     /** 
27879      * @private Called after the drag operation by the DDProxy
27880      */
27881     onEndProxyDrag : function(e){
27882         Roo.get(this.proxy).setDisplayed(false);
27883         var endPoint = Roo.lib.Event.getXY(e);
27884         if(this.overlay){
27885             this.overlay.hide();
27886         }
27887         var newSize;
27888         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27889             newSize = this.dragSpecs.startSize + 
27890                 (this.placement == Roo.SplitBar.LEFT ?
27891                     endPoint[0] - this.dragSpecs.startPoint[0] :
27892                     this.dragSpecs.startPoint[0] - endPoint[0]
27893                 );
27894         }else{
27895             newSize = this.dragSpecs.startSize + 
27896                 (this.placement == Roo.SplitBar.TOP ?
27897                     endPoint[1] - this.dragSpecs.startPoint[1] :
27898                     this.dragSpecs.startPoint[1] - endPoint[1]
27899                 );
27900         }
27901         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27902         if(newSize != this.dragSpecs.startSize){
27903             if(this.fireEvent('beforeapply', this, newSize) !== false){
27904                 this.adapter.setElementSize(this, newSize);
27905                 this.fireEvent("moved", this, newSize);
27906                 this.fireEvent("resize", this, newSize);
27907             }
27908         }
27909     },
27910     
27911     /**
27912      * Get the adapter this SplitBar uses
27913      * @return The adapter object
27914      */
27915     getAdapter : function(){
27916         return this.adapter;
27917     },
27918     
27919     /**
27920      * Set the adapter this SplitBar uses
27921      * @param {Object} adapter A SplitBar adapter object
27922      */
27923     setAdapter : function(adapter){
27924         this.adapter = adapter;
27925         this.adapter.init(this);
27926     },
27927     
27928     /**
27929      * Gets the minimum size for the resizing element
27930      * @return {Number} The minimum size
27931      */
27932     getMinimumSize : function(){
27933         return this.minSize;
27934     },
27935     
27936     /**
27937      * Sets the minimum size for the resizing element
27938      * @param {Number} minSize The minimum size
27939      */
27940     setMinimumSize : function(minSize){
27941         this.minSize = minSize;
27942     },
27943     
27944     /**
27945      * Gets the maximum size for the resizing element
27946      * @return {Number} The maximum size
27947      */
27948     getMaximumSize : function(){
27949         return this.maxSize;
27950     },
27951     
27952     /**
27953      * Sets the maximum size for the resizing element
27954      * @param {Number} maxSize The maximum size
27955      */
27956     setMaximumSize : function(maxSize){
27957         this.maxSize = maxSize;
27958     },
27959     
27960     /**
27961      * Sets the initialize size for the resizing element
27962      * @param {Number} size The initial size
27963      */
27964     setCurrentSize : function(size){
27965         var oldAnimate = this.animate;
27966         this.animate = false;
27967         this.adapter.setElementSize(this, size);
27968         this.animate = oldAnimate;
27969     },
27970     
27971     /**
27972      * Destroy this splitbar. 
27973      * @param {Boolean} removeEl True to remove the element
27974      */
27975     destroy : function(removeEl){
27976         if(this.shim){
27977             this.shim.remove();
27978         }
27979         this.dd.unreg();
27980         this.proxy.parentNode.removeChild(this.proxy);
27981         if(removeEl){
27982             this.el.remove();
27983         }
27984     }
27985 });
27986
27987 /**
27988  * @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.
27989  */
27990 Roo.SplitBar.createProxy = function(dir){
27991     var proxy = new Roo.Element(document.createElement("div"));
27992     proxy.unselectable();
27993     var cls = 'x-splitbar-proxy';
27994     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27995     document.body.appendChild(proxy.dom);
27996     return proxy.dom;
27997 };
27998
27999 /** 
28000  * @class Roo.SplitBar.BasicLayoutAdapter
28001  * Default Adapter. It assumes the splitter and resizing element are not positioned
28002  * elements and only gets/sets the width of the element. Generally used for table based layouts.
28003  */
28004 Roo.SplitBar.BasicLayoutAdapter = function(){
28005 };
28006
28007 Roo.SplitBar.BasicLayoutAdapter.prototype = {
28008     // do nothing for now
28009     init : function(s){
28010     
28011     },
28012     /**
28013      * Called before drag operations to get the current size of the resizing element. 
28014      * @param {Roo.SplitBar} s The SplitBar using this adapter
28015      */
28016      getElementSize : function(s){
28017         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28018             return s.resizingEl.getWidth();
28019         }else{
28020             return s.resizingEl.getHeight();
28021         }
28022     },
28023     
28024     /**
28025      * Called after drag operations to set the size of the resizing element.
28026      * @param {Roo.SplitBar} s The SplitBar using this adapter
28027      * @param {Number} newSize The new size to set
28028      * @param {Function} onComplete A function to be invoked when resizing is complete
28029      */
28030     setElementSize : function(s, newSize, onComplete){
28031         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28032             if(!s.animate){
28033                 s.resizingEl.setWidth(newSize);
28034                 if(onComplete){
28035                     onComplete(s, newSize);
28036                 }
28037             }else{
28038                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28039             }
28040         }else{
28041             
28042             if(!s.animate){
28043                 s.resizingEl.setHeight(newSize);
28044                 if(onComplete){
28045                     onComplete(s, newSize);
28046                 }
28047             }else{
28048                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28049             }
28050         }
28051     }
28052 };
28053
28054 /** 
28055  *@class Roo.SplitBar.AbsoluteLayoutAdapter
28056  * @extends Roo.SplitBar.BasicLayoutAdapter
28057  * Adapter that  moves the splitter element to align with the resized sizing element. 
28058  * Used with an absolute positioned SplitBar.
28059  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28060  * document.body, make sure you assign an id to the body element.
28061  */
28062 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28063     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28064     this.container = Roo.get(container);
28065 };
28066
28067 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28068     init : function(s){
28069         this.basic.init(s);
28070     },
28071     
28072     getElementSize : function(s){
28073         return this.basic.getElementSize(s);
28074     },
28075     
28076     setElementSize : function(s, newSize, onComplete){
28077         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28078     },
28079     
28080     moveSplitter : function(s){
28081         var yes = Roo.SplitBar;
28082         switch(s.placement){
28083             case yes.LEFT:
28084                 s.el.setX(s.resizingEl.getRight());
28085                 break;
28086             case yes.RIGHT:
28087                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28088                 break;
28089             case yes.TOP:
28090                 s.el.setY(s.resizingEl.getBottom());
28091                 break;
28092             case yes.BOTTOM:
28093                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28094                 break;
28095         }
28096     }
28097 };
28098
28099 /**
28100  * Orientation constant - Create a vertical SplitBar
28101  * @static
28102  * @type Number
28103  */
28104 Roo.SplitBar.VERTICAL = 1;
28105
28106 /**
28107  * Orientation constant - Create a horizontal SplitBar
28108  * @static
28109  * @type Number
28110  */
28111 Roo.SplitBar.HORIZONTAL = 2;
28112
28113 /**
28114  * Placement constant - The resizing element is to the left of the splitter element
28115  * @static
28116  * @type Number
28117  */
28118 Roo.SplitBar.LEFT = 1;
28119
28120 /**
28121  * Placement constant - The resizing element is to the right of the splitter element
28122  * @static
28123  * @type Number
28124  */
28125 Roo.SplitBar.RIGHT = 2;
28126
28127 /**
28128  * Placement constant - The resizing element is positioned above the splitter element
28129  * @static
28130  * @type Number
28131  */
28132 Roo.SplitBar.TOP = 3;
28133
28134 /**
28135  * Placement constant - The resizing element is positioned under splitter element
28136  * @static
28137  * @type Number
28138  */
28139 Roo.SplitBar.BOTTOM = 4;
28140 /*
28141  * Based on:
28142  * Ext JS Library 1.1.1
28143  * Copyright(c) 2006-2007, Ext JS, LLC.
28144  *
28145  * Originally Released Under LGPL - original licence link has changed is not relivant.
28146  *
28147  * Fork - LGPL
28148  * <script type="text/javascript">
28149  */
28150
28151 /**
28152  * @class Roo.View
28153  * @extends Roo.util.Observable
28154  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
28155  * This class also supports single and multi selection modes. <br>
28156  * Create a data model bound view:
28157  <pre><code>
28158  var store = new Roo.data.Store(...);
28159
28160  var view = new Roo.View({
28161     el : "my-element",
28162     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
28163  
28164     singleSelect: true,
28165     selectedClass: "ydataview-selected",
28166     store: store
28167  });
28168
28169  // listen for node click?
28170  view.on("click", function(vw, index, node, e){
28171  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28172  });
28173
28174  // load XML data
28175  dataModel.load("foobar.xml");
28176  </code></pre>
28177  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28178  * <br><br>
28179  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28180  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28181  * 
28182  * Note: old style constructor is still suported (container, template, config)
28183  * 
28184  * @constructor
28185  * Create a new View
28186  * @param {Object} config The config object
28187  * 
28188  */
28189 Roo.View = function(config, depreciated_tpl, depreciated_config){
28190     
28191     this.parent = false;
28192     
28193     if (typeof(depreciated_tpl) == 'undefined') {
28194         // new way.. - universal constructor.
28195         Roo.apply(this, config);
28196         this.el  = Roo.get(this.el);
28197     } else {
28198         // old format..
28199         this.el  = Roo.get(config);
28200         this.tpl = depreciated_tpl;
28201         Roo.apply(this, depreciated_config);
28202     }
28203     this.wrapEl  = this.el.wrap().wrap();
28204     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28205     
28206     
28207     if(typeof(this.tpl) == "string"){
28208         this.tpl = new Roo.Template(this.tpl);
28209     } else {
28210         // support xtype ctors..
28211         this.tpl = new Roo.factory(this.tpl, Roo);
28212     }
28213     
28214     
28215     this.tpl.compile();
28216     
28217     /** @private */
28218     this.addEvents({
28219         /**
28220          * @event beforeclick
28221          * Fires before a click is processed. Returns false to cancel the default action.
28222          * @param {Roo.View} this
28223          * @param {Number} index The index of the target node
28224          * @param {HTMLElement} node The target node
28225          * @param {Roo.EventObject} e The raw event object
28226          */
28227             "beforeclick" : true,
28228         /**
28229          * @event click
28230          * Fires when a template node is clicked.
28231          * @param {Roo.View} this
28232          * @param {Number} index The index of the target node
28233          * @param {HTMLElement} node The target node
28234          * @param {Roo.EventObject} e The raw event object
28235          */
28236             "click" : true,
28237         /**
28238          * @event dblclick
28239          * Fires when a template node is double clicked.
28240          * @param {Roo.View} this
28241          * @param {Number} index The index of the target node
28242          * @param {HTMLElement} node The target node
28243          * @param {Roo.EventObject} e The raw event object
28244          */
28245             "dblclick" : true,
28246         /**
28247          * @event contextmenu
28248          * Fires when a template node is right clicked.
28249          * @param {Roo.View} this
28250          * @param {Number} index The index of the target node
28251          * @param {HTMLElement} node The target node
28252          * @param {Roo.EventObject} e The raw event object
28253          */
28254             "contextmenu" : true,
28255         /**
28256          * @event selectionchange
28257          * Fires when the selected nodes change.
28258          * @param {Roo.View} this
28259          * @param {Array} selections Array of the selected nodes
28260          */
28261             "selectionchange" : true,
28262     
28263         /**
28264          * @event beforeselect
28265          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28266          * @param {Roo.View} this
28267          * @param {HTMLElement} node The node to be selected
28268          * @param {Array} selections Array of currently selected nodes
28269          */
28270             "beforeselect" : true,
28271         /**
28272          * @event preparedata
28273          * Fires on every row to render, to allow you to change the data.
28274          * @param {Roo.View} this
28275          * @param {Object} data to be rendered (change this)
28276          */
28277           "preparedata" : true
28278           
28279           
28280         });
28281
28282
28283
28284     this.el.on({
28285         "click": this.onClick,
28286         "dblclick": this.onDblClick,
28287         "contextmenu": this.onContextMenu,
28288         scope:this
28289     });
28290
28291     this.selections = [];
28292     this.nodes = [];
28293     this.cmp = new Roo.CompositeElementLite([]);
28294     if(this.store){
28295         this.store = Roo.factory(this.store, Roo.data);
28296         this.setStore(this.store, true);
28297     }
28298     
28299     if ( this.footer && this.footer.xtype) {
28300            
28301          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28302         
28303         this.footer.dataSource = this.store;
28304         this.footer.container = fctr;
28305         this.footer = Roo.factory(this.footer, Roo);
28306         fctr.insertFirst(this.el);
28307         
28308         // this is a bit insane - as the paging toolbar seems to detach the el..
28309 //        dom.parentNode.parentNode.parentNode
28310          // they get detached?
28311     }
28312     
28313     
28314     Roo.View.superclass.constructor.call(this);
28315     
28316     
28317 };
28318
28319 Roo.extend(Roo.View, Roo.util.Observable, {
28320     
28321      /**
28322      * @cfg {Roo.data.Store} store Data store to load data from.
28323      */
28324     store : false,
28325     
28326     /**
28327      * @cfg {String|Roo.Element} el The container element.
28328      */
28329     el : '',
28330     
28331     /**
28332      * @cfg {String|Roo.Template} tpl The template used by this View 
28333      */
28334     tpl : false,
28335     /**
28336      * @cfg {String} dataName the named area of the template to use as the data area
28337      *                          Works with domtemplates roo-name="name"
28338      */
28339     dataName: false,
28340     /**
28341      * @cfg {String} selectedClass The css class to add to selected nodes
28342      */
28343     selectedClass : "x-view-selected",
28344      /**
28345      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28346      */
28347     emptyText : "",
28348     
28349     /**
28350      * @cfg {String} text to display on mask (default Loading)
28351      */
28352     mask : false,
28353     /**
28354      * @cfg {Boolean} multiSelect Allow multiple selection
28355      */
28356     multiSelect : false,
28357     /**
28358      * @cfg {Boolean} singleSelect Allow single selection
28359      */
28360     singleSelect:  false,
28361     
28362     /**
28363      * @cfg {Boolean} toggleSelect - selecting 
28364      */
28365     toggleSelect : false,
28366     
28367     /**
28368      * @cfg {Boolean} tickable - selecting 
28369      */
28370     tickable : false,
28371     
28372     /**
28373      * Returns the element this view is bound to.
28374      * @return {Roo.Element}
28375      */
28376     getEl : function(){
28377         return this.wrapEl;
28378     },
28379     
28380     
28381
28382     /**
28383      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28384      */
28385     refresh : function(){
28386         //Roo.log('refresh');
28387         var t = this.tpl;
28388         
28389         // if we are using something like 'domtemplate', then
28390         // the what gets used is:
28391         // t.applySubtemplate(NAME, data, wrapping data..)
28392         // the outer template then get' applied with
28393         //     the store 'extra data'
28394         // and the body get's added to the
28395         //      roo-name="data" node?
28396         //      <span class='roo-tpl-{name}'></span> ?????
28397         
28398         
28399         
28400         this.clearSelections();
28401         this.el.update("");
28402         var html = [];
28403         var records = this.store.getRange();
28404         if(records.length < 1) {
28405             
28406             // is this valid??  = should it render a template??
28407             
28408             this.el.update(this.emptyText);
28409             return;
28410         }
28411         var el = this.el;
28412         if (this.dataName) {
28413             this.el.update(t.apply(this.store.meta)); //????
28414             el = this.el.child('.roo-tpl-' + this.dataName);
28415         }
28416         
28417         for(var i = 0, len = records.length; i < len; i++){
28418             var data = this.prepareData(records[i].data, i, records[i]);
28419             this.fireEvent("preparedata", this, data, i, records[i]);
28420             
28421             var d = Roo.apply({}, data);
28422             
28423             if(this.tickable){
28424                 Roo.apply(d, {'roo-id' : Roo.id()});
28425                 
28426                 var _this = this;
28427             
28428                 Roo.each(this.parent.item, function(item){
28429                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28430                         return;
28431                     }
28432                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28433                 });
28434             }
28435             
28436             html[html.length] = Roo.util.Format.trim(
28437                 this.dataName ?
28438                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28439                     t.apply(d)
28440             );
28441         }
28442         
28443         
28444         
28445         el.update(html.join(""));
28446         this.nodes = el.dom.childNodes;
28447         this.updateIndexes(0);
28448     },
28449     
28450
28451     /**
28452      * Function to override to reformat the data that is sent to
28453      * the template for each node.
28454      * DEPRICATED - use the preparedata event handler.
28455      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28456      * a JSON object for an UpdateManager bound view).
28457      */
28458     prepareData : function(data, index, record)
28459     {
28460         this.fireEvent("preparedata", this, data, index, record);
28461         return data;
28462     },
28463
28464     onUpdate : function(ds, record){
28465         // Roo.log('on update');   
28466         this.clearSelections();
28467         var index = this.store.indexOf(record);
28468         var n = this.nodes[index];
28469         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28470         n.parentNode.removeChild(n);
28471         this.updateIndexes(index, index);
28472     },
28473
28474     
28475     
28476 // --------- FIXME     
28477     onAdd : function(ds, records, index)
28478     {
28479         //Roo.log(['on Add', ds, records, index] );        
28480         this.clearSelections();
28481         if(this.nodes.length == 0){
28482             this.refresh();
28483             return;
28484         }
28485         var n = this.nodes[index];
28486         for(var i = 0, len = records.length; i < len; i++){
28487             var d = this.prepareData(records[i].data, i, records[i]);
28488             if(n){
28489                 this.tpl.insertBefore(n, d);
28490             }else{
28491                 
28492                 this.tpl.append(this.el, d);
28493             }
28494         }
28495         this.updateIndexes(index);
28496     },
28497
28498     onRemove : function(ds, record, index){
28499        // Roo.log('onRemove');
28500         this.clearSelections();
28501         var el = this.dataName  ?
28502             this.el.child('.roo-tpl-' + this.dataName) :
28503             this.el; 
28504         
28505         el.dom.removeChild(this.nodes[index]);
28506         this.updateIndexes(index);
28507     },
28508
28509     /**
28510      * Refresh an individual node.
28511      * @param {Number} index
28512      */
28513     refreshNode : function(index){
28514         this.onUpdate(this.store, this.store.getAt(index));
28515     },
28516
28517     updateIndexes : function(startIndex, endIndex){
28518         var ns = this.nodes;
28519         startIndex = startIndex || 0;
28520         endIndex = endIndex || ns.length - 1;
28521         for(var i = startIndex; i <= endIndex; i++){
28522             ns[i].nodeIndex = i;
28523         }
28524     },
28525
28526     /**
28527      * Changes the data store this view uses and refresh the view.
28528      * @param {Store} store
28529      */
28530     setStore : function(store, initial){
28531         if(!initial && this.store){
28532             this.store.un("datachanged", this.refresh);
28533             this.store.un("add", this.onAdd);
28534             this.store.un("remove", this.onRemove);
28535             this.store.un("update", this.onUpdate);
28536             this.store.un("clear", this.refresh);
28537             this.store.un("beforeload", this.onBeforeLoad);
28538             this.store.un("load", this.onLoad);
28539             this.store.un("loadexception", this.onLoad);
28540         }
28541         if(store){
28542           
28543             store.on("datachanged", this.refresh, this);
28544             store.on("add", this.onAdd, this);
28545             store.on("remove", this.onRemove, this);
28546             store.on("update", this.onUpdate, this);
28547             store.on("clear", this.refresh, this);
28548             store.on("beforeload", this.onBeforeLoad, this);
28549             store.on("load", this.onLoad, this);
28550             store.on("loadexception", this.onLoad, this);
28551         }
28552         
28553         if(store){
28554             this.refresh();
28555         }
28556     },
28557     /**
28558      * onbeforeLoad - masks the loading area.
28559      *
28560      */
28561     onBeforeLoad : function(store,opts)
28562     {
28563          //Roo.log('onBeforeLoad');   
28564         if (!opts.add) {
28565             this.el.update("");
28566         }
28567         this.el.mask(this.mask ? this.mask : "Loading" ); 
28568     },
28569     onLoad : function ()
28570     {
28571         this.el.unmask();
28572     },
28573     
28574
28575     /**
28576      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28577      * @param {HTMLElement} node
28578      * @return {HTMLElement} The template node
28579      */
28580     findItemFromChild : function(node){
28581         var el = this.dataName  ?
28582             this.el.child('.roo-tpl-' + this.dataName,true) :
28583             this.el.dom; 
28584         
28585         if(!node || node.parentNode == el){
28586                     return node;
28587             }
28588             var p = node.parentNode;
28589             while(p && p != el){
28590             if(p.parentNode == el){
28591                 return p;
28592             }
28593             p = p.parentNode;
28594         }
28595             return null;
28596     },
28597
28598     /** @ignore */
28599     onClick : function(e){
28600         var item = this.findItemFromChild(e.getTarget());
28601         if(item){
28602             var index = this.indexOf(item);
28603             if(this.onItemClick(item, index, e) !== false){
28604                 this.fireEvent("click", this, index, item, e);
28605             }
28606         }else{
28607             this.clearSelections();
28608         }
28609     },
28610
28611     /** @ignore */
28612     onContextMenu : function(e){
28613         var item = this.findItemFromChild(e.getTarget());
28614         if(item){
28615             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28616         }
28617     },
28618
28619     /** @ignore */
28620     onDblClick : function(e){
28621         var item = this.findItemFromChild(e.getTarget());
28622         if(item){
28623             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28624         }
28625     },
28626
28627     onItemClick : function(item, index, e)
28628     {
28629         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28630             return false;
28631         }
28632         if (this.toggleSelect) {
28633             var m = this.isSelected(item) ? 'unselect' : 'select';
28634             //Roo.log(m);
28635             var _t = this;
28636             _t[m](item, true, false);
28637             return true;
28638         }
28639         if(this.multiSelect || this.singleSelect){
28640             if(this.multiSelect && e.shiftKey && this.lastSelection){
28641                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28642             }else{
28643                 this.select(item, this.multiSelect && e.ctrlKey);
28644                 this.lastSelection = item;
28645             }
28646             
28647             if(!this.tickable){
28648                 e.preventDefault();
28649             }
28650             
28651         }
28652         return true;
28653     },
28654
28655     /**
28656      * Get the number of selected nodes.
28657      * @return {Number}
28658      */
28659     getSelectionCount : function(){
28660         return this.selections.length;
28661     },
28662
28663     /**
28664      * Get the currently selected nodes.
28665      * @return {Array} An array of HTMLElements
28666      */
28667     getSelectedNodes : function(){
28668         return this.selections;
28669     },
28670
28671     /**
28672      * Get the indexes of the selected nodes.
28673      * @return {Array}
28674      */
28675     getSelectedIndexes : function(){
28676         var indexes = [], s = this.selections;
28677         for(var i = 0, len = s.length; i < len; i++){
28678             indexes.push(s[i].nodeIndex);
28679         }
28680         return indexes;
28681     },
28682
28683     /**
28684      * Clear all selections
28685      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28686      */
28687     clearSelections : function(suppressEvent){
28688         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28689             this.cmp.elements = this.selections;
28690             this.cmp.removeClass(this.selectedClass);
28691             this.selections = [];
28692             if(!suppressEvent){
28693                 this.fireEvent("selectionchange", this, this.selections);
28694             }
28695         }
28696     },
28697
28698     /**
28699      * Returns true if the passed node is selected
28700      * @param {HTMLElement/Number} node The node or node index
28701      * @return {Boolean}
28702      */
28703     isSelected : function(node){
28704         var s = this.selections;
28705         if(s.length < 1){
28706             return false;
28707         }
28708         node = this.getNode(node);
28709         return s.indexOf(node) !== -1;
28710     },
28711
28712     /**
28713      * Selects nodes.
28714      * @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
28715      * @param {Boolean} keepExisting (optional) true to keep existing selections
28716      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28717      */
28718     select : function(nodeInfo, keepExisting, suppressEvent){
28719         if(nodeInfo instanceof Array){
28720             if(!keepExisting){
28721                 this.clearSelections(true);
28722             }
28723             for(var i = 0, len = nodeInfo.length; i < len; i++){
28724                 this.select(nodeInfo[i], true, true);
28725             }
28726             return;
28727         } 
28728         var node = this.getNode(nodeInfo);
28729         if(!node || this.isSelected(node)){
28730             return; // already selected.
28731         }
28732         if(!keepExisting){
28733             this.clearSelections(true);
28734         }
28735         
28736         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28737             Roo.fly(node).addClass(this.selectedClass);
28738             this.selections.push(node);
28739             if(!suppressEvent){
28740                 this.fireEvent("selectionchange", this, this.selections);
28741             }
28742         }
28743         
28744         
28745     },
28746       /**
28747      * Unselects nodes.
28748      * @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
28749      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28750      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28751      */
28752     unselect : function(nodeInfo, keepExisting, suppressEvent)
28753     {
28754         if(nodeInfo instanceof Array){
28755             Roo.each(this.selections, function(s) {
28756                 this.unselect(s, nodeInfo);
28757             }, this);
28758             return;
28759         }
28760         var node = this.getNode(nodeInfo);
28761         if(!node || !this.isSelected(node)){
28762             //Roo.log("not selected");
28763             return; // not selected.
28764         }
28765         // fireevent???
28766         var ns = [];
28767         Roo.each(this.selections, function(s) {
28768             if (s == node ) {
28769                 Roo.fly(node).removeClass(this.selectedClass);
28770
28771                 return;
28772             }
28773             ns.push(s);
28774         },this);
28775         
28776         this.selections= ns;
28777         this.fireEvent("selectionchange", this, this.selections);
28778     },
28779
28780     /**
28781      * Gets a template node.
28782      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28783      * @return {HTMLElement} The node or null if it wasn't found
28784      */
28785     getNode : function(nodeInfo){
28786         if(typeof nodeInfo == "string"){
28787             return document.getElementById(nodeInfo);
28788         }else if(typeof nodeInfo == "number"){
28789             return this.nodes[nodeInfo];
28790         }
28791         return nodeInfo;
28792     },
28793
28794     /**
28795      * Gets a range template nodes.
28796      * @param {Number} startIndex
28797      * @param {Number} endIndex
28798      * @return {Array} An array of nodes
28799      */
28800     getNodes : function(start, end){
28801         var ns = this.nodes;
28802         start = start || 0;
28803         end = typeof end == "undefined" ? ns.length - 1 : end;
28804         var nodes = [];
28805         if(start <= end){
28806             for(var i = start; i <= end; i++){
28807                 nodes.push(ns[i]);
28808             }
28809         } else{
28810             for(var i = start; i >= end; i--){
28811                 nodes.push(ns[i]);
28812             }
28813         }
28814         return nodes;
28815     },
28816
28817     /**
28818      * Finds the index of the passed node
28819      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28820      * @return {Number} The index of the node or -1
28821      */
28822     indexOf : function(node){
28823         node = this.getNode(node);
28824         if(typeof node.nodeIndex == "number"){
28825             return node.nodeIndex;
28826         }
28827         var ns = this.nodes;
28828         for(var i = 0, len = ns.length; i < len; i++){
28829             if(ns[i] == node){
28830                 return i;
28831             }
28832         }
28833         return -1;
28834     }
28835 });
28836 /*
28837  * Based on:
28838  * Ext JS Library 1.1.1
28839  * Copyright(c) 2006-2007, Ext JS, LLC.
28840  *
28841  * Originally Released Under LGPL - original licence link has changed is not relivant.
28842  *
28843  * Fork - LGPL
28844  * <script type="text/javascript">
28845  */
28846
28847 /**
28848  * @class Roo.JsonView
28849  * @extends Roo.View
28850  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28851 <pre><code>
28852 var view = new Roo.JsonView({
28853     container: "my-element",
28854     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28855     multiSelect: true, 
28856     jsonRoot: "data" 
28857 });
28858
28859 // listen for node click?
28860 view.on("click", function(vw, index, node, e){
28861     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28862 });
28863
28864 // direct load of JSON data
28865 view.load("foobar.php");
28866
28867 // Example from my blog list
28868 var tpl = new Roo.Template(
28869     '&lt;div class="entry"&gt;' +
28870     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28871     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28872     "&lt;/div&gt;&lt;hr /&gt;"
28873 );
28874
28875 var moreView = new Roo.JsonView({
28876     container :  "entry-list", 
28877     template : tpl,
28878     jsonRoot: "posts"
28879 });
28880 moreView.on("beforerender", this.sortEntries, this);
28881 moreView.load({
28882     url: "/blog/get-posts.php",
28883     params: "allposts=true",
28884     text: "Loading Blog Entries..."
28885 });
28886 </code></pre>
28887
28888 * Note: old code is supported with arguments : (container, template, config)
28889
28890
28891  * @constructor
28892  * Create a new JsonView
28893  * 
28894  * @param {Object} config The config object
28895  * 
28896  */
28897 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28898     
28899     
28900     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28901
28902     var um = this.el.getUpdateManager();
28903     um.setRenderer(this);
28904     um.on("update", this.onLoad, this);
28905     um.on("failure", this.onLoadException, this);
28906
28907     /**
28908      * @event beforerender
28909      * Fires before rendering of the downloaded JSON data.
28910      * @param {Roo.JsonView} this
28911      * @param {Object} data The JSON data loaded
28912      */
28913     /**
28914      * @event load
28915      * Fires when data is loaded.
28916      * @param {Roo.JsonView} this
28917      * @param {Object} data The JSON data loaded
28918      * @param {Object} response The raw Connect response object
28919      */
28920     /**
28921      * @event loadexception
28922      * Fires when loading fails.
28923      * @param {Roo.JsonView} this
28924      * @param {Object} response The raw Connect response object
28925      */
28926     this.addEvents({
28927         'beforerender' : true,
28928         'load' : true,
28929         'loadexception' : true
28930     });
28931 };
28932 Roo.extend(Roo.JsonView, Roo.View, {
28933     /**
28934      * @type {String} The root property in the loaded JSON object that contains the data
28935      */
28936     jsonRoot : "",
28937
28938     /**
28939      * Refreshes the view.
28940      */
28941     refresh : function(){
28942         this.clearSelections();
28943         this.el.update("");
28944         var html = [];
28945         var o = this.jsonData;
28946         if(o && o.length > 0){
28947             for(var i = 0, len = o.length; i < len; i++){
28948                 var data = this.prepareData(o[i], i, o);
28949                 html[html.length] = this.tpl.apply(data);
28950             }
28951         }else{
28952             html.push(this.emptyText);
28953         }
28954         this.el.update(html.join(""));
28955         this.nodes = this.el.dom.childNodes;
28956         this.updateIndexes(0);
28957     },
28958
28959     /**
28960      * 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.
28961      * @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:
28962      <pre><code>
28963      view.load({
28964          url: "your-url.php",
28965          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28966          callback: yourFunction,
28967          scope: yourObject, //(optional scope)
28968          discardUrl: false,
28969          nocache: false,
28970          text: "Loading...",
28971          timeout: 30,
28972          scripts: false
28973      });
28974      </code></pre>
28975      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28976      * 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.
28977      * @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}
28978      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28979      * @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.
28980      */
28981     load : function(){
28982         var um = this.el.getUpdateManager();
28983         um.update.apply(um, arguments);
28984     },
28985
28986     // note - render is a standard framework call...
28987     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28988     render : function(el, response){
28989         
28990         this.clearSelections();
28991         this.el.update("");
28992         var o;
28993         try{
28994             if (response != '') {
28995                 o = Roo.util.JSON.decode(response.responseText);
28996                 if(this.jsonRoot){
28997                     
28998                     o = o[this.jsonRoot];
28999                 }
29000             }
29001         } catch(e){
29002         }
29003         /**
29004          * The current JSON data or null
29005          */
29006         this.jsonData = o;
29007         this.beforeRender();
29008         this.refresh();
29009     },
29010
29011 /**
29012  * Get the number of records in the current JSON dataset
29013  * @return {Number}
29014  */
29015     getCount : function(){
29016         return this.jsonData ? this.jsonData.length : 0;
29017     },
29018
29019 /**
29020  * Returns the JSON object for the specified node(s)
29021  * @param {HTMLElement/Array} node The node or an array of nodes
29022  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
29023  * you get the JSON object for the node
29024  */
29025     getNodeData : function(node){
29026         if(node instanceof Array){
29027             var data = [];
29028             for(var i = 0, len = node.length; i < len; i++){
29029                 data.push(this.getNodeData(node[i]));
29030             }
29031             return data;
29032         }
29033         return this.jsonData[this.indexOf(node)] || null;
29034     },
29035
29036     beforeRender : function(){
29037         this.snapshot = this.jsonData;
29038         if(this.sortInfo){
29039             this.sort.apply(this, this.sortInfo);
29040         }
29041         this.fireEvent("beforerender", this, this.jsonData);
29042     },
29043
29044     onLoad : function(el, o){
29045         this.fireEvent("load", this, this.jsonData, o);
29046     },
29047
29048     onLoadException : function(el, o){
29049         this.fireEvent("loadexception", this, o);
29050     },
29051
29052 /**
29053  * Filter the data by a specific property.
29054  * @param {String} property A property on your JSON objects
29055  * @param {String/RegExp} value Either string that the property values
29056  * should start with, or a RegExp to test against the property
29057  */
29058     filter : function(property, value){
29059         if(this.jsonData){
29060             var data = [];
29061             var ss = this.snapshot;
29062             if(typeof value == "string"){
29063                 var vlen = value.length;
29064                 if(vlen == 0){
29065                     this.clearFilter();
29066                     return;
29067                 }
29068                 value = value.toLowerCase();
29069                 for(var i = 0, len = ss.length; i < len; i++){
29070                     var o = ss[i];
29071                     if(o[property].substr(0, vlen).toLowerCase() == value){
29072                         data.push(o);
29073                     }
29074                 }
29075             } else if(value.exec){ // regex?
29076                 for(var i = 0, len = ss.length; i < len; i++){
29077                     var o = ss[i];
29078                     if(value.test(o[property])){
29079                         data.push(o);
29080                     }
29081                 }
29082             } else{
29083                 return;
29084             }
29085             this.jsonData = data;
29086             this.refresh();
29087         }
29088     },
29089
29090 /**
29091  * Filter by a function. The passed function will be called with each
29092  * object in the current dataset. If the function returns true the value is kept,
29093  * otherwise it is filtered.
29094  * @param {Function} fn
29095  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29096  */
29097     filterBy : function(fn, scope){
29098         if(this.jsonData){
29099             var data = [];
29100             var ss = this.snapshot;
29101             for(var i = 0, len = ss.length; i < len; i++){
29102                 var o = ss[i];
29103                 if(fn.call(scope || this, o)){
29104                     data.push(o);
29105                 }
29106             }
29107             this.jsonData = data;
29108             this.refresh();
29109         }
29110     },
29111
29112 /**
29113  * Clears the current filter.
29114  */
29115     clearFilter : function(){
29116         if(this.snapshot && this.jsonData != this.snapshot){
29117             this.jsonData = this.snapshot;
29118             this.refresh();
29119         }
29120     },
29121
29122
29123 /**
29124  * Sorts the data for this view and refreshes it.
29125  * @param {String} property A property on your JSON objects to sort on
29126  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29127  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29128  */
29129     sort : function(property, dir, sortType){
29130         this.sortInfo = Array.prototype.slice.call(arguments, 0);
29131         if(this.jsonData){
29132             var p = property;
29133             var dsc = dir && dir.toLowerCase() == "desc";
29134             var f = function(o1, o2){
29135                 var v1 = sortType ? sortType(o1[p]) : o1[p];
29136                 var v2 = sortType ? sortType(o2[p]) : o2[p];
29137                 ;
29138                 if(v1 < v2){
29139                     return dsc ? +1 : -1;
29140                 } else if(v1 > v2){
29141                     return dsc ? -1 : +1;
29142                 } else{
29143                     return 0;
29144                 }
29145             };
29146             this.jsonData.sort(f);
29147             this.refresh();
29148             if(this.jsonData != this.snapshot){
29149                 this.snapshot.sort(f);
29150             }
29151         }
29152     }
29153 });/*
29154  * Based on:
29155  * Ext JS Library 1.1.1
29156  * Copyright(c) 2006-2007, Ext JS, LLC.
29157  *
29158  * Originally Released Under LGPL - original licence link has changed is not relivant.
29159  *
29160  * Fork - LGPL
29161  * <script type="text/javascript">
29162  */
29163  
29164
29165 /**
29166  * @class Roo.ColorPalette
29167  * @extends Roo.Component
29168  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
29169  * Here's an example of typical usage:
29170  * <pre><code>
29171 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
29172 cp.render('my-div');
29173
29174 cp.on('select', function(palette, selColor){
29175     // do something with selColor
29176 });
29177 </code></pre>
29178  * @constructor
29179  * Create a new ColorPalette
29180  * @param {Object} config The config object
29181  */
29182 Roo.ColorPalette = function(config){
29183     Roo.ColorPalette.superclass.constructor.call(this, config);
29184     this.addEvents({
29185         /**
29186              * @event select
29187              * Fires when a color is selected
29188              * @param {ColorPalette} this
29189              * @param {String} color The 6-digit color hex code (without the # symbol)
29190              */
29191         select: true
29192     });
29193
29194     if(this.handler){
29195         this.on("select", this.handler, this.scope, true);
29196     }
29197 };
29198 Roo.extend(Roo.ColorPalette, Roo.Component, {
29199     /**
29200      * @cfg {String} itemCls
29201      * The CSS class to apply to the containing element (defaults to "x-color-palette")
29202      */
29203     itemCls : "x-color-palette",
29204     /**
29205      * @cfg {String} value
29206      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29207      * the hex codes are case-sensitive.
29208      */
29209     value : null,
29210     clickEvent:'click',
29211     // private
29212     ctype: "Roo.ColorPalette",
29213
29214     /**
29215      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29216      */
29217     allowReselect : false,
29218
29219     /**
29220      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29221      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29222      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29223      * of colors with the width setting until the box is symmetrical.</p>
29224      * <p>You can override individual colors if needed:</p>
29225      * <pre><code>
29226 var cp = new Roo.ColorPalette();
29227 cp.colors[0] = "FF0000";  // change the first box to red
29228 </code></pre>
29229
29230 Or you can provide a custom array of your own for complete control:
29231 <pre><code>
29232 var cp = new Roo.ColorPalette();
29233 cp.colors = ["000000", "993300", "333300"];
29234 </code></pre>
29235      * @type Array
29236      */
29237     colors : [
29238         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29239         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29240         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29241         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29242         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29243     ],
29244
29245     // private
29246     onRender : function(container, position){
29247         var t = new Roo.MasterTemplate(
29248             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29249         );
29250         var c = this.colors;
29251         for(var i = 0, len = c.length; i < len; i++){
29252             t.add([c[i]]);
29253         }
29254         var el = document.createElement("div");
29255         el.className = this.itemCls;
29256         t.overwrite(el);
29257         container.dom.insertBefore(el, position);
29258         this.el = Roo.get(el);
29259         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29260         if(this.clickEvent != 'click'){
29261             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29262         }
29263     },
29264
29265     // private
29266     afterRender : function(){
29267         Roo.ColorPalette.superclass.afterRender.call(this);
29268         if(this.value){
29269             var s = this.value;
29270             this.value = null;
29271             this.select(s);
29272         }
29273     },
29274
29275     // private
29276     handleClick : function(e, t){
29277         e.preventDefault();
29278         if(!this.disabled){
29279             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29280             this.select(c.toUpperCase());
29281         }
29282     },
29283
29284     /**
29285      * Selects the specified color in the palette (fires the select event)
29286      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29287      */
29288     select : function(color){
29289         color = color.replace("#", "");
29290         if(color != this.value || this.allowReselect){
29291             var el = this.el;
29292             if(this.value){
29293                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29294             }
29295             el.child("a.color-"+color).addClass("x-color-palette-sel");
29296             this.value = color;
29297             this.fireEvent("select", this, color);
29298         }
29299     }
29300 });/*
29301  * Based on:
29302  * Ext JS Library 1.1.1
29303  * Copyright(c) 2006-2007, Ext JS, LLC.
29304  *
29305  * Originally Released Under LGPL - original licence link has changed is not relivant.
29306  *
29307  * Fork - LGPL
29308  * <script type="text/javascript">
29309  */
29310  
29311 /**
29312  * @class Roo.DatePicker
29313  * @extends Roo.Component
29314  * Simple date picker class.
29315  * @constructor
29316  * Create a new DatePicker
29317  * @param {Object} config The config object
29318  */
29319 Roo.DatePicker = function(config){
29320     Roo.DatePicker.superclass.constructor.call(this, config);
29321
29322     this.value = config && config.value ?
29323                  config.value.clearTime() : new Date().clearTime();
29324
29325     this.addEvents({
29326         /**
29327              * @event select
29328              * Fires when a date is selected
29329              * @param {DatePicker} this
29330              * @param {Date} date The selected date
29331              */
29332         'select': true,
29333         /**
29334              * @event monthchange
29335              * Fires when the displayed month changes 
29336              * @param {DatePicker} this
29337              * @param {Date} date The selected month
29338              */
29339         'monthchange': true
29340     });
29341
29342     if(this.handler){
29343         this.on("select", this.handler,  this.scope || this);
29344     }
29345     // build the disabledDatesRE
29346     if(!this.disabledDatesRE && this.disabledDates){
29347         var dd = this.disabledDates;
29348         var re = "(?:";
29349         for(var i = 0; i < dd.length; i++){
29350             re += dd[i];
29351             if(i != dd.length-1) {
29352                 re += "|";
29353             }
29354         }
29355         this.disabledDatesRE = new RegExp(re + ")");
29356     }
29357 };
29358
29359 Roo.extend(Roo.DatePicker, Roo.Component, {
29360     /**
29361      * @cfg {String} todayText
29362      * The text to display on the button that selects the current date (defaults to "Today")
29363      */
29364     todayText : "Today",
29365     /**
29366      * @cfg {String} okText
29367      * The text to display on the ok button
29368      */
29369     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29370     /**
29371      * @cfg {String} cancelText
29372      * The text to display on the cancel button
29373      */
29374     cancelText : "Cancel",
29375     /**
29376      * @cfg {String} todayTip
29377      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29378      */
29379     todayTip : "{0} (Spacebar)",
29380     /**
29381      * @cfg {Date} minDate
29382      * Minimum allowable date (JavaScript date object, defaults to null)
29383      */
29384     minDate : null,
29385     /**
29386      * @cfg {Date} maxDate
29387      * Maximum allowable date (JavaScript date object, defaults to null)
29388      */
29389     maxDate : null,
29390     /**
29391      * @cfg {String} minText
29392      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29393      */
29394     minText : "This date is before the minimum date",
29395     /**
29396      * @cfg {String} maxText
29397      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29398      */
29399     maxText : "This date is after the maximum date",
29400     /**
29401      * @cfg {String} format
29402      * The default date format string which can be overriden for localization support.  The format must be
29403      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29404      */
29405     format : "m/d/y",
29406     /**
29407      * @cfg {Array} disabledDays
29408      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29409      */
29410     disabledDays : null,
29411     /**
29412      * @cfg {String} disabledDaysText
29413      * The tooltip to display when the date falls on a disabled day (defaults to "")
29414      */
29415     disabledDaysText : "",
29416     /**
29417      * @cfg {RegExp} disabledDatesRE
29418      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29419      */
29420     disabledDatesRE : null,
29421     /**
29422      * @cfg {String} disabledDatesText
29423      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29424      */
29425     disabledDatesText : "",
29426     /**
29427      * @cfg {Boolean} constrainToViewport
29428      * True to constrain the date picker to the viewport (defaults to true)
29429      */
29430     constrainToViewport : true,
29431     /**
29432      * @cfg {Array} monthNames
29433      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29434      */
29435     monthNames : Date.monthNames,
29436     /**
29437      * @cfg {Array} dayNames
29438      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29439      */
29440     dayNames : Date.dayNames,
29441     /**
29442      * @cfg {String} nextText
29443      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29444      */
29445     nextText: 'Next Month (Control+Right)',
29446     /**
29447      * @cfg {String} prevText
29448      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29449      */
29450     prevText: 'Previous Month (Control+Left)',
29451     /**
29452      * @cfg {String} monthYearText
29453      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29454      */
29455     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29456     /**
29457      * @cfg {Number} startDay
29458      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29459      */
29460     startDay : 0,
29461     /**
29462      * @cfg {Bool} showClear
29463      * Show a clear button (usefull for date form elements that can be blank.)
29464      */
29465     
29466     showClear: false,
29467     
29468     /**
29469      * Sets the value of the date field
29470      * @param {Date} value The date to set
29471      */
29472     setValue : function(value){
29473         var old = this.value;
29474         
29475         if (typeof(value) == 'string') {
29476          
29477             value = Date.parseDate(value, this.format);
29478         }
29479         if (!value) {
29480             value = new Date();
29481         }
29482         
29483         this.value = value.clearTime(true);
29484         if(this.el){
29485             this.update(this.value);
29486         }
29487     },
29488
29489     /**
29490      * Gets the current selected value of the date field
29491      * @return {Date} The selected date
29492      */
29493     getValue : function(){
29494         return this.value;
29495     },
29496
29497     // private
29498     focus : function(){
29499         if(this.el){
29500             this.update(this.activeDate);
29501         }
29502     },
29503
29504     // privateval
29505     onRender : function(container, position){
29506         
29507         var m = [
29508              '<table cellspacing="0">',
29509                 '<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>',
29510                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29511         var dn = this.dayNames;
29512         for(var i = 0; i < 7; i++){
29513             var d = this.startDay+i;
29514             if(d > 6){
29515                 d = d-7;
29516             }
29517             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29518         }
29519         m[m.length] = "</tr></thead><tbody><tr>";
29520         for(var i = 0; i < 42; i++) {
29521             if(i % 7 == 0 && i != 0){
29522                 m[m.length] = "</tr><tr>";
29523             }
29524             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29525         }
29526         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29527             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29528
29529         var el = document.createElement("div");
29530         el.className = "x-date-picker";
29531         el.innerHTML = m.join("");
29532
29533         container.dom.insertBefore(el, position);
29534
29535         this.el = Roo.get(el);
29536         this.eventEl = Roo.get(el.firstChild);
29537
29538         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29539             handler: this.showPrevMonth,
29540             scope: this,
29541             preventDefault:true,
29542             stopDefault:true
29543         });
29544
29545         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29546             handler: this.showNextMonth,
29547             scope: this,
29548             preventDefault:true,
29549             stopDefault:true
29550         });
29551
29552         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29553
29554         this.monthPicker = this.el.down('div.x-date-mp');
29555         this.monthPicker.enableDisplayMode('block');
29556         
29557         var kn = new Roo.KeyNav(this.eventEl, {
29558             "left" : function(e){
29559                 e.ctrlKey ?
29560                     this.showPrevMonth() :
29561                     this.update(this.activeDate.add("d", -1));
29562             },
29563
29564             "right" : function(e){
29565                 e.ctrlKey ?
29566                     this.showNextMonth() :
29567                     this.update(this.activeDate.add("d", 1));
29568             },
29569
29570             "up" : function(e){
29571                 e.ctrlKey ?
29572                     this.showNextYear() :
29573                     this.update(this.activeDate.add("d", -7));
29574             },
29575
29576             "down" : function(e){
29577                 e.ctrlKey ?
29578                     this.showPrevYear() :
29579                     this.update(this.activeDate.add("d", 7));
29580             },
29581
29582             "pageUp" : function(e){
29583                 this.showNextMonth();
29584             },
29585
29586             "pageDown" : function(e){
29587                 this.showPrevMonth();
29588             },
29589
29590             "enter" : function(e){
29591                 e.stopPropagation();
29592                 return true;
29593             },
29594
29595             scope : this
29596         });
29597
29598         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29599
29600         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29601
29602         this.el.unselectable();
29603         
29604         this.cells = this.el.select("table.x-date-inner tbody td");
29605         this.textNodes = this.el.query("table.x-date-inner tbody span");
29606
29607         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29608             text: "&#160;",
29609             tooltip: this.monthYearText
29610         });
29611
29612         this.mbtn.on('click', this.showMonthPicker, this);
29613         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29614
29615
29616         var today = (new Date()).dateFormat(this.format);
29617         
29618         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29619         if (this.showClear) {
29620             baseTb.add( new Roo.Toolbar.Fill());
29621         }
29622         baseTb.add({
29623             text: String.format(this.todayText, today),
29624             tooltip: String.format(this.todayTip, today),
29625             handler: this.selectToday,
29626             scope: this
29627         });
29628         
29629         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29630             
29631         //});
29632         if (this.showClear) {
29633             
29634             baseTb.add( new Roo.Toolbar.Fill());
29635             baseTb.add({
29636                 text: '&#160;',
29637                 cls: 'x-btn-icon x-btn-clear',
29638                 handler: function() {
29639                     //this.value = '';
29640                     this.fireEvent("select", this, '');
29641                 },
29642                 scope: this
29643             });
29644         }
29645         
29646         
29647         if(Roo.isIE){
29648             this.el.repaint();
29649         }
29650         this.update(this.value);
29651     },
29652
29653     createMonthPicker : function(){
29654         if(!this.monthPicker.dom.firstChild){
29655             var buf = ['<table border="0" cellspacing="0">'];
29656             for(var i = 0; i < 6; i++){
29657                 buf.push(
29658                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29659                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29660                     i == 0 ?
29661                     '<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>' :
29662                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29663                 );
29664             }
29665             buf.push(
29666                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29667                     this.okText,
29668                     '</button><button type="button" class="x-date-mp-cancel">',
29669                     this.cancelText,
29670                     '</button></td></tr>',
29671                 '</table>'
29672             );
29673             this.monthPicker.update(buf.join(''));
29674             this.monthPicker.on('click', this.onMonthClick, this);
29675             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29676
29677             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29678             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29679
29680             this.mpMonths.each(function(m, a, i){
29681                 i += 1;
29682                 if((i%2) == 0){
29683                     m.dom.xmonth = 5 + Math.round(i * .5);
29684                 }else{
29685                     m.dom.xmonth = Math.round((i-1) * .5);
29686                 }
29687             });
29688         }
29689     },
29690
29691     showMonthPicker : function(){
29692         this.createMonthPicker();
29693         var size = this.el.getSize();
29694         this.monthPicker.setSize(size);
29695         this.monthPicker.child('table').setSize(size);
29696
29697         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29698         this.updateMPMonth(this.mpSelMonth);
29699         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29700         this.updateMPYear(this.mpSelYear);
29701
29702         this.monthPicker.slideIn('t', {duration:.2});
29703     },
29704
29705     updateMPYear : function(y){
29706         this.mpyear = y;
29707         var ys = this.mpYears.elements;
29708         for(var i = 1; i <= 10; i++){
29709             var td = ys[i-1], y2;
29710             if((i%2) == 0){
29711                 y2 = y + Math.round(i * .5);
29712                 td.firstChild.innerHTML = y2;
29713                 td.xyear = y2;
29714             }else{
29715                 y2 = y - (5-Math.round(i * .5));
29716                 td.firstChild.innerHTML = y2;
29717                 td.xyear = y2;
29718             }
29719             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29720         }
29721     },
29722
29723     updateMPMonth : function(sm){
29724         this.mpMonths.each(function(m, a, i){
29725             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29726         });
29727     },
29728
29729     selectMPMonth: function(m){
29730         
29731     },
29732
29733     onMonthClick : function(e, t){
29734         e.stopEvent();
29735         var el = new Roo.Element(t), pn;
29736         if(el.is('button.x-date-mp-cancel')){
29737             this.hideMonthPicker();
29738         }
29739         else if(el.is('button.x-date-mp-ok')){
29740             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29741             this.hideMonthPicker();
29742         }
29743         else if(pn = el.up('td.x-date-mp-month', 2)){
29744             this.mpMonths.removeClass('x-date-mp-sel');
29745             pn.addClass('x-date-mp-sel');
29746             this.mpSelMonth = pn.dom.xmonth;
29747         }
29748         else if(pn = el.up('td.x-date-mp-year', 2)){
29749             this.mpYears.removeClass('x-date-mp-sel');
29750             pn.addClass('x-date-mp-sel');
29751             this.mpSelYear = pn.dom.xyear;
29752         }
29753         else if(el.is('a.x-date-mp-prev')){
29754             this.updateMPYear(this.mpyear-10);
29755         }
29756         else if(el.is('a.x-date-mp-next')){
29757             this.updateMPYear(this.mpyear+10);
29758         }
29759     },
29760
29761     onMonthDblClick : function(e, t){
29762         e.stopEvent();
29763         var el = new Roo.Element(t), pn;
29764         if(pn = el.up('td.x-date-mp-month', 2)){
29765             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29766             this.hideMonthPicker();
29767         }
29768         else if(pn = el.up('td.x-date-mp-year', 2)){
29769             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29770             this.hideMonthPicker();
29771         }
29772     },
29773
29774     hideMonthPicker : function(disableAnim){
29775         if(this.monthPicker){
29776             if(disableAnim === true){
29777                 this.monthPicker.hide();
29778             }else{
29779                 this.monthPicker.slideOut('t', {duration:.2});
29780             }
29781         }
29782     },
29783
29784     // private
29785     showPrevMonth : function(e){
29786         this.update(this.activeDate.add("mo", -1));
29787     },
29788
29789     // private
29790     showNextMonth : function(e){
29791         this.update(this.activeDate.add("mo", 1));
29792     },
29793
29794     // private
29795     showPrevYear : function(){
29796         this.update(this.activeDate.add("y", -1));
29797     },
29798
29799     // private
29800     showNextYear : function(){
29801         this.update(this.activeDate.add("y", 1));
29802     },
29803
29804     // private
29805     handleMouseWheel : function(e){
29806         var delta = e.getWheelDelta();
29807         if(delta > 0){
29808             this.showPrevMonth();
29809             e.stopEvent();
29810         } else if(delta < 0){
29811             this.showNextMonth();
29812             e.stopEvent();
29813         }
29814     },
29815
29816     // private
29817     handleDateClick : function(e, t){
29818         e.stopEvent();
29819         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29820             this.setValue(new Date(t.dateValue));
29821             this.fireEvent("select", this, this.value);
29822         }
29823     },
29824
29825     // private
29826     selectToday : function(){
29827         this.setValue(new Date().clearTime());
29828         this.fireEvent("select", this, this.value);
29829     },
29830
29831     // private
29832     update : function(date)
29833     {
29834         var vd = this.activeDate;
29835         this.activeDate = date;
29836         if(vd && this.el){
29837             var t = date.getTime();
29838             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29839                 this.cells.removeClass("x-date-selected");
29840                 this.cells.each(function(c){
29841                    if(c.dom.firstChild.dateValue == t){
29842                        c.addClass("x-date-selected");
29843                        setTimeout(function(){
29844                             try{c.dom.firstChild.focus();}catch(e){}
29845                        }, 50);
29846                        return false;
29847                    }
29848                 });
29849                 return;
29850             }
29851         }
29852         
29853         var days = date.getDaysInMonth();
29854         var firstOfMonth = date.getFirstDateOfMonth();
29855         var startingPos = firstOfMonth.getDay()-this.startDay;
29856
29857         if(startingPos <= this.startDay){
29858             startingPos += 7;
29859         }
29860
29861         var pm = date.add("mo", -1);
29862         var prevStart = pm.getDaysInMonth()-startingPos;
29863
29864         var cells = this.cells.elements;
29865         var textEls = this.textNodes;
29866         days += startingPos;
29867
29868         // convert everything to numbers so it's fast
29869         var day = 86400000;
29870         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29871         var today = new Date().clearTime().getTime();
29872         var sel = date.clearTime().getTime();
29873         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29874         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29875         var ddMatch = this.disabledDatesRE;
29876         var ddText = this.disabledDatesText;
29877         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29878         var ddaysText = this.disabledDaysText;
29879         var format = this.format;
29880
29881         var setCellClass = function(cal, cell){
29882             cell.title = "";
29883             var t = d.getTime();
29884             cell.firstChild.dateValue = t;
29885             if(t == today){
29886                 cell.className += " x-date-today";
29887                 cell.title = cal.todayText;
29888             }
29889             if(t == sel){
29890                 cell.className += " x-date-selected";
29891                 setTimeout(function(){
29892                     try{cell.firstChild.focus();}catch(e){}
29893                 }, 50);
29894             }
29895             // disabling
29896             if(t < min) {
29897                 cell.className = " x-date-disabled";
29898                 cell.title = cal.minText;
29899                 return;
29900             }
29901             if(t > max) {
29902                 cell.className = " x-date-disabled";
29903                 cell.title = cal.maxText;
29904                 return;
29905             }
29906             if(ddays){
29907                 if(ddays.indexOf(d.getDay()) != -1){
29908                     cell.title = ddaysText;
29909                     cell.className = " x-date-disabled";
29910                 }
29911             }
29912             if(ddMatch && format){
29913                 var fvalue = d.dateFormat(format);
29914                 if(ddMatch.test(fvalue)){
29915                     cell.title = ddText.replace("%0", fvalue);
29916                     cell.className = " x-date-disabled";
29917                 }
29918             }
29919         };
29920
29921         var i = 0;
29922         for(; i < startingPos; i++) {
29923             textEls[i].innerHTML = (++prevStart);
29924             d.setDate(d.getDate()+1);
29925             cells[i].className = "x-date-prevday";
29926             setCellClass(this, cells[i]);
29927         }
29928         for(; i < days; i++){
29929             intDay = i - startingPos + 1;
29930             textEls[i].innerHTML = (intDay);
29931             d.setDate(d.getDate()+1);
29932             cells[i].className = "x-date-active";
29933             setCellClass(this, cells[i]);
29934         }
29935         var extraDays = 0;
29936         for(; i < 42; i++) {
29937              textEls[i].innerHTML = (++extraDays);
29938              d.setDate(d.getDate()+1);
29939              cells[i].className = "x-date-nextday";
29940              setCellClass(this, cells[i]);
29941         }
29942
29943         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29944         this.fireEvent('monthchange', this, date);
29945         
29946         if(!this.internalRender){
29947             var main = this.el.dom.firstChild;
29948             var w = main.offsetWidth;
29949             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29950             Roo.fly(main).setWidth(w);
29951             this.internalRender = true;
29952             // opera does not respect the auto grow header center column
29953             // then, after it gets a width opera refuses to recalculate
29954             // without a second pass
29955             if(Roo.isOpera && !this.secondPass){
29956                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29957                 this.secondPass = true;
29958                 this.update.defer(10, this, [date]);
29959             }
29960         }
29961         
29962         
29963     }
29964 });        /*
29965  * Based on:
29966  * Ext JS Library 1.1.1
29967  * Copyright(c) 2006-2007, Ext JS, LLC.
29968  *
29969  * Originally Released Under LGPL - original licence link has changed is not relivant.
29970  *
29971  * Fork - LGPL
29972  * <script type="text/javascript">
29973  */
29974 /**
29975  * @class Roo.TabPanel
29976  * @extends Roo.util.Observable
29977  * A lightweight tab container.
29978  * <br><br>
29979  * Usage:
29980  * <pre><code>
29981 // basic tabs 1, built from existing content
29982 var tabs = new Roo.TabPanel("tabs1");
29983 tabs.addTab("script", "View Script");
29984 tabs.addTab("markup", "View Markup");
29985 tabs.activate("script");
29986
29987 // more advanced tabs, built from javascript
29988 var jtabs = new Roo.TabPanel("jtabs");
29989 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29990
29991 // set up the UpdateManager
29992 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29993 var updater = tab2.getUpdateManager();
29994 updater.setDefaultUrl("ajax1.htm");
29995 tab2.on('activate', updater.refresh, updater, true);
29996
29997 // Use setUrl for Ajax loading
29998 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29999 tab3.setUrl("ajax2.htm", null, true);
30000
30001 // Disabled tab
30002 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
30003 tab4.disable();
30004
30005 jtabs.activate("jtabs-1");
30006  * </code></pre>
30007  * @constructor
30008  * Create a new TabPanel.
30009  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
30010  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
30011  */
30012 Roo.TabPanel = function(container, config){
30013     /**
30014     * The container element for this TabPanel.
30015     * @type Roo.Element
30016     */
30017     this.el = Roo.get(container, true);
30018     if(config){
30019         if(typeof config == "boolean"){
30020             this.tabPosition = config ? "bottom" : "top";
30021         }else{
30022             Roo.apply(this, config);
30023         }
30024     }
30025     if(this.tabPosition == "bottom"){
30026         this.bodyEl = Roo.get(this.createBody(this.el.dom));
30027         this.el.addClass("x-tabs-bottom");
30028     }
30029     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
30030     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
30031     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
30032     if(Roo.isIE){
30033         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
30034     }
30035     if(this.tabPosition != "bottom"){
30036         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
30037          * @type Roo.Element
30038          */
30039         this.bodyEl = Roo.get(this.createBody(this.el.dom));
30040         this.el.addClass("x-tabs-top");
30041     }
30042     this.items = [];
30043
30044     this.bodyEl.setStyle("position", "relative");
30045
30046     this.active = null;
30047     this.activateDelegate = this.activate.createDelegate(this);
30048
30049     this.addEvents({
30050         /**
30051          * @event tabchange
30052          * Fires when the active tab changes
30053          * @param {Roo.TabPanel} this
30054          * @param {Roo.TabPanelItem} activePanel The new active tab
30055          */
30056         "tabchange": true,
30057         /**
30058          * @event beforetabchange
30059          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
30060          * @param {Roo.TabPanel} this
30061          * @param {Object} e Set cancel to true on this object to cancel the tab change
30062          * @param {Roo.TabPanelItem} tab The tab being changed to
30063          */
30064         "beforetabchange" : true
30065     });
30066
30067     Roo.EventManager.onWindowResize(this.onResize, this);
30068     this.cpad = this.el.getPadding("lr");
30069     this.hiddenCount = 0;
30070
30071
30072     // toolbar on the tabbar support...
30073     if (this.toolbar) {
30074         var tcfg = this.toolbar;
30075         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
30076         this.toolbar = new Roo.Toolbar(tcfg);
30077         if (Roo.isSafari) {
30078             var tbl = tcfg.container.child('table', true);
30079             tbl.setAttribute('width', '100%');
30080         }
30081         
30082     }
30083    
30084
30085
30086     Roo.TabPanel.superclass.constructor.call(this);
30087 };
30088
30089 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
30090     /*
30091      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
30092      */
30093     tabPosition : "top",
30094     /*
30095      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
30096      */
30097     currentTabWidth : 0,
30098     /*
30099      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
30100      */
30101     minTabWidth : 40,
30102     /*
30103      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
30104      */
30105     maxTabWidth : 250,
30106     /*
30107      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
30108      */
30109     preferredTabWidth : 175,
30110     /*
30111      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
30112      */
30113     resizeTabs : false,
30114     /*
30115      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
30116      */
30117     monitorResize : true,
30118     /*
30119      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
30120      */
30121     toolbar : false,
30122
30123     /**
30124      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
30125      * @param {String} id The id of the div to use <b>or create</b>
30126      * @param {String} text The text for the tab
30127      * @param {String} content (optional) Content to put in the TabPanelItem body
30128      * @param {Boolean} closable (optional) True to create a close icon on the tab
30129      * @return {Roo.TabPanelItem} The created TabPanelItem
30130      */
30131     addTab : function(id, text, content, closable){
30132         var item = new Roo.TabPanelItem(this, id, text, closable);
30133         this.addTabItem(item);
30134         if(content){
30135             item.setContent(content);
30136         }
30137         return item;
30138     },
30139
30140     /**
30141      * Returns the {@link Roo.TabPanelItem} with the specified id/index
30142      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
30143      * @return {Roo.TabPanelItem}
30144      */
30145     getTab : function(id){
30146         return this.items[id];
30147     },
30148
30149     /**
30150      * Hides the {@link Roo.TabPanelItem} with the specified id/index
30151      * @param {String/Number} id The id or index of the TabPanelItem to hide.
30152      */
30153     hideTab : function(id){
30154         var t = this.items[id];
30155         if(!t.isHidden()){
30156            t.setHidden(true);
30157            this.hiddenCount++;
30158            this.autoSizeTabs();
30159         }
30160     },
30161
30162     /**
30163      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
30164      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
30165      */
30166     unhideTab : function(id){
30167         var t = this.items[id];
30168         if(t.isHidden()){
30169            t.setHidden(false);
30170            this.hiddenCount--;
30171            this.autoSizeTabs();
30172         }
30173     },
30174
30175     /**
30176      * Adds an existing {@link Roo.TabPanelItem}.
30177      * @param {Roo.TabPanelItem} item The TabPanelItem to add
30178      */
30179     addTabItem : function(item){
30180         this.items[item.id] = item;
30181         this.items.push(item);
30182         if(this.resizeTabs){
30183            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
30184            this.autoSizeTabs();
30185         }else{
30186             item.autoSize();
30187         }
30188     },
30189
30190     /**
30191      * Removes a {@link Roo.TabPanelItem}.
30192      * @param {String/Number} id The id or index of the TabPanelItem to remove.
30193      */
30194     removeTab : function(id){
30195         var items = this.items;
30196         var tab = items[id];
30197         if(!tab) { return; }
30198         var index = items.indexOf(tab);
30199         if(this.active == tab && items.length > 1){
30200             var newTab = this.getNextAvailable(index);
30201             if(newTab) {
30202                 newTab.activate();
30203             }
30204         }
30205         this.stripEl.dom.removeChild(tab.pnode.dom);
30206         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30207             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30208         }
30209         items.splice(index, 1);
30210         delete this.items[tab.id];
30211         tab.fireEvent("close", tab);
30212         tab.purgeListeners();
30213         this.autoSizeTabs();
30214     },
30215
30216     getNextAvailable : function(start){
30217         var items = this.items;
30218         var index = start;
30219         // look for a next tab that will slide over to
30220         // replace the one being removed
30221         while(index < items.length){
30222             var item = items[++index];
30223             if(item && !item.isHidden()){
30224                 return item;
30225             }
30226         }
30227         // if one isn't found select the previous tab (on the left)
30228         index = start;
30229         while(index >= 0){
30230             var item = items[--index];
30231             if(item && !item.isHidden()){
30232                 return item;
30233             }
30234         }
30235         return null;
30236     },
30237
30238     /**
30239      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30240      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30241      */
30242     disableTab : function(id){
30243         var tab = this.items[id];
30244         if(tab && this.active != tab){
30245             tab.disable();
30246         }
30247     },
30248
30249     /**
30250      * Enables a {@link Roo.TabPanelItem} that is disabled.
30251      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30252      */
30253     enableTab : function(id){
30254         var tab = this.items[id];
30255         tab.enable();
30256     },
30257
30258     /**
30259      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30260      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30261      * @return {Roo.TabPanelItem} The TabPanelItem.
30262      */
30263     activate : function(id){
30264         var tab = this.items[id];
30265         if(!tab){
30266             return null;
30267         }
30268         if(tab == this.active || tab.disabled){
30269             return tab;
30270         }
30271         var e = {};
30272         this.fireEvent("beforetabchange", this, e, tab);
30273         if(e.cancel !== true && !tab.disabled){
30274             if(this.active){
30275                 this.active.hide();
30276             }
30277             this.active = this.items[id];
30278             this.active.show();
30279             this.fireEvent("tabchange", this, this.active);
30280         }
30281         return tab;
30282     },
30283
30284     /**
30285      * Gets the active {@link Roo.TabPanelItem}.
30286      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30287      */
30288     getActiveTab : function(){
30289         return this.active;
30290     },
30291
30292     /**
30293      * Updates the tab body element to fit the height of the container element
30294      * for overflow scrolling
30295      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30296      */
30297     syncHeight : function(targetHeight){
30298         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30299         var bm = this.bodyEl.getMargins();
30300         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30301         this.bodyEl.setHeight(newHeight);
30302         return newHeight;
30303     },
30304
30305     onResize : function(){
30306         if(this.monitorResize){
30307             this.autoSizeTabs();
30308         }
30309     },
30310
30311     /**
30312      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30313      */
30314     beginUpdate : function(){
30315         this.updating = true;
30316     },
30317
30318     /**
30319      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30320      */
30321     endUpdate : function(){
30322         this.updating = false;
30323         this.autoSizeTabs();
30324     },
30325
30326     /**
30327      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30328      */
30329     autoSizeTabs : function(){
30330         var count = this.items.length;
30331         var vcount = count - this.hiddenCount;
30332         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30333             return;
30334         }
30335         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30336         var availWidth = Math.floor(w / vcount);
30337         var b = this.stripBody;
30338         if(b.getWidth() > w){
30339             var tabs = this.items;
30340             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30341             if(availWidth < this.minTabWidth){
30342                 /*if(!this.sleft){    // incomplete scrolling code
30343                     this.createScrollButtons();
30344                 }
30345                 this.showScroll();
30346                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30347             }
30348         }else{
30349             if(this.currentTabWidth < this.preferredTabWidth){
30350                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30351             }
30352         }
30353     },
30354
30355     /**
30356      * Returns the number of tabs in this TabPanel.
30357      * @return {Number}
30358      */
30359      getCount : function(){
30360          return this.items.length;
30361      },
30362
30363     /**
30364      * Resizes all the tabs to the passed width
30365      * @param {Number} The new width
30366      */
30367     setTabWidth : function(width){
30368         this.currentTabWidth = width;
30369         for(var i = 0, len = this.items.length; i < len; i++) {
30370                 if(!this.items[i].isHidden()) {
30371                 this.items[i].setWidth(width);
30372             }
30373         }
30374     },
30375
30376     /**
30377      * Destroys this TabPanel
30378      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30379      */
30380     destroy : function(removeEl){
30381         Roo.EventManager.removeResizeListener(this.onResize, this);
30382         for(var i = 0, len = this.items.length; i < len; i++){
30383             this.items[i].purgeListeners();
30384         }
30385         if(removeEl === true){
30386             this.el.update("");
30387             this.el.remove();
30388         }
30389     }
30390 });
30391
30392 /**
30393  * @class Roo.TabPanelItem
30394  * @extends Roo.util.Observable
30395  * Represents an individual item (tab plus body) in a TabPanel.
30396  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30397  * @param {String} id The id of this TabPanelItem
30398  * @param {String} text The text for the tab of this TabPanelItem
30399  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30400  */
30401 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30402     /**
30403      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30404      * @type Roo.TabPanel
30405      */
30406     this.tabPanel = tabPanel;
30407     /**
30408      * The id for this TabPanelItem
30409      * @type String
30410      */
30411     this.id = id;
30412     /** @private */
30413     this.disabled = false;
30414     /** @private */
30415     this.text = text;
30416     /** @private */
30417     this.loaded = false;
30418     this.closable = closable;
30419
30420     /**
30421      * The body element for this TabPanelItem.
30422      * @type Roo.Element
30423      */
30424     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30425     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30426     this.bodyEl.setStyle("display", "block");
30427     this.bodyEl.setStyle("zoom", "1");
30428     this.hideAction();
30429
30430     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30431     /** @private */
30432     this.el = Roo.get(els.el, true);
30433     this.inner = Roo.get(els.inner, true);
30434     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30435     this.pnode = Roo.get(els.el.parentNode, true);
30436     this.el.on("mousedown", this.onTabMouseDown, this);
30437     this.el.on("click", this.onTabClick, this);
30438     /** @private */
30439     if(closable){
30440         var c = Roo.get(els.close, true);
30441         c.dom.title = this.closeText;
30442         c.addClassOnOver("close-over");
30443         c.on("click", this.closeClick, this);
30444      }
30445
30446     this.addEvents({
30447          /**
30448          * @event activate
30449          * Fires when this tab becomes the active tab.
30450          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30451          * @param {Roo.TabPanelItem} this
30452          */
30453         "activate": true,
30454         /**
30455          * @event beforeclose
30456          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30457          * @param {Roo.TabPanelItem} this
30458          * @param {Object} e Set cancel to true on this object to cancel the close.
30459          */
30460         "beforeclose": true,
30461         /**
30462          * @event close
30463          * Fires when this tab is closed.
30464          * @param {Roo.TabPanelItem} this
30465          */
30466          "close": true,
30467         /**
30468          * @event deactivate
30469          * Fires when this tab is no longer the active tab.
30470          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30471          * @param {Roo.TabPanelItem} this
30472          */
30473          "deactivate" : true
30474     });
30475     this.hidden = false;
30476
30477     Roo.TabPanelItem.superclass.constructor.call(this);
30478 };
30479
30480 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30481     purgeListeners : function(){
30482        Roo.util.Observable.prototype.purgeListeners.call(this);
30483        this.el.removeAllListeners();
30484     },
30485     /**
30486      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30487      */
30488     show : function(){
30489         this.pnode.addClass("on");
30490         this.showAction();
30491         if(Roo.isOpera){
30492             this.tabPanel.stripWrap.repaint();
30493         }
30494         this.fireEvent("activate", this.tabPanel, this);
30495     },
30496
30497     /**
30498      * Returns true if this tab is the active tab.
30499      * @return {Boolean}
30500      */
30501     isActive : function(){
30502         return this.tabPanel.getActiveTab() == this;
30503     },
30504
30505     /**
30506      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30507      */
30508     hide : function(){
30509         this.pnode.removeClass("on");
30510         this.hideAction();
30511         this.fireEvent("deactivate", this.tabPanel, this);
30512     },
30513
30514     hideAction : function(){
30515         this.bodyEl.hide();
30516         this.bodyEl.setStyle("position", "absolute");
30517         this.bodyEl.setLeft("-20000px");
30518         this.bodyEl.setTop("-20000px");
30519     },
30520
30521     showAction : function(){
30522         this.bodyEl.setStyle("position", "relative");
30523         this.bodyEl.setTop("");
30524         this.bodyEl.setLeft("");
30525         this.bodyEl.show();
30526     },
30527
30528     /**
30529      * Set the tooltip for the tab.
30530      * @param {String} tooltip The tab's tooltip
30531      */
30532     setTooltip : function(text){
30533         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30534             this.textEl.dom.qtip = text;
30535             this.textEl.dom.removeAttribute('title');
30536         }else{
30537             this.textEl.dom.title = text;
30538         }
30539     },
30540
30541     onTabClick : function(e){
30542         e.preventDefault();
30543         this.tabPanel.activate(this.id);
30544     },
30545
30546     onTabMouseDown : function(e){
30547         e.preventDefault();
30548         this.tabPanel.activate(this.id);
30549     },
30550
30551     getWidth : function(){
30552         return this.inner.getWidth();
30553     },
30554
30555     setWidth : function(width){
30556         var iwidth = width - this.pnode.getPadding("lr");
30557         this.inner.setWidth(iwidth);
30558         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30559         this.pnode.setWidth(width);
30560     },
30561
30562     /**
30563      * Show or hide the tab
30564      * @param {Boolean} hidden True to hide or false to show.
30565      */
30566     setHidden : function(hidden){
30567         this.hidden = hidden;
30568         this.pnode.setStyle("display", hidden ? "none" : "");
30569     },
30570
30571     /**
30572      * Returns true if this tab is "hidden"
30573      * @return {Boolean}
30574      */
30575     isHidden : function(){
30576         return this.hidden;
30577     },
30578
30579     /**
30580      * Returns the text for this tab
30581      * @return {String}
30582      */
30583     getText : function(){
30584         return this.text;
30585     },
30586
30587     autoSize : function(){
30588         //this.el.beginMeasure();
30589         this.textEl.setWidth(1);
30590         /*
30591          *  #2804 [new] Tabs in Roojs
30592          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30593          */
30594         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30595         //this.el.endMeasure();
30596     },
30597
30598     /**
30599      * Sets the text for the tab (Note: this also sets the tooltip text)
30600      * @param {String} text The tab's text and tooltip
30601      */
30602     setText : function(text){
30603         this.text = text;
30604         this.textEl.update(text);
30605         this.setTooltip(text);
30606         if(!this.tabPanel.resizeTabs){
30607             this.autoSize();
30608         }
30609     },
30610     /**
30611      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30612      */
30613     activate : function(){
30614         this.tabPanel.activate(this.id);
30615     },
30616
30617     /**
30618      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30619      */
30620     disable : function(){
30621         if(this.tabPanel.active != this){
30622             this.disabled = true;
30623             this.pnode.addClass("disabled");
30624         }
30625     },
30626
30627     /**
30628      * Enables this TabPanelItem if it was previously disabled.
30629      */
30630     enable : function(){
30631         this.disabled = false;
30632         this.pnode.removeClass("disabled");
30633     },
30634
30635     /**
30636      * Sets the content for this TabPanelItem.
30637      * @param {String} content The content
30638      * @param {Boolean} loadScripts true to look for and load scripts
30639      */
30640     setContent : function(content, loadScripts){
30641         this.bodyEl.update(content, loadScripts);
30642     },
30643
30644     /**
30645      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30646      * @return {Roo.UpdateManager} The UpdateManager
30647      */
30648     getUpdateManager : function(){
30649         return this.bodyEl.getUpdateManager();
30650     },
30651
30652     /**
30653      * Set a URL to be used to load the content for this TabPanelItem.
30654      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30655      * @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)
30656      * @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)
30657      * @return {Roo.UpdateManager} The UpdateManager
30658      */
30659     setUrl : function(url, params, loadOnce){
30660         if(this.refreshDelegate){
30661             this.un('activate', this.refreshDelegate);
30662         }
30663         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30664         this.on("activate", this.refreshDelegate);
30665         return this.bodyEl.getUpdateManager();
30666     },
30667
30668     /** @private */
30669     _handleRefresh : function(url, params, loadOnce){
30670         if(!loadOnce || !this.loaded){
30671             var updater = this.bodyEl.getUpdateManager();
30672             updater.update(url, params, this._setLoaded.createDelegate(this));
30673         }
30674     },
30675
30676     /**
30677      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30678      *   Will fail silently if the setUrl method has not been called.
30679      *   This does not activate the panel, just updates its content.
30680      */
30681     refresh : function(){
30682         if(this.refreshDelegate){
30683            this.loaded = false;
30684            this.refreshDelegate();
30685         }
30686     },
30687
30688     /** @private */
30689     _setLoaded : function(){
30690         this.loaded = true;
30691     },
30692
30693     /** @private */
30694     closeClick : function(e){
30695         var o = {};
30696         e.stopEvent();
30697         this.fireEvent("beforeclose", this, o);
30698         if(o.cancel !== true){
30699             this.tabPanel.removeTab(this.id);
30700         }
30701     },
30702     /**
30703      * The text displayed in the tooltip for the close icon.
30704      * @type String
30705      */
30706     closeText : "Close this tab"
30707 });
30708
30709 /** @private */
30710 Roo.TabPanel.prototype.createStrip = function(container){
30711     var strip = document.createElement("div");
30712     strip.className = "x-tabs-wrap";
30713     container.appendChild(strip);
30714     return strip;
30715 };
30716 /** @private */
30717 Roo.TabPanel.prototype.createStripList = function(strip){
30718     // div wrapper for retard IE
30719     // returns the "tr" element.
30720     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30721         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30722         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30723     return strip.firstChild.firstChild.firstChild.firstChild;
30724 };
30725 /** @private */
30726 Roo.TabPanel.prototype.createBody = function(container){
30727     var body = document.createElement("div");
30728     Roo.id(body, "tab-body");
30729     Roo.fly(body).addClass("x-tabs-body");
30730     container.appendChild(body);
30731     return body;
30732 };
30733 /** @private */
30734 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30735     var body = Roo.getDom(id);
30736     if(!body){
30737         body = document.createElement("div");
30738         body.id = id;
30739     }
30740     Roo.fly(body).addClass("x-tabs-item-body");
30741     bodyEl.insertBefore(body, bodyEl.firstChild);
30742     return body;
30743 };
30744 /** @private */
30745 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30746     var td = document.createElement("td");
30747     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30748     //stripEl.appendChild(td);
30749     if(closable){
30750         td.className = "x-tabs-closable";
30751         if(!this.closeTpl){
30752             this.closeTpl = new Roo.Template(
30753                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30754                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30755                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30756             );
30757         }
30758         var el = this.closeTpl.overwrite(td, {"text": text});
30759         var close = el.getElementsByTagName("div")[0];
30760         var inner = el.getElementsByTagName("em")[0];
30761         return {"el": el, "close": close, "inner": inner};
30762     } else {
30763         if(!this.tabTpl){
30764             this.tabTpl = new Roo.Template(
30765                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30766                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30767             );
30768         }
30769         var el = this.tabTpl.overwrite(td, {"text": text});
30770         var inner = el.getElementsByTagName("em")[0];
30771         return {"el": el, "inner": inner};
30772     }
30773 };/*
30774  * Based on:
30775  * Ext JS Library 1.1.1
30776  * Copyright(c) 2006-2007, Ext JS, LLC.
30777  *
30778  * Originally Released Under LGPL - original licence link has changed is not relivant.
30779  *
30780  * Fork - LGPL
30781  * <script type="text/javascript">
30782  */
30783
30784 /**
30785  * @class Roo.Button
30786  * @extends Roo.util.Observable
30787  * Simple Button class
30788  * @cfg {String} text The button text
30789  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30790  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30791  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30792  * @cfg {Object} scope The scope of the handler
30793  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30794  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30795  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30796  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30797  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30798  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30799    applies if enableToggle = true)
30800  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30801  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30802   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30803  * @constructor
30804  * Create a new button
30805  * @param {Object} config The config object
30806  */
30807 Roo.Button = function(renderTo, config)
30808 {
30809     if (!config) {
30810         config = renderTo;
30811         renderTo = config.renderTo || false;
30812     }
30813     
30814     Roo.apply(this, config);
30815     this.addEvents({
30816         /**
30817              * @event click
30818              * Fires when this button is clicked
30819              * @param {Button} this
30820              * @param {EventObject} e The click event
30821              */
30822             "click" : true,
30823         /**
30824              * @event toggle
30825              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30826              * @param {Button} this
30827              * @param {Boolean} pressed
30828              */
30829             "toggle" : true,
30830         /**
30831              * @event mouseover
30832              * Fires when the mouse hovers over the button
30833              * @param {Button} this
30834              * @param {Event} e The event object
30835              */
30836         'mouseover' : true,
30837         /**
30838              * @event mouseout
30839              * Fires when the mouse exits the button
30840              * @param {Button} this
30841              * @param {Event} e The event object
30842              */
30843         'mouseout': true,
30844          /**
30845              * @event render
30846              * Fires when the button is rendered
30847              * @param {Button} this
30848              */
30849         'render': true
30850     });
30851     if(this.menu){
30852         this.menu = Roo.menu.MenuMgr.get(this.menu);
30853     }
30854     // register listeners first!!  - so render can be captured..
30855     Roo.util.Observable.call(this);
30856     if(renderTo){
30857         this.render(renderTo);
30858     }
30859     
30860   
30861 };
30862
30863 Roo.extend(Roo.Button, Roo.util.Observable, {
30864     /**
30865      * 
30866      */
30867     
30868     /**
30869      * Read-only. True if this button is hidden
30870      * @type Boolean
30871      */
30872     hidden : false,
30873     /**
30874      * Read-only. True if this button is disabled
30875      * @type Boolean
30876      */
30877     disabled : false,
30878     /**
30879      * Read-only. True if this button is pressed (only if enableToggle = true)
30880      * @type Boolean
30881      */
30882     pressed : false,
30883
30884     /**
30885      * @cfg {Number} tabIndex 
30886      * The DOM tabIndex for this button (defaults to undefined)
30887      */
30888     tabIndex : undefined,
30889
30890     /**
30891      * @cfg {Boolean} enableToggle
30892      * True to enable pressed/not pressed toggling (defaults to false)
30893      */
30894     enableToggle: false,
30895     /**
30896      * @cfg {Roo.menu.Menu} menu
30897      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30898      */
30899     menu : undefined,
30900     /**
30901      * @cfg {String} menuAlign
30902      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30903      */
30904     menuAlign : "tl-bl?",
30905
30906     /**
30907      * @cfg {String} iconCls
30908      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30909      */
30910     iconCls : undefined,
30911     /**
30912      * @cfg {String} type
30913      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30914      */
30915     type : 'button',
30916
30917     // private
30918     menuClassTarget: 'tr',
30919
30920     /**
30921      * @cfg {String} clickEvent
30922      * The type of event to map to the button's event handler (defaults to 'click')
30923      */
30924     clickEvent : 'click',
30925
30926     /**
30927      * @cfg {Boolean} handleMouseEvents
30928      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30929      */
30930     handleMouseEvents : true,
30931
30932     /**
30933      * @cfg {String} tooltipType
30934      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30935      */
30936     tooltipType : 'qtip',
30937
30938     /**
30939      * @cfg {String} cls
30940      * A CSS class to apply to the button's main element.
30941      */
30942     
30943     /**
30944      * @cfg {Roo.Template} template (Optional)
30945      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30946      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30947      * require code modifications if required elements (e.g. a button) aren't present.
30948      */
30949
30950     // private
30951     render : function(renderTo){
30952         var btn;
30953         if(this.hideParent){
30954             this.parentEl = Roo.get(renderTo);
30955         }
30956         if(!this.dhconfig){
30957             if(!this.template){
30958                 if(!Roo.Button.buttonTemplate){
30959                     // hideous table template
30960                     Roo.Button.buttonTemplate = new Roo.Template(
30961                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30962                         '<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>',
30963                         "</tr></tbody></table>");
30964                 }
30965                 this.template = Roo.Button.buttonTemplate;
30966             }
30967             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30968             var btnEl = btn.child("button:first");
30969             btnEl.on('focus', this.onFocus, this);
30970             btnEl.on('blur', this.onBlur, this);
30971             if(this.cls){
30972                 btn.addClass(this.cls);
30973             }
30974             if(this.icon){
30975                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30976             }
30977             if(this.iconCls){
30978                 btnEl.addClass(this.iconCls);
30979                 if(!this.cls){
30980                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30981                 }
30982             }
30983             if(this.tabIndex !== undefined){
30984                 btnEl.dom.tabIndex = this.tabIndex;
30985             }
30986             if(this.tooltip){
30987                 if(typeof this.tooltip == 'object'){
30988                     Roo.QuickTips.tips(Roo.apply({
30989                           target: btnEl.id
30990                     }, this.tooltip));
30991                 } else {
30992                     btnEl.dom[this.tooltipType] = this.tooltip;
30993                 }
30994             }
30995         }else{
30996             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30997         }
30998         this.el = btn;
30999         if(this.id){
31000             this.el.dom.id = this.el.id = this.id;
31001         }
31002         if(this.menu){
31003             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
31004             this.menu.on("show", this.onMenuShow, this);
31005             this.menu.on("hide", this.onMenuHide, this);
31006         }
31007         btn.addClass("x-btn");
31008         if(Roo.isIE && !Roo.isIE7){
31009             this.autoWidth.defer(1, this);
31010         }else{
31011             this.autoWidth();
31012         }
31013         if(this.handleMouseEvents){
31014             btn.on("mouseover", this.onMouseOver, this);
31015             btn.on("mouseout", this.onMouseOut, this);
31016             btn.on("mousedown", this.onMouseDown, this);
31017         }
31018         btn.on(this.clickEvent, this.onClick, this);
31019         //btn.on("mouseup", this.onMouseUp, this);
31020         if(this.hidden){
31021             this.hide();
31022         }
31023         if(this.disabled){
31024             this.disable();
31025         }
31026         Roo.ButtonToggleMgr.register(this);
31027         if(this.pressed){
31028             this.el.addClass("x-btn-pressed");
31029         }
31030         if(this.repeat){
31031             var repeater = new Roo.util.ClickRepeater(btn,
31032                 typeof this.repeat == "object" ? this.repeat : {}
31033             );
31034             repeater.on("click", this.onClick,  this);
31035         }
31036         
31037         this.fireEvent('render', this);
31038         
31039     },
31040     /**
31041      * Returns the button's underlying element
31042      * @return {Roo.Element} The element
31043      */
31044     getEl : function(){
31045         return this.el;  
31046     },
31047     
31048     /**
31049      * Destroys this Button and removes any listeners.
31050      */
31051     destroy : function(){
31052         Roo.ButtonToggleMgr.unregister(this);
31053         this.el.removeAllListeners();
31054         this.purgeListeners();
31055         this.el.remove();
31056     },
31057
31058     // private
31059     autoWidth : function(){
31060         if(this.el){
31061             this.el.setWidth("auto");
31062             if(Roo.isIE7 && Roo.isStrict){
31063                 var ib = this.el.child('button');
31064                 if(ib && ib.getWidth() > 20){
31065                     ib.clip();
31066                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31067                 }
31068             }
31069             if(this.minWidth){
31070                 if(this.hidden){
31071                     this.el.beginMeasure();
31072                 }
31073                 if(this.el.getWidth() < this.minWidth){
31074                     this.el.setWidth(this.minWidth);
31075                 }
31076                 if(this.hidden){
31077                     this.el.endMeasure();
31078                 }
31079             }
31080         }
31081     },
31082
31083     /**
31084      * Assigns this button's click handler
31085      * @param {Function} handler The function to call when the button is clicked
31086      * @param {Object} scope (optional) Scope for the function passed in
31087      */
31088     setHandler : function(handler, scope){
31089         this.handler = handler;
31090         this.scope = scope;  
31091     },
31092     
31093     /**
31094      * Sets this button's text
31095      * @param {String} text The button text
31096      */
31097     setText : function(text){
31098         this.text = text;
31099         if(this.el){
31100             this.el.child("td.x-btn-center button.x-btn-text").update(text);
31101         }
31102         this.autoWidth();
31103     },
31104     
31105     /**
31106      * Gets the text for this button
31107      * @return {String} The button text
31108      */
31109     getText : function(){
31110         return this.text;  
31111     },
31112     
31113     /**
31114      * Show this button
31115      */
31116     show: function(){
31117         this.hidden = false;
31118         if(this.el){
31119             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
31120         }
31121     },
31122     
31123     /**
31124      * Hide this button
31125      */
31126     hide: function(){
31127         this.hidden = true;
31128         if(this.el){
31129             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
31130         }
31131     },
31132     
31133     /**
31134      * Convenience function for boolean show/hide
31135      * @param {Boolean} visible True to show, false to hide
31136      */
31137     setVisible: function(visible){
31138         if(visible) {
31139             this.show();
31140         }else{
31141             this.hide();
31142         }
31143     },
31144     /**
31145          * Similar to toggle, but does not trigger event.
31146          * @param {Boolean} state [required] Force a particular state
31147          */
31148         setPressed : function(state)
31149         {
31150             if(state != this.pressed){
31151             if(state){
31152                 this.el.addClass("x-btn-pressed");
31153                 this.pressed = true;
31154             }else{
31155                 this.el.removeClass("x-btn-pressed");
31156                 this.pressed = false;
31157             }
31158         }
31159         },
31160         
31161     /**
31162      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
31163      * @param {Boolean} state (optional) Force a particular state
31164      */
31165     toggle : function(state){
31166         state = state === undefined ? !this.pressed : state;
31167         if(state != this.pressed){
31168             if(state){
31169                 this.el.addClass("x-btn-pressed");
31170                 this.pressed = true;
31171                 this.fireEvent("toggle", this, true);
31172             }else{
31173                 this.el.removeClass("x-btn-pressed");
31174                 this.pressed = false;
31175                 this.fireEvent("toggle", this, false);
31176             }
31177             if(this.toggleHandler){
31178                 this.toggleHandler.call(this.scope || this, this, state);
31179             }
31180         }
31181     },
31182     
31183         
31184         
31185     /**
31186      * Focus the button
31187      */
31188     focus : function(){
31189         this.el.child('button:first').focus();
31190     },
31191     
31192     /**
31193      * Disable this button
31194      */
31195     disable : function(){
31196         if(this.el){
31197             this.el.addClass("x-btn-disabled");
31198         }
31199         this.disabled = true;
31200     },
31201     
31202     /**
31203      * Enable this button
31204      */
31205     enable : function(){
31206         if(this.el){
31207             this.el.removeClass("x-btn-disabled");
31208         }
31209         this.disabled = false;
31210     },
31211
31212     /**
31213      * Convenience function for boolean enable/disable
31214      * @param {Boolean} enabled True to enable, false to disable
31215      */
31216     setDisabled : function(v){
31217         this[v !== true ? "enable" : "disable"]();
31218     },
31219
31220     // private
31221     onClick : function(e)
31222     {
31223         if(e){
31224             e.preventDefault();
31225         }
31226         if(e.button != 0){
31227             return;
31228         }
31229         if(!this.disabled){
31230             if(this.enableToggle){
31231                 this.toggle();
31232             }
31233             if(this.menu && !this.menu.isVisible()){
31234                 this.menu.show(this.el, this.menuAlign);
31235             }
31236             this.fireEvent("click", this, e);
31237             if(this.handler){
31238                 this.el.removeClass("x-btn-over");
31239                 this.handler.call(this.scope || this, this, e);
31240             }
31241         }
31242     },
31243     // private
31244     onMouseOver : function(e){
31245         if(!this.disabled){
31246             this.el.addClass("x-btn-over");
31247             this.fireEvent('mouseover', this, e);
31248         }
31249     },
31250     // private
31251     onMouseOut : function(e){
31252         if(!e.within(this.el,  true)){
31253             this.el.removeClass("x-btn-over");
31254             this.fireEvent('mouseout', this, e);
31255         }
31256     },
31257     // private
31258     onFocus : function(e){
31259         if(!this.disabled){
31260             this.el.addClass("x-btn-focus");
31261         }
31262     },
31263     // private
31264     onBlur : function(e){
31265         this.el.removeClass("x-btn-focus");
31266     },
31267     // private
31268     onMouseDown : function(e){
31269         if(!this.disabled && e.button == 0){
31270             this.el.addClass("x-btn-click");
31271             Roo.get(document).on('mouseup', this.onMouseUp, this);
31272         }
31273     },
31274     // private
31275     onMouseUp : function(e){
31276         if(e.button == 0){
31277             this.el.removeClass("x-btn-click");
31278             Roo.get(document).un('mouseup', this.onMouseUp, this);
31279         }
31280     },
31281     // private
31282     onMenuShow : function(e){
31283         this.el.addClass("x-btn-menu-active");
31284     },
31285     // private
31286     onMenuHide : function(e){
31287         this.el.removeClass("x-btn-menu-active");
31288     }   
31289 });
31290
31291 // Private utility class used by Button
31292 Roo.ButtonToggleMgr = function(){
31293    var groups = {};
31294    
31295    function toggleGroup(btn, state){
31296        if(state){
31297            var g = groups[btn.toggleGroup];
31298            for(var i = 0, l = g.length; i < l; i++){
31299                if(g[i] != btn){
31300                    g[i].toggle(false);
31301                }
31302            }
31303        }
31304    }
31305    
31306    return {
31307        register : function(btn){
31308            if(!btn.toggleGroup){
31309                return;
31310            }
31311            var g = groups[btn.toggleGroup];
31312            if(!g){
31313                g = groups[btn.toggleGroup] = [];
31314            }
31315            g.push(btn);
31316            btn.on("toggle", toggleGroup);
31317        },
31318        
31319        unregister : function(btn){
31320            if(!btn.toggleGroup){
31321                return;
31322            }
31323            var g = groups[btn.toggleGroup];
31324            if(g){
31325                g.remove(btn);
31326                btn.un("toggle", toggleGroup);
31327            }
31328        }
31329    };
31330 }();/*
31331  * Based on:
31332  * Ext JS Library 1.1.1
31333  * Copyright(c) 2006-2007, Ext JS, LLC.
31334  *
31335  * Originally Released Under LGPL - original licence link has changed is not relivant.
31336  *
31337  * Fork - LGPL
31338  * <script type="text/javascript">
31339  */
31340  
31341 /**
31342  * @class Roo.SplitButton
31343  * @extends Roo.Button
31344  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31345  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31346  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31347  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31348  * @cfg {String} arrowTooltip The title attribute of the arrow
31349  * @constructor
31350  * Create a new menu button
31351  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31352  * @param {Object} config The config object
31353  */
31354 Roo.SplitButton = function(renderTo, config){
31355     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31356     /**
31357      * @event arrowclick
31358      * Fires when this button's arrow is clicked
31359      * @param {SplitButton} this
31360      * @param {EventObject} e The click event
31361      */
31362     this.addEvents({"arrowclick":true});
31363 };
31364
31365 Roo.extend(Roo.SplitButton, Roo.Button, {
31366     render : function(renderTo){
31367         // this is one sweet looking template!
31368         var tpl = new Roo.Template(
31369             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31370             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31371             '<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>',
31372             "</tbody></table></td><td>",
31373             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31374             '<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>',
31375             "</tbody></table></td></tr></table>"
31376         );
31377         var btn = tpl.append(renderTo, [this.text, this.type], true);
31378         var btnEl = btn.child("button");
31379         if(this.cls){
31380             btn.addClass(this.cls);
31381         }
31382         if(this.icon){
31383             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31384         }
31385         if(this.iconCls){
31386             btnEl.addClass(this.iconCls);
31387             if(!this.cls){
31388                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31389             }
31390         }
31391         this.el = btn;
31392         if(this.handleMouseEvents){
31393             btn.on("mouseover", this.onMouseOver, this);
31394             btn.on("mouseout", this.onMouseOut, this);
31395             btn.on("mousedown", this.onMouseDown, this);
31396             btn.on("mouseup", this.onMouseUp, this);
31397         }
31398         btn.on(this.clickEvent, this.onClick, this);
31399         if(this.tooltip){
31400             if(typeof this.tooltip == 'object'){
31401                 Roo.QuickTips.tips(Roo.apply({
31402                       target: btnEl.id
31403                 }, this.tooltip));
31404             } else {
31405                 btnEl.dom[this.tooltipType] = this.tooltip;
31406             }
31407         }
31408         if(this.arrowTooltip){
31409             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31410         }
31411         if(this.hidden){
31412             this.hide();
31413         }
31414         if(this.disabled){
31415             this.disable();
31416         }
31417         if(this.pressed){
31418             this.el.addClass("x-btn-pressed");
31419         }
31420         if(Roo.isIE && !Roo.isIE7){
31421             this.autoWidth.defer(1, this);
31422         }else{
31423             this.autoWidth();
31424         }
31425         if(this.menu){
31426             this.menu.on("show", this.onMenuShow, this);
31427             this.menu.on("hide", this.onMenuHide, this);
31428         }
31429         this.fireEvent('render', this);
31430     },
31431
31432     // private
31433     autoWidth : function(){
31434         if(this.el){
31435             var tbl = this.el.child("table:first");
31436             var tbl2 = this.el.child("table:last");
31437             this.el.setWidth("auto");
31438             tbl.setWidth("auto");
31439             if(Roo.isIE7 && Roo.isStrict){
31440                 var ib = this.el.child('button:first');
31441                 if(ib && ib.getWidth() > 20){
31442                     ib.clip();
31443                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31444                 }
31445             }
31446             if(this.minWidth){
31447                 if(this.hidden){
31448                     this.el.beginMeasure();
31449                 }
31450                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31451                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31452                 }
31453                 if(this.hidden){
31454                     this.el.endMeasure();
31455                 }
31456             }
31457             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31458         } 
31459     },
31460     /**
31461      * Sets this button's click handler
31462      * @param {Function} handler The function to call when the button is clicked
31463      * @param {Object} scope (optional) Scope for the function passed above
31464      */
31465     setHandler : function(handler, scope){
31466         this.handler = handler;
31467         this.scope = scope;  
31468     },
31469     
31470     /**
31471      * Sets this button's arrow click handler
31472      * @param {Function} handler The function to call when the arrow is clicked
31473      * @param {Object} scope (optional) Scope for the function passed above
31474      */
31475     setArrowHandler : function(handler, scope){
31476         this.arrowHandler = handler;
31477         this.scope = scope;  
31478     },
31479     
31480     /**
31481      * Focus the button
31482      */
31483     focus : function(){
31484         if(this.el){
31485             this.el.child("button:first").focus();
31486         }
31487     },
31488
31489     // private
31490     onClick : function(e){
31491         e.preventDefault();
31492         if(!this.disabled){
31493             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31494                 if(this.menu && !this.menu.isVisible()){
31495                     this.menu.show(this.el, this.menuAlign);
31496                 }
31497                 this.fireEvent("arrowclick", this, e);
31498                 if(this.arrowHandler){
31499                     this.arrowHandler.call(this.scope || this, this, e);
31500                 }
31501             }else{
31502                 this.fireEvent("click", this, e);
31503                 if(this.handler){
31504                     this.handler.call(this.scope || this, this, e);
31505                 }
31506             }
31507         }
31508     },
31509     // private
31510     onMouseDown : function(e){
31511         if(!this.disabled){
31512             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31513         }
31514     },
31515     // private
31516     onMouseUp : function(e){
31517         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31518     }   
31519 });
31520
31521
31522 // backwards compat
31523 Roo.MenuButton = Roo.SplitButton;/*
31524  * Based on:
31525  * Ext JS Library 1.1.1
31526  * Copyright(c) 2006-2007, Ext JS, LLC.
31527  *
31528  * Originally Released Under LGPL - original licence link has changed is not relivant.
31529  *
31530  * Fork - LGPL
31531  * <script type="text/javascript">
31532  */
31533
31534 /**
31535  * @class Roo.Toolbar
31536  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31537  * Basic Toolbar class.
31538  * @constructor
31539  * Creates a new Toolbar
31540  * @param {Object} container The config object
31541  */ 
31542 Roo.Toolbar = function(container, buttons, config)
31543 {
31544     /// old consturctor format still supported..
31545     if(container instanceof Array){ // omit the container for later rendering
31546         buttons = container;
31547         config = buttons;
31548         container = null;
31549     }
31550     if (typeof(container) == 'object' && container.xtype) {
31551         config = container;
31552         container = config.container;
31553         buttons = config.buttons || []; // not really - use items!!
31554     }
31555     var xitems = [];
31556     if (config && config.items) {
31557         xitems = config.items;
31558         delete config.items;
31559     }
31560     Roo.apply(this, config);
31561     this.buttons = buttons;
31562     
31563     if(container){
31564         this.render(container);
31565     }
31566     this.xitems = xitems;
31567     Roo.each(xitems, function(b) {
31568         this.add(b);
31569     }, this);
31570     
31571 };
31572
31573 Roo.Toolbar.prototype = {
31574     /**
31575      * @cfg {Array} items
31576      * array of button configs or elements to add (will be converted to a MixedCollection)
31577      */
31578     items: false,
31579     /**
31580      * @cfg {String/HTMLElement/Element} container
31581      * The id or element that will contain the toolbar
31582      */
31583     // private
31584     render : function(ct){
31585         this.el = Roo.get(ct);
31586         if(this.cls){
31587             this.el.addClass(this.cls);
31588         }
31589         // using a table allows for vertical alignment
31590         // 100% width is needed by Safari...
31591         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31592         this.tr = this.el.child("tr", true);
31593         var autoId = 0;
31594         this.items = new Roo.util.MixedCollection(false, function(o){
31595             return o.id || ("item" + (++autoId));
31596         });
31597         if(this.buttons){
31598             this.add.apply(this, this.buttons);
31599             delete this.buttons;
31600         }
31601     },
31602
31603     /**
31604      * Adds element(s) to the toolbar -- this function takes a variable number of 
31605      * arguments of mixed type and adds them to the toolbar.
31606      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31607      * <ul>
31608      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31609      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31610      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31611      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31612      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31613      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31614      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31615      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31616      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31617      * </ul>
31618      * @param {Mixed} arg2
31619      * @param {Mixed} etc.
31620      */
31621     add : function(){
31622         var a = arguments, l = a.length;
31623         for(var i = 0; i < l; i++){
31624             this._add(a[i]);
31625         }
31626     },
31627     // private..
31628     _add : function(el) {
31629         
31630         if (el.xtype) {
31631             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31632         }
31633         
31634         if (el.applyTo){ // some kind of form field
31635             return this.addField(el);
31636         } 
31637         if (el.render){ // some kind of Toolbar.Item
31638             return this.addItem(el);
31639         }
31640         if (typeof el == "string"){ // string
31641             if(el == "separator" || el == "-"){
31642                 return this.addSeparator();
31643             }
31644             if (el == " "){
31645                 return this.addSpacer();
31646             }
31647             if(el == "->"){
31648                 return this.addFill();
31649             }
31650             return this.addText(el);
31651             
31652         }
31653         if(el.tagName){ // element
31654             return this.addElement(el);
31655         }
31656         if(typeof el == "object"){ // must be button config?
31657             return this.addButton(el);
31658         }
31659         // and now what?!?!
31660         return false;
31661         
31662     },
31663     
31664     /**
31665      * Add an Xtype element
31666      * @param {Object} xtype Xtype Object
31667      * @return {Object} created Object
31668      */
31669     addxtype : function(e){
31670         return this.add(e);  
31671     },
31672     
31673     /**
31674      * Returns the Element for this toolbar.
31675      * @return {Roo.Element}
31676      */
31677     getEl : function(){
31678         return this.el;  
31679     },
31680     
31681     /**
31682      * Adds a separator
31683      * @return {Roo.Toolbar.Item} The separator item
31684      */
31685     addSeparator : function(){
31686         return this.addItem(new Roo.Toolbar.Separator());
31687     },
31688
31689     /**
31690      * Adds a spacer element
31691      * @return {Roo.Toolbar.Spacer} The spacer item
31692      */
31693     addSpacer : function(){
31694         return this.addItem(new Roo.Toolbar.Spacer());
31695     },
31696
31697     /**
31698      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31699      * @return {Roo.Toolbar.Fill} The fill item
31700      */
31701     addFill : function(){
31702         return this.addItem(new Roo.Toolbar.Fill());
31703     },
31704
31705     /**
31706      * Adds any standard HTML element to the toolbar
31707      * @param {String/HTMLElement/Element} el The element or id of the element to add
31708      * @return {Roo.Toolbar.Item} The element's item
31709      */
31710     addElement : function(el){
31711         return this.addItem(new Roo.Toolbar.Item(el));
31712     },
31713     /**
31714      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31715      * @type Roo.util.MixedCollection  
31716      */
31717     items : false,
31718      
31719     /**
31720      * Adds any Toolbar.Item or subclass
31721      * @param {Roo.Toolbar.Item} item
31722      * @return {Roo.Toolbar.Item} The item
31723      */
31724     addItem : function(item){
31725         var td = this.nextBlock();
31726         item.render(td);
31727         this.items.add(item);
31728         return item;
31729     },
31730     
31731     /**
31732      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31733      * @param {Object/Array} config A button config or array of configs
31734      * @return {Roo.Toolbar.Button/Array}
31735      */
31736     addButton : function(config){
31737         if(config instanceof Array){
31738             var buttons = [];
31739             for(var i = 0, len = config.length; i < len; i++) {
31740                 buttons.push(this.addButton(config[i]));
31741             }
31742             return buttons;
31743         }
31744         var b = config;
31745         if(!(config instanceof Roo.Toolbar.Button)){
31746             b = config.split ?
31747                 new Roo.Toolbar.SplitButton(config) :
31748                 new Roo.Toolbar.Button(config);
31749         }
31750         var td = this.nextBlock();
31751         b.render(td);
31752         this.items.add(b);
31753         return b;
31754     },
31755     
31756     /**
31757      * Adds text to the toolbar
31758      * @param {String} text The text to add
31759      * @return {Roo.Toolbar.Item} The element's item
31760      */
31761     addText : function(text){
31762         return this.addItem(new Roo.Toolbar.TextItem(text));
31763     },
31764     
31765     /**
31766      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31767      * @param {Number} index The index where the item is to be inserted
31768      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31769      * @return {Roo.Toolbar.Button/Item}
31770      */
31771     insertButton : function(index, item){
31772         if(item instanceof Array){
31773             var buttons = [];
31774             for(var i = 0, len = item.length; i < len; i++) {
31775                buttons.push(this.insertButton(index + i, item[i]));
31776             }
31777             return buttons;
31778         }
31779         if (!(item instanceof Roo.Toolbar.Button)){
31780            item = new Roo.Toolbar.Button(item);
31781         }
31782         var td = document.createElement("td");
31783         this.tr.insertBefore(td, this.tr.childNodes[index]);
31784         item.render(td);
31785         this.items.insert(index, item);
31786         return item;
31787     },
31788     
31789     /**
31790      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31791      * @param {Object} config
31792      * @return {Roo.Toolbar.Item} The element's item
31793      */
31794     addDom : function(config, returnEl){
31795         var td = this.nextBlock();
31796         Roo.DomHelper.overwrite(td, config);
31797         var ti = new Roo.Toolbar.Item(td.firstChild);
31798         ti.render(td);
31799         this.items.add(ti);
31800         return ti;
31801     },
31802
31803     /**
31804      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31805      * @type Roo.util.MixedCollection  
31806      */
31807     fields : false,
31808     
31809     /**
31810      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31811      * Note: the field should not have been rendered yet. For a field that has already been
31812      * rendered, use {@link #addElement}.
31813      * @param {Roo.form.Field} field
31814      * @return {Roo.ToolbarItem}
31815      */
31816      
31817       
31818     addField : function(field) {
31819         if (!this.fields) {
31820             var autoId = 0;
31821             this.fields = new Roo.util.MixedCollection(false, function(o){
31822                 return o.id || ("item" + (++autoId));
31823             });
31824
31825         }
31826         
31827         var td = this.nextBlock();
31828         field.render(td);
31829         var ti = new Roo.Toolbar.Item(td.firstChild);
31830         ti.render(td);
31831         this.items.add(ti);
31832         this.fields.add(field);
31833         return ti;
31834     },
31835     /**
31836      * Hide the toolbar
31837      * @method hide
31838      */
31839      
31840       
31841     hide : function()
31842     {
31843         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31844         this.el.child('div').hide();
31845     },
31846     /**
31847      * Show the toolbar
31848      * @method show
31849      */
31850     show : function()
31851     {
31852         this.el.child('div').show();
31853     },
31854       
31855     // private
31856     nextBlock : function(){
31857         var td = document.createElement("td");
31858         this.tr.appendChild(td);
31859         return td;
31860     },
31861
31862     // private
31863     destroy : function(){
31864         if(this.items){ // rendered?
31865             Roo.destroy.apply(Roo, this.items.items);
31866         }
31867         if(this.fields){ // rendered?
31868             Roo.destroy.apply(Roo, this.fields.items);
31869         }
31870         Roo.Element.uncache(this.el, this.tr);
31871     }
31872 };
31873
31874 /**
31875  * @class Roo.Toolbar.Item
31876  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31877  * @constructor
31878  * Creates a new Item
31879  * @param {HTMLElement} el 
31880  */
31881 Roo.Toolbar.Item = function(el){
31882     var cfg = {};
31883     if (typeof (el.xtype) != 'undefined') {
31884         cfg = el;
31885         el = cfg.el;
31886     }
31887     
31888     this.el = Roo.getDom(el);
31889     this.id = Roo.id(this.el);
31890     this.hidden = false;
31891     
31892     this.addEvents({
31893          /**
31894              * @event render
31895              * Fires when the button is rendered
31896              * @param {Button} this
31897              */
31898         'render': true
31899     });
31900     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31901 };
31902 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31903 //Roo.Toolbar.Item.prototype = {
31904     
31905     /**
31906      * Get this item's HTML Element
31907      * @return {HTMLElement}
31908      */
31909     getEl : function(){
31910        return this.el;  
31911     },
31912
31913     // private
31914     render : function(td){
31915         
31916          this.td = td;
31917         td.appendChild(this.el);
31918         
31919         this.fireEvent('render', this);
31920     },
31921     
31922     /**
31923      * Removes and destroys this item.
31924      */
31925     destroy : function(){
31926         this.td.parentNode.removeChild(this.td);
31927     },
31928     
31929     /**
31930      * Shows this item.
31931      */
31932     show: function(){
31933         this.hidden = false;
31934         this.td.style.display = "";
31935     },
31936     
31937     /**
31938      * Hides this item.
31939      */
31940     hide: function(){
31941         this.hidden = true;
31942         this.td.style.display = "none";
31943     },
31944     
31945     /**
31946      * Convenience function for boolean show/hide.
31947      * @param {Boolean} visible true to show/false to hide
31948      */
31949     setVisible: function(visible){
31950         if(visible) {
31951             this.show();
31952         }else{
31953             this.hide();
31954         }
31955     },
31956     
31957     /**
31958      * Try to focus this item.
31959      */
31960     focus : function(){
31961         Roo.fly(this.el).focus();
31962     },
31963     
31964     /**
31965      * Disables this item.
31966      */
31967     disable : function(){
31968         Roo.fly(this.td).addClass("x-item-disabled");
31969         this.disabled = true;
31970         this.el.disabled = true;
31971     },
31972     
31973     /**
31974      * Enables this item.
31975      */
31976     enable : function(){
31977         Roo.fly(this.td).removeClass("x-item-disabled");
31978         this.disabled = false;
31979         this.el.disabled = false;
31980     }
31981 });
31982
31983
31984 /**
31985  * @class Roo.Toolbar.Separator
31986  * @extends Roo.Toolbar.Item
31987  * A simple toolbar separator class
31988  * @constructor
31989  * Creates a new Separator
31990  */
31991 Roo.Toolbar.Separator = function(cfg){
31992     
31993     var s = document.createElement("span");
31994     s.className = "ytb-sep";
31995     if (cfg) {
31996         cfg.el = s;
31997     }
31998     
31999     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
32000 };
32001 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
32002     enable:Roo.emptyFn,
32003     disable:Roo.emptyFn,
32004     focus:Roo.emptyFn
32005 });
32006
32007 /**
32008  * @class Roo.Toolbar.Spacer
32009  * @extends Roo.Toolbar.Item
32010  * A simple element that adds extra horizontal space to a toolbar.
32011  * @constructor
32012  * Creates a new Spacer
32013  */
32014 Roo.Toolbar.Spacer = function(cfg){
32015     var s = document.createElement("div");
32016     s.className = "ytb-spacer";
32017     if (cfg) {
32018         cfg.el = s;
32019     }
32020     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
32021 };
32022 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
32023     enable:Roo.emptyFn,
32024     disable:Roo.emptyFn,
32025     focus:Roo.emptyFn
32026 });
32027
32028 /**
32029  * @class Roo.Toolbar.Fill
32030  * @extends Roo.Toolbar.Spacer
32031  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
32032  * @constructor
32033  * Creates a new Spacer
32034  */
32035 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
32036     // private
32037     render : function(td){
32038         td.style.width = '100%';
32039         Roo.Toolbar.Fill.superclass.render.call(this, td);
32040     }
32041 });
32042
32043 /**
32044  * @class Roo.Toolbar.TextItem
32045  * @extends Roo.Toolbar.Item
32046  * A simple class that renders text directly into a toolbar.
32047  * @constructor
32048  * Creates a new TextItem
32049  * @cfg {string} text 
32050  */
32051 Roo.Toolbar.TextItem = function(cfg){
32052     var  text = cfg || "";
32053     if (typeof(cfg) == 'object') {
32054         text = cfg.text || "";
32055     }  else {
32056         cfg = null;
32057     }
32058     var s = document.createElement("span");
32059     s.className = "ytb-text";
32060     s.innerHTML = text;
32061     if (cfg) {
32062         cfg.el  = s;
32063     }
32064     
32065     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
32066 };
32067 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
32068     
32069      
32070     enable:Roo.emptyFn,
32071     disable:Roo.emptyFn,
32072     focus:Roo.emptyFn,
32073      /**
32074      * Shows this button
32075      */
32076     show: function(){
32077         this.hidden = false;
32078         this.el.style.display = "";
32079     },
32080     
32081     /**
32082      * Hides this button
32083      */
32084     hide: function(){
32085         this.hidden = true;
32086         this.el.style.display = "none";
32087     }
32088     
32089 });
32090
32091 /**
32092  * @class Roo.Toolbar.Button
32093  * @extends Roo.Button
32094  * A button that renders into a toolbar.
32095  * @constructor
32096  * Creates a new Button
32097  * @param {Object} config A standard {@link Roo.Button} config object
32098  */
32099 Roo.Toolbar.Button = function(config){
32100     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
32101 };
32102 Roo.extend(Roo.Toolbar.Button, Roo.Button,
32103 {
32104     
32105     
32106     render : function(td){
32107         this.td = td;
32108         Roo.Toolbar.Button.superclass.render.call(this, td);
32109     },
32110     
32111     /**
32112      * Removes and destroys this button
32113      */
32114     destroy : function(){
32115         Roo.Toolbar.Button.superclass.destroy.call(this);
32116         this.td.parentNode.removeChild(this.td);
32117     },
32118     
32119     /**
32120      * Shows this button
32121      */
32122     show: function(){
32123         this.hidden = false;
32124         this.td.style.display = "";
32125     },
32126     
32127     /**
32128      * Hides this button
32129      */
32130     hide: function(){
32131         this.hidden = true;
32132         this.td.style.display = "none";
32133     },
32134
32135     /**
32136      * Disables this item
32137      */
32138     disable : function(){
32139         Roo.fly(this.td).addClass("x-item-disabled");
32140         this.disabled = true;
32141     },
32142
32143     /**
32144      * Enables this item
32145      */
32146     enable : function(){
32147         Roo.fly(this.td).removeClass("x-item-disabled");
32148         this.disabled = false;
32149     }
32150 });
32151 // backwards compat
32152 Roo.ToolbarButton = Roo.Toolbar.Button;
32153
32154 /**
32155  * @class Roo.Toolbar.SplitButton
32156  * @extends Roo.SplitButton
32157  * A menu button that renders into a toolbar.
32158  * @constructor
32159  * Creates a new SplitButton
32160  * @param {Object} config A standard {@link Roo.SplitButton} config object
32161  */
32162 Roo.Toolbar.SplitButton = function(config){
32163     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
32164 };
32165 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
32166     render : function(td){
32167         this.td = td;
32168         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
32169     },
32170     
32171     /**
32172      * Removes and destroys this button
32173      */
32174     destroy : function(){
32175         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
32176         this.td.parentNode.removeChild(this.td);
32177     },
32178     
32179     /**
32180      * Shows this button
32181      */
32182     show: function(){
32183         this.hidden = false;
32184         this.td.style.display = "";
32185     },
32186     
32187     /**
32188      * Hides this button
32189      */
32190     hide: function(){
32191         this.hidden = true;
32192         this.td.style.display = "none";
32193     }
32194 });
32195
32196 // backwards compat
32197 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
32198  * Based on:
32199  * Ext JS Library 1.1.1
32200  * Copyright(c) 2006-2007, Ext JS, LLC.
32201  *
32202  * Originally Released Under LGPL - original licence link has changed is not relivant.
32203  *
32204  * Fork - LGPL
32205  * <script type="text/javascript">
32206  */
32207  
32208 /**
32209  * @class Roo.PagingToolbar
32210  * @extends Roo.Toolbar
32211  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
32212  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32213  * @constructor
32214  * Create a new PagingToolbar
32215  * @param {Object} config The config object
32216  */
32217 Roo.PagingToolbar = function(el, ds, config)
32218 {
32219     // old args format still supported... - xtype is prefered..
32220     if (typeof(el) == 'object' && el.xtype) {
32221         // created from xtype...
32222         config = el;
32223         ds = el.dataSource;
32224         el = config.container;
32225     }
32226     var items = [];
32227     if (config.items) {
32228         items = config.items;
32229         config.items = [];
32230     }
32231     
32232     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32233     this.ds = ds;
32234     this.cursor = 0;
32235     this.renderButtons(this.el);
32236     this.bind(ds);
32237     
32238     // supprot items array.
32239    
32240     Roo.each(items, function(e) {
32241         this.add(Roo.factory(e));
32242     },this);
32243     
32244 };
32245
32246 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32247    
32248     /**
32249      * @cfg {String/HTMLElement/Element} container
32250      * container The id or element that will contain the toolbar
32251      */
32252     /**
32253      * @cfg {Boolean} displayInfo
32254      * True to display the displayMsg (defaults to false)
32255      */
32256     
32257     
32258     /**
32259      * @cfg {Number} pageSize
32260      * The number of records to display per page (defaults to 20)
32261      */
32262     pageSize: 20,
32263     /**
32264      * @cfg {String} displayMsg
32265      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32266      */
32267     displayMsg : 'Displaying {0} - {1} of {2}',
32268     /**
32269      * @cfg {String} emptyMsg
32270      * The message to display when no records are found (defaults to "No data to display")
32271      */
32272     emptyMsg : 'No data to display',
32273     /**
32274      * Customizable piece of the default paging text (defaults to "Page")
32275      * @type String
32276      */
32277     beforePageText : "Page",
32278     /**
32279      * Customizable piece of the default paging text (defaults to "of %0")
32280      * @type String
32281      */
32282     afterPageText : "of {0}",
32283     /**
32284      * Customizable piece of the default paging text (defaults to "First Page")
32285      * @type String
32286      */
32287     firstText : "First Page",
32288     /**
32289      * Customizable piece of the default paging text (defaults to "Previous Page")
32290      * @type String
32291      */
32292     prevText : "Previous Page",
32293     /**
32294      * Customizable piece of the default paging text (defaults to "Next Page")
32295      * @type String
32296      */
32297     nextText : "Next Page",
32298     /**
32299      * Customizable piece of the default paging text (defaults to "Last Page")
32300      * @type String
32301      */
32302     lastText : "Last Page",
32303     /**
32304      * Customizable piece of the default paging text (defaults to "Refresh")
32305      * @type String
32306      */
32307     refreshText : "Refresh",
32308
32309     // private
32310     renderButtons : function(el){
32311         Roo.PagingToolbar.superclass.render.call(this, el);
32312         this.first = this.addButton({
32313             tooltip: this.firstText,
32314             cls: "x-btn-icon x-grid-page-first",
32315             disabled: true,
32316             handler: this.onClick.createDelegate(this, ["first"])
32317         });
32318         this.prev = this.addButton({
32319             tooltip: this.prevText,
32320             cls: "x-btn-icon x-grid-page-prev",
32321             disabled: true,
32322             handler: this.onClick.createDelegate(this, ["prev"])
32323         });
32324         //this.addSeparator();
32325         this.add(this.beforePageText);
32326         this.field = Roo.get(this.addDom({
32327            tag: "input",
32328            type: "text",
32329            size: "3",
32330            value: "1",
32331            cls: "x-grid-page-number"
32332         }).el);
32333         this.field.on("keydown", this.onPagingKeydown, this);
32334         this.field.on("focus", function(){this.dom.select();});
32335         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32336         this.field.setHeight(18);
32337         //this.addSeparator();
32338         this.next = this.addButton({
32339             tooltip: this.nextText,
32340             cls: "x-btn-icon x-grid-page-next",
32341             disabled: true,
32342             handler: this.onClick.createDelegate(this, ["next"])
32343         });
32344         this.last = this.addButton({
32345             tooltip: this.lastText,
32346             cls: "x-btn-icon x-grid-page-last",
32347             disabled: true,
32348             handler: this.onClick.createDelegate(this, ["last"])
32349         });
32350         //this.addSeparator();
32351         this.loading = this.addButton({
32352             tooltip: this.refreshText,
32353             cls: "x-btn-icon x-grid-loading",
32354             handler: this.onClick.createDelegate(this, ["refresh"])
32355         });
32356
32357         if(this.displayInfo){
32358             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32359         }
32360     },
32361
32362     // private
32363     updateInfo : function(){
32364         if(this.displayEl){
32365             var count = this.ds.getCount();
32366             var msg = count == 0 ?
32367                 this.emptyMsg :
32368                 String.format(
32369                     this.displayMsg,
32370                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32371                 );
32372             this.displayEl.update(msg);
32373         }
32374     },
32375
32376     // private
32377     onLoad : function(ds, r, o){
32378        this.cursor = o.params ? o.params.start : 0;
32379        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32380
32381        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32382        this.field.dom.value = ap;
32383        this.first.setDisabled(ap == 1);
32384        this.prev.setDisabled(ap == 1);
32385        this.next.setDisabled(ap == ps);
32386        this.last.setDisabled(ap == ps);
32387        this.loading.enable();
32388        this.updateInfo();
32389     },
32390
32391     // private
32392     getPageData : function(){
32393         var total = this.ds.getTotalCount();
32394         return {
32395             total : total,
32396             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32397             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32398         };
32399     },
32400
32401     // private
32402     onLoadError : function(){
32403         this.loading.enable();
32404     },
32405
32406     // private
32407     onPagingKeydown : function(e){
32408         var k = e.getKey();
32409         var d = this.getPageData();
32410         if(k == e.RETURN){
32411             var v = this.field.dom.value, pageNum;
32412             if(!v || isNaN(pageNum = parseInt(v, 10))){
32413                 this.field.dom.value = d.activePage;
32414                 return;
32415             }
32416             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32417             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32418             e.stopEvent();
32419         }
32420         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))
32421         {
32422           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32423           this.field.dom.value = pageNum;
32424           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32425           e.stopEvent();
32426         }
32427         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32428         {
32429           var v = this.field.dom.value, pageNum; 
32430           var increment = (e.shiftKey) ? 10 : 1;
32431           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32432             increment *= -1;
32433           }
32434           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32435             this.field.dom.value = d.activePage;
32436             return;
32437           }
32438           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32439           {
32440             this.field.dom.value = parseInt(v, 10) + increment;
32441             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32442             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32443           }
32444           e.stopEvent();
32445         }
32446     },
32447
32448     // private
32449     beforeLoad : function(){
32450         if(this.loading){
32451             this.loading.disable();
32452         }
32453     },
32454     /**
32455      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
32456      * @param {String} which (first|prev|next|last|refresh)  which button to press.
32457      *
32458      */
32459     // private
32460     onClick : function(which){
32461         var ds = this.ds;
32462         switch(which){
32463             case "first":
32464                 ds.load({params:{start: 0, limit: this.pageSize}});
32465             break;
32466             case "prev":
32467                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32468             break;
32469             case "next":
32470                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32471             break;
32472             case "last":
32473                 var total = ds.getTotalCount();
32474                 var extra = total % this.pageSize;
32475                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32476                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32477             break;
32478             case "refresh":
32479                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32480             break;
32481         }
32482     },
32483
32484     /**
32485      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32486      * @param {Roo.data.Store} store The data store to unbind
32487      */
32488     unbind : function(ds){
32489         ds.un("beforeload", this.beforeLoad, this);
32490         ds.un("load", this.onLoad, this);
32491         ds.un("loadexception", this.onLoadError, this);
32492         ds.un("remove", this.updateInfo, this);
32493         ds.un("add", this.updateInfo, this);
32494         this.ds = undefined;
32495     },
32496
32497     /**
32498      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32499      * @param {Roo.data.Store} store The data store to bind
32500      */
32501     bind : function(ds){
32502         ds.on("beforeload", this.beforeLoad, this);
32503         ds.on("load", this.onLoad, this);
32504         ds.on("loadexception", this.onLoadError, this);
32505         ds.on("remove", this.updateInfo, this);
32506         ds.on("add", this.updateInfo, this);
32507         this.ds = ds;
32508     }
32509 });/*
32510  * Based on:
32511  * Ext JS Library 1.1.1
32512  * Copyright(c) 2006-2007, Ext JS, LLC.
32513  *
32514  * Originally Released Under LGPL - original licence link has changed is not relivant.
32515  *
32516  * Fork - LGPL
32517  * <script type="text/javascript">
32518  */
32519
32520 /**
32521  * @class Roo.Resizable
32522  * @extends Roo.util.Observable
32523  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32524  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32525  * 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
32526  * the element will be wrapped for you automatically.</p>
32527  * <p>Here is the list of valid resize handles:</p>
32528  * <pre>
32529 Value   Description
32530 ------  -------------------
32531  'n'     north
32532  's'     south
32533  'e'     east
32534  'w'     west
32535  'nw'    northwest
32536  'sw'    southwest
32537  'se'    southeast
32538  'ne'    northeast
32539  'hd'    horizontal drag
32540  'all'   all
32541 </pre>
32542  * <p>Here's an example showing the creation of a typical Resizable:</p>
32543  * <pre><code>
32544 var resizer = new Roo.Resizable("element-id", {
32545     handles: 'all',
32546     minWidth: 200,
32547     minHeight: 100,
32548     maxWidth: 500,
32549     maxHeight: 400,
32550     pinned: true
32551 });
32552 resizer.on("resize", myHandler);
32553 </code></pre>
32554  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32555  * resizer.east.setDisplayed(false);</p>
32556  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32557  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32558  * resize operation's new size (defaults to [0, 0])
32559  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32560  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32561  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32562  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32563  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32564  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32565  * @cfg {Number} width The width of the element in pixels (defaults to null)
32566  * @cfg {Number} height The height of the element in pixels (defaults to null)
32567  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32568  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32569  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32570  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32571  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32572  * in favor of the handles config option (defaults to false)
32573  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32574  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32575  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32576  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32577  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32578  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32579  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32580  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32581  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32582  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32583  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32584  * @constructor
32585  * Create a new resizable component
32586  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32587  * @param {Object} config configuration options
32588   */
32589 Roo.Resizable = function(el, config)
32590 {
32591     this.el = Roo.get(el);
32592
32593     if(config && config.wrap){
32594         config.resizeChild = this.el;
32595         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32596         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32597         this.el.setStyle("overflow", "hidden");
32598         this.el.setPositioning(config.resizeChild.getPositioning());
32599         config.resizeChild.clearPositioning();
32600         if(!config.width || !config.height){
32601             var csize = config.resizeChild.getSize();
32602             this.el.setSize(csize.width, csize.height);
32603         }
32604         if(config.pinned && !config.adjustments){
32605             config.adjustments = "auto";
32606         }
32607     }
32608
32609     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32610     this.proxy.unselectable();
32611     this.proxy.enableDisplayMode('block');
32612
32613     Roo.apply(this, config);
32614
32615     if(this.pinned){
32616         this.disableTrackOver = true;
32617         this.el.addClass("x-resizable-pinned");
32618     }
32619     // if the element isn't positioned, make it relative
32620     var position = this.el.getStyle("position");
32621     if(position != "absolute" && position != "fixed"){
32622         this.el.setStyle("position", "relative");
32623     }
32624     if(!this.handles){ // no handles passed, must be legacy style
32625         this.handles = 's,e,se';
32626         if(this.multiDirectional){
32627             this.handles += ',n,w';
32628         }
32629     }
32630     if(this.handles == "all"){
32631         this.handles = "n s e w ne nw se sw";
32632     }
32633     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32634     var ps = Roo.Resizable.positions;
32635     for(var i = 0, len = hs.length; i < len; i++){
32636         if(hs[i] && ps[hs[i]]){
32637             var pos = ps[hs[i]];
32638             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32639         }
32640     }
32641     // legacy
32642     this.corner = this.southeast;
32643     
32644     // updateBox = the box can move..
32645     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32646         this.updateBox = true;
32647     }
32648
32649     this.activeHandle = null;
32650
32651     if(this.resizeChild){
32652         if(typeof this.resizeChild == "boolean"){
32653             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32654         }else{
32655             this.resizeChild = Roo.get(this.resizeChild, true);
32656         }
32657     }
32658     
32659     if(this.adjustments == "auto"){
32660         var rc = this.resizeChild;
32661         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32662         if(rc && (hw || hn)){
32663             rc.position("relative");
32664             rc.setLeft(hw ? hw.el.getWidth() : 0);
32665             rc.setTop(hn ? hn.el.getHeight() : 0);
32666         }
32667         this.adjustments = [
32668             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32669             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32670         ];
32671     }
32672
32673     if(this.draggable){
32674         this.dd = this.dynamic ?
32675             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32676         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32677     }
32678
32679     // public events
32680     this.addEvents({
32681         /**
32682          * @event beforeresize
32683          * Fired before resize is allowed. Set enabled to false to cancel resize.
32684          * @param {Roo.Resizable} this
32685          * @param {Roo.EventObject} e The mousedown event
32686          */
32687         "beforeresize" : true,
32688         /**
32689          * @event resizing
32690          * Fired a resizing.
32691          * @param {Roo.Resizable} this
32692          * @param {Number} x The new x position
32693          * @param {Number} y The new y position
32694          * @param {Number} w The new w width
32695          * @param {Number} h The new h hight
32696          * @param {Roo.EventObject} e The mouseup event
32697          */
32698         "resizing" : true,
32699         /**
32700          * @event resize
32701          * Fired after a resize.
32702          * @param {Roo.Resizable} this
32703          * @param {Number} width The new width
32704          * @param {Number} height The new height
32705          * @param {Roo.EventObject} e The mouseup event
32706          */
32707         "resize" : true
32708     });
32709
32710     if(this.width !== null && this.height !== null){
32711         this.resizeTo(this.width, this.height);
32712     }else{
32713         this.updateChildSize();
32714     }
32715     if(Roo.isIE){
32716         this.el.dom.style.zoom = 1;
32717     }
32718     Roo.Resizable.superclass.constructor.call(this);
32719 };
32720
32721 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32722         resizeChild : false,
32723         adjustments : [0, 0],
32724         minWidth : 5,
32725         minHeight : 5,
32726         maxWidth : 10000,
32727         maxHeight : 10000,
32728         enabled : true,
32729         animate : false,
32730         duration : .35,
32731         dynamic : false,
32732         handles : false,
32733         multiDirectional : false,
32734         disableTrackOver : false,
32735         easing : 'easeOutStrong',
32736         widthIncrement : 0,
32737         heightIncrement : 0,
32738         pinned : false,
32739         width : null,
32740         height : null,
32741         preserveRatio : false,
32742         transparent: false,
32743         minX: 0,
32744         minY: 0,
32745         draggable: false,
32746
32747         /**
32748          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32749          */
32750         constrainTo: undefined,
32751         /**
32752          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32753          */
32754         resizeRegion: undefined,
32755
32756
32757     /**
32758      * Perform a manual resize
32759      * @param {Number} width
32760      * @param {Number} height
32761      */
32762     resizeTo : function(width, height){
32763         this.el.setSize(width, height);
32764         this.updateChildSize();
32765         this.fireEvent("resize", this, width, height, null);
32766     },
32767
32768     // private
32769     startSizing : function(e, handle){
32770         this.fireEvent("beforeresize", this, e);
32771         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32772
32773             if(!this.overlay){
32774                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32775                 this.overlay.unselectable();
32776                 this.overlay.enableDisplayMode("block");
32777                 this.overlay.on("mousemove", this.onMouseMove, this);
32778                 this.overlay.on("mouseup", this.onMouseUp, this);
32779             }
32780             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32781
32782             this.resizing = true;
32783             this.startBox = this.el.getBox();
32784             this.startPoint = e.getXY();
32785             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32786                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32787
32788             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32789             this.overlay.show();
32790
32791             if(this.constrainTo) {
32792                 var ct = Roo.get(this.constrainTo);
32793                 this.resizeRegion = ct.getRegion().adjust(
32794                     ct.getFrameWidth('t'),
32795                     ct.getFrameWidth('l'),
32796                     -ct.getFrameWidth('b'),
32797                     -ct.getFrameWidth('r')
32798                 );
32799             }
32800
32801             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32802             this.proxy.show();
32803             this.proxy.setBox(this.startBox);
32804             if(!this.dynamic){
32805                 this.proxy.setStyle('visibility', 'visible');
32806             }
32807         }
32808     },
32809
32810     // private
32811     onMouseDown : function(handle, e){
32812         if(this.enabled){
32813             e.stopEvent();
32814             this.activeHandle = handle;
32815             this.startSizing(e, handle);
32816         }
32817     },
32818
32819     // private
32820     onMouseUp : function(e){
32821         var size = this.resizeElement();
32822         this.resizing = false;
32823         this.handleOut();
32824         this.overlay.hide();
32825         this.proxy.hide();
32826         this.fireEvent("resize", this, size.width, size.height, e);
32827     },
32828
32829     // private
32830     updateChildSize : function(){
32831         
32832         if(this.resizeChild){
32833             var el = this.el;
32834             var child = this.resizeChild;
32835             var adj = this.adjustments;
32836             if(el.dom.offsetWidth){
32837                 var b = el.getSize(true);
32838                 child.setSize(b.width+adj[0], b.height+adj[1]);
32839             }
32840             // Second call here for IE
32841             // The first call enables instant resizing and
32842             // the second call corrects scroll bars if they
32843             // exist
32844             if(Roo.isIE){
32845                 setTimeout(function(){
32846                     if(el.dom.offsetWidth){
32847                         var b = el.getSize(true);
32848                         child.setSize(b.width+adj[0], b.height+adj[1]);
32849                     }
32850                 }, 10);
32851             }
32852         }
32853     },
32854
32855     // private
32856     snap : function(value, inc, min){
32857         if(!inc || !value) {
32858             return value;
32859         }
32860         var newValue = value;
32861         var m = value % inc;
32862         if(m > 0){
32863             if(m > (inc/2)){
32864                 newValue = value + (inc-m);
32865             }else{
32866                 newValue = value - m;
32867             }
32868         }
32869         return Math.max(min, newValue);
32870     },
32871
32872     // private
32873     resizeElement : function(){
32874         var box = this.proxy.getBox();
32875         if(this.updateBox){
32876             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32877         }else{
32878             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32879         }
32880         this.updateChildSize();
32881         if(!this.dynamic){
32882             this.proxy.hide();
32883         }
32884         return box;
32885     },
32886
32887     // private
32888     constrain : function(v, diff, m, mx){
32889         if(v - diff < m){
32890             diff = v - m;
32891         }else if(v - diff > mx){
32892             diff = mx - v;
32893         }
32894         return diff;
32895     },
32896
32897     // private
32898     onMouseMove : function(e){
32899         
32900         if(this.enabled){
32901             try{// try catch so if something goes wrong the user doesn't get hung
32902
32903             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32904                 return;
32905             }
32906
32907             //var curXY = this.startPoint;
32908             var curSize = this.curSize || this.startBox;
32909             var x = this.startBox.x, y = this.startBox.y;
32910             var ox = x, oy = y;
32911             var w = curSize.width, h = curSize.height;
32912             var ow = w, oh = h;
32913             var mw = this.minWidth, mh = this.minHeight;
32914             var mxw = this.maxWidth, mxh = this.maxHeight;
32915             var wi = this.widthIncrement;
32916             var hi = this.heightIncrement;
32917
32918             var eventXY = e.getXY();
32919             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32920             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32921
32922             var pos = this.activeHandle.position;
32923
32924             switch(pos){
32925                 case "east":
32926                     w += diffX;
32927                     w = Math.min(Math.max(mw, w), mxw);
32928                     break;
32929              
32930                 case "south":
32931                     h += diffY;
32932                     h = Math.min(Math.max(mh, h), mxh);
32933                     break;
32934                 case "southeast":
32935                     w += diffX;
32936                     h += diffY;
32937                     w = Math.min(Math.max(mw, w), mxw);
32938                     h = Math.min(Math.max(mh, h), mxh);
32939                     break;
32940                 case "north":
32941                     diffY = this.constrain(h, diffY, mh, mxh);
32942                     y += diffY;
32943                     h -= diffY;
32944                     break;
32945                 case "hdrag":
32946                     
32947                     if (wi) {
32948                         var adiffX = Math.abs(diffX);
32949                         var sub = (adiffX % wi); // how much 
32950                         if (sub > (wi/2)) { // far enough to snap
32951                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32952                         } else {
32953                             // remove difference.. 
32954                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32955                         }
32956                     }
32957                     x += diffX;
32958                     x = Math.max(this.minX, x);
32959                     break;
32960                 case "west":
32961                     diffX = this.constrain(w, diffX, mw, mxw);
32962                     x += diffX;
32963                     w -= diffX;
32964                     break;
32965                 case "northeast":
32966                     w += diffX;
32967                     w = Math.min(Math.max(mw, w), mxw);
32968                     diffY = this.constrain(h, diffY, mh, mxh);
32969                     y += diffY;
32970                     h -= diffY;
32971                     break;
32972                 case "northwest":
32973                     diffX = this.constrain(w, diffX, mw, mxw);
32974                     diffY = this.constrain(h, diffY, mh, mxh);
32975                     y += diffY;
32976                     h -= diffY;
32977                     x += diffX;
32978                     w -= diffX;
32979                     break;
32980                case "southwest":
32981                     diffX = this.constrain(w, diffX, mw, mxw);
32982                     h += diffY;
32983                     h = Math.min(Math.max(mh, h), mxh);
32984                     x += diffX;
32985                     w -= diffX;
32986                     break;
32987             }
32988
32989             var sw = this.snap(w, wi, mw);
32990             var sh = this.snap(h, hi, mh);
32991             if(sw != w || sh != h){
32992                 switch(pos){
32993                     case "northeast":
32994                         y -= sh - h;
32995                     break;
32996                     case "north":
32997                         y -= sh - h;
32998                         break;
32999                     case "southwest":
33000                         x -= sw - w;
33001                     break;
33002                     case "west":
33003                         x -= sw - w;
33004                         break;
33005                     case "northwest":
33006                         x -= sw - w;
33007                         y -= sh - h;
33008                     break;
33009                 }
33010                 w = sw;
33011                 h = sh;
33012             }
33013
33014             if(this.preserveRatio){
33015                 switch(pos){
33016                     case "southeast":
33017                     case "east":
33018                         h = oh * (w/ow);
33019                         h = Math.min(Math.max(mh, h), mxh);
33020                         w = ow * (h/oh);
33021                        break;
33022                     case "south":
33023                         w = ow * (h/oh);
33024                         w = Math.min(Math.max(mw, w), mxw);
33025                         h = oh * (w/ow);
33026                         break;
33027                     case "northeast":
33028                         w = ow * (h/oh);
33029                         w = Math.min(Math.max(mw, w), mxw);
33030                         h = oh * (w/ow);
33031                     break;
33032                     case "north":
33033                         var tw = w;
33034                         w = ow * (h/oh);
33035                         w = Math.min(Math.max(mw, w), mxw);
33036                         h = oh * (w/ow);
33037                         x += (tw - w) / 2;
33038                         break;
33039                     case "southwest":
33040                         h = oh * (w/ow);
33041                         h = Math.min(Math.max(mh, h), mxh);
33042                         var tw = w;
33043                         w = ow * (h/oh);
33044                         x += tw - w;
33045                         break;
33046                     case "west":
33047                         var th = h;
33048                         h = oh * (w/ow);
33049                         h = Math.min(Math.max(mh, h), mxh);
33050                         y += (th - h) / 2;
33051                         var tw = w;
33052                         w = ow * (h/oh);
33053                         x += tw - w;
33054                        break;
33055                     case "northwest":
33056                         var tw = w;
33057                         var th = h;
33058                         h = oh * (w/ow);
33059                         h = Math.min(Math.max(mh, h), mxh);
33060                         w = ow * (h/oh);
33061                         y += th - h;
33062                         x += tw - w;
33063                        break;
33064
33065                 }
33066             }
33067             if (pos == 'hdrag') {
33068                 w = ow;
33069             }
33070             this.proxy.setBounds(x, y, w, h);
33071             if(this.dynamic){
33072                 this.resizeElement();
33073             }
33074             }catch(e){}
33075         }
33076         this.fireEvent("resizing", this, x, y, w, h, e);
33077     },
33078
33079     // private
33080     handleOver : function(){
33081         if(this.enabled){
33082             this.el.addClass("x-resizable-over");
33083         }
33084     },
33085
33086     // private
33087     handleOut : function(){
33088         if(!this.resizing){
33089             this.el.removeClass("x-resizable-over");
33090         }
33091     },
33092
33093     /**
33094      * Returns the element this component is bound to.
33095      * @return {Roo.Element}
33096      */
33097     getEl : function(){
33098         return this.el;
33099     },
33100
33101     /**
33102      * Returns the resizeChild element (or null).
33103      * @return {Roo.Element}
33104      */
33105     getResizeChild : function(){
33106         return this.resizeChild;
33107     },
33108     groupHandler : function()
33109     {
33110         
33111     },
33112     /**
33113      * Destroys this resizable. If the element was wrapped and
33114      * removeEl is not true then the element remains.
33115      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33116      */
33117     destroy : function(removeEl){
33118         this.proxy.remove();
33119         if(this.overlay){
33120             this.overlay.removeAllListeners();
33121             this.overlay.remove();
33122         }
33123         var ps = Roo.Resizable.positions;
33124         for(var k in ps){
33125             if(typeof ps[k] != "function" && this[ps[k]]){
33126                 var h = this[ps[k]];
33127                 h.el.removeAllListeners();
33128                 h.el.remove();
33129             }
33130         }
33131         if(removeEl){
33132             this.el.update("");
33133             this.el.remove();
33134         }
33135     }
33136 });
33137
33138 // private
33139 // hash to map config positions to true positions
33140 Roo.Resizable.positions = {
33141     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
33142     hd: "hdrag"
33143 };
33144
33145 // private
33146 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
33147     if(!this.tpl){
33148         // only initialize the template if resizable is used
33149         var tpl = Roo.DomHelper.createTemplate(
33150             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
33151         );
33152         tpl.compile();
33153         Roo.Resizable.Handle.prototype.tpl = tpl;
33154     }
33155     this.position = pos;
33156     this.rz = rz;
33157     // show north drag fro topdra
33158     var handlepos = pos == 'hdrag' ? 'north' : pos;
33159     
33160     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
33161     if (pos == 'hdrag') {
33162         this.el.setStyle('cursor', 'pointer');
33163     }
33164     this.el.unselectable();
33165     if(transparent){
33166         this.el.setOpacity(0);
33167     }
33168     this.el.on("mousedown", this.onMouseDown, this);
33169     if(!disableTrackOver){
33170         this.el.on("mouseover", this.onMouseOver, this);
33171         this.el.on("mouseout", this.onMouseOut, this);
33172     }
33173 };
33174
33175 // private
33176 Roo.Resizable.Handle.prototype = {
33177     afterResize : function(rz){
33178         Roo.log('after?');
33179         // do nothing
33180     },
33181     // private
33182     onMouseDown : function(e){
33183         this.rz.onMouseDown(this, e);
33184     },
33185     // private
33186     onMouseOver : function(e){
33187         this.rz.handleOver(this, e);
33188     },
33189     // private
33190     onMouseOut : function(e){
33191         this.rz.handleOut(this, e);
33192     }
33193 };/*
33194  * Based on:
33195  * Ext JS Library 1.1.1
33196  * Copyright(c) 2006-2007, Ext JS, LLC.
33197  *
33198  * Originally Released Under LGPL - original licence link has changed is not relivant.
33199  *
33200  * Fork - LGPL
33201  * <script type="text/javascript">
33202  */
33203
33204 /**
33205  * @class Roo.Editor
33206  * @extends Roo.Component
33207  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
33208  * @constructor
33209  * Create a new Editor
33210  * @param {Roo.form.Field} field The Field object (or descendant)
33211  * @param {Object} config The config object
33212  */
33213 Roo.Editor = function(field, config){
33214     Roo.Editor.superclass.constructor.call(this, config);
33215     this.field = field;
33216     this.addEvents({
33217         /**
33218              * @event beforestartedit
33219              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33220              * false from the handler of this event.
33221              * @param {Editor} this
33222              * @param {Roo.Element} boundEl The underlying element bound to this editor
33223              * @param {Mixed} value The field value being set
33224              */
33225         "beforestartedit" : true,
33226         /**
33227              * @event startedit
33228              * Fires when this editor is displayed
33229              * @param {Roo.Element} boundEl The underlying element bound to this editor
33230              * @param {Mixed} value The starting field value
33231              */
33232         "startedit" : true,
33233         /**
33234              * @event beforecomplete
33235              * Fires after a change has been made to the field, but before the change is reflected in the underlying
33236              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
33237              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33238              * event will not fire since no edit actually occurred.
33239              * @param {Editor} this
33240              * @param {Mixed} value The current field value
33241              * @param {Mixed} startValue The original field value
33242              */
33243         "beforecomplete" : true,
33244         /**
33245              * @event complete
33246              * Fires after editing is complete and any changed value has been written to the underlying field.
33247              * @param {Editor} this
33248              * @param {Mixed} value The current field value
33249              * @param {Mixed} startValue The original field value
33250              */
33251         "complete" : true,
33252         /**
33253          * @event specialkey
33254          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33255          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33256          * @param {Roo.form.Field} this
33257          * @param {Roo.EventObject} e The event object
33258          */
33259         "specialkey" : true
33260     });
33261 };
33262
33263 Roo.extend(Roo.Editor, Roo.Component, {
33264     /**
33265      * @cfg {Boolean/String} autosize
33266      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33267      * or "height" to adopt the height only (defaults to false)
33268      */
33269     /**
33270      * @cfg {Boolean} revertInvalid
33271      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33272      * validation fails (defaults to true)
33273      */
33274     /**
33275      * @cfg {Boolean} ignoreNoChange
33276      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33277      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33278      * will never be ignored.
33279      */
33280     /**
33281      * @cfg {Boolean} hideEl
33282      * False to keep the bound element visible while the editor is displayed (defaults to true)
33283      */
33284     /**
33285      * @cfg {Mixed} value
33286      * The data value of the underlying field (defaults to "")
33287      */
33288     value : "",
33289     /**
33290      * @cfg {String} alignment
33291      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33292      */
33293     alignment: "c-c?",
33294     /**
33295      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33296      * for bottom-right shadow (defaults to "frame")
33297      */
33298     shadow : "frame",
33299     /**
33300      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33301      */
33302     constrain : false,
33303     /**
33304      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33305      */
33306     completeOnEnter : false,
33307     /**
33308      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33309      */
33310     cancelOnEsc : false,
33311     /**
33312      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33313      */
33314     updateEl : false,
33315
33316     // private
33317     onRender : function(ct, position){
33318         this.el = new Roo.Layer({
33319             shadow: this.shadow,
33320             cls: "x-editor",
33321             parentEl : ct,
33322             shim : this.shim,
33323             shadowOffset:4,
33324             id: this.id,
33325             constrain: this.constrain
33326         });
33327         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33328         if(this.field.msgTarget != 'title'){
33329             this.field.msgTarget = 'qtip';
33330         }
33331         this.field.render(this.el);
33332         if(Roo.isGecko){
33333             this.field.el.dom.setAttribute('autocomplete', 'off');
33334         }
33335         this.field.on("specialkey", this.onSpecialKey, this);
33336         if(this.swallowKeys){
33337             this.field.el.swallowEvent(['keydown','keypress']);
33338         }
33339         this.field.show();
33340         this.field.on("blur", this.onBlur, this);
33341         if(this.field.grow){
33342             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33343         }
33344     },
33345
33346     onSpecialKey : function(field, e)
33347     {
33348         //Roo.log('editor onSpecialKey');
33349         if(this.completeOnEnter && e.getKey() == e.ENTER){
33350             e.stopEvent();
33351             this.completeEdit();
33352             return;
33353         }
33354         // do not fire special key otherwise it might hide close the editor...
33355         if(e.getKey() == e.ENTER){    
33356             return;
33357         }
33358         if(this.cancelOnEsc && e.getKey() == e.ESC){
33359             this.cancelEdit();
33360             return;
33361         } 
33362         this.fireEvent('specialkey', field, e);
33363     
33364     },
33365
33366     /**
33367      * Starts the editing process and shows the editor.
33368      * @param {String/HTMLElement/Element} el The element to edit
33369      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33370       * to the innerHTML of el.
33371      */
33372     startEdit : function(el, value){
33373         if(this.editing){
33374             this.completeEdit();
33375         }
33376         this.boundEl = Roo.get(el);
33377         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33378         if(!this.rendered){
33379             this.render(this.parentEl || document.body);
33380         }
33381         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33382             return;
33383         }
33384         this.startValue = v;
33385         this.field.setValue(v);
33386         if(this.autoSize){
33387             var sz = this.boundEl.getSize();
33388             switch(this.autoSize){
33389                 case "width":
33390                 this.setSize(sz.width,  "");
33391                 break;
33392                 case "height":
33393                 this.setSize("",  sz.height);
33394                 break;
33395                 default:
33396                 this.setSize(sz.width,  sz.height);
33397             }
33398         }
33399         this.el.alignTo(this.boundEl, this.alignment);
33400         this.editing = true;
33401         if(Roo.QuickTips){
33402             Roo.QuickTips.disable();
33403         }
33404         this.show();
33405     },
33406
33407     /**
33408      * Sets the height and width of this editor.
33409      * @param {Number} width The new width
33410      * @param {Number} height The new height
33411      */
33412     setSize : function(w, h){
33413         this.field.setSize(w, h);
33414         if(this.el){
33415             this.el.sync();
33416         }
33417     },
33418
33419     /**
33420      * Realigns the editor to the bound field based on the current alignment config value.
33421      */
33422     realign : function(){
33423         this.el.alignTo(this.boundEl, this.alignment);
33424     },
33425
33426     /**
33427      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33428      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33429      */
33430     completeEdit : function(remainVisible){
33431         if(!this.editing){
33432             return;
33433         }
33434         var v = this.getValue();
33435         if(this.revertInvalid !== false && !this.field.isValid()){
33436             v = this.startValue;
33437             this.cancelEdit(true);
33438         }
33439         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33440             this.editing = false;
33441             this.hide();
33442             return;
33443         }
33444         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33445             this.editing = false;
33446             if(this.updateEl && this.boundEl){
33447                 this.boundEl.update(v);
33448             }
33449             if(remainVisible !== true){
33450                 this.hide();
33451             }
33452             this.fireEvent("complete", this, v, this.startValue);
33453         }
33454     },
33455
33456     // private
33457     onShow : function(){
33458         this.el.show();
33459         if(this.hideEl !== false){
33460             this.boundEl.hide();
33461         }
33462         this.field.show();
33463         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33464             this.fixIEFocus = true;
33465             this.deferredFocus.defer(50, this);
33466         }else{
33467             this.field.focus();
33468         }
33469         this.fireEvent("startedit", this.boundEl, this.startValue);
33470     },
33471
33472     deferredFocus : function(){
33473         if(this.editing){
33474             this.field.focus();
33475         }
33476     },
33477
33478     /**
33479      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33480      * reverted to the original starting value.
33481      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33482      * cancel (defaults to false)
33483      */
33484     cancelEdit : function(remainVisible){
33485         if(this.editing){
33486             this.setValue(this.startValue);
33487             if(remainVisible !== true){
33488                 this.hide();
33489             }
33490         }
33491     },
33492
33493     // private
33494     onBlur : function(){
33495         if(this.allowBlur !== true && this.editing){
33496             this.completeEdit();
33497         }
33498     },
33499
33500     // private
33501     onHide : function(){
33502         if(this.editing){
33503             this.completeEdit();
33504             return;
33505         }
33506         this.field.blur();
33507         if(this.field.collapse){
33508             this.field.collapse();
33509         }
33510         this.el.hide();
33511         if(this.hideEl !== false){
33512             this.boundEl.show();
33513         }
33514         if(Roo.QuickTips){
33515             Roo.QuickTips.enable();
33516         }
33517     },
33518
33519     /**
33520      * Sets the data value of the editor
33521      * @param {Mixed} value Any valid value supported by the underlying field
33522      */
33523     setValue : function(v){
33524         this.field.setValue(v);
33525     },
33526
33527     /**
33528      * Gets the data value of the editor
33529      * @return {Mixed} The data value
33530      */
33531     getValue : function(){
33532         return this.field.getValue();
33533     }
33534 });/*
33535  * Based on:
33536  * Ext JS Library 1.1.1
33537  * Copyright(c) 2006-2007, Ext JS, LLC.
33538  *
33539  * Originally Released Under LGPL - original licence link has changed is not relivant.
33540  *
33541  * Fork - LGPL
33542  * <script type="text/javascript">
33543  */
33544  
33545 /**
33546  * @class Roo.BasicDialog
33547  * @extends Roo.util.Observable
33548  * @parent none builder
33549  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33550  * <pre><code>
33551 var dlg = new Roo.BasicDialog("my-dlg", {
33552     height: 200,
33553     width: 300,
33554     minHeight: 100,
33555     minWidth: 150,
33556     modal: true,
33557     proxyDrag: true,
33558     shadow: true
33559 });
33560 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33561 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33562 dlg.addButton('Cancel', dlg.hide, dlg);
33563 dlg.show();
33564 </code></pre>
33565   <b>A Dialog should always be a direct child of the body element.</b>
33566  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33567  * @cfg {String} title Default text to display in the title bar (defaults to null)
33568  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33569  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33570  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33571  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33572  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33573  * (defaults to null with no animation)
33574  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33575  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33576  * property for valid values (defaults to 'all')
33577  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33578  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33579  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33580  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33581  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33582  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33583  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33584  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33585  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33586  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33587  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33588  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33589  * draggable = true (defaults to false)
33590  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33591  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33592  * shadow (defaults to false)
33593  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33594  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33595  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33596  * @cfg {Array} buttons Array of buttons
33597  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33598  * @constructor
33599  * Create a new BasicDialog.
33600  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33601  * @param {Object} config Configuration options
33602  */
33603 Roo.BasicDialog = function(el, config){
33604     this.el = Roo.get(el);
33605     var dh = Roo.DomHelper;
33606     if(!this.el && config && config.autoCreate){
33607         if(typeof config.autoCreate == "object"){
33608             if(!config.autoCreate.id){
33609                 config.autoCreate.id = el;
33610             }
33611             this.el = dh.append(document.body,
33612                         config.autoCreate, true);
33613         }else{
33614             this.el = dh.append(document.body,
33615                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33616         }
33617     }
33618     el = this.el;
33619     el.setDisplayed(true);
33620     el.hide = this.hideAction;
33621     this.id = el.id;
33622     el.addClass("x-dlg");
33623
33624     Roo.apply(this, config);
33625
33626     this.proxy = el.createProxy("x-dlg-proxy");
33627     this.proxy.hide = this.hideAction;
33628     this.proxy.setOpacity(.5);
33629     this.proxy.hide();
33630
33631     if(config.width){
33632         el.setWidth(config.width);
33633     }
33634     if(config.height){
33635         el.setHeight(config.height);
33636     }
33637     this.size = el.getSize();
33638     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33639         this.xy = [config.x,config.y];
33640     }else{
33641         this.xy = el.getCenterXY(true);
33642     }
33643     /** The header element @type Roo.Element */
33644     this.header = el.child("> .x-dlg-hd");
33645     /** The body element @type Roo.Element */
33646     this.body = el.child("> .x-dlg-bd");
33647     /** The footer element @type Roo.Element */
33648     this.footer = el.child("> .x-dlg-ft");
33649
33650     if(!this.header){
33651         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33652     }
33653     if(!this.body){
33654         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33655     }
33656
33657     this.header.unselectable();
33658     if(this.title){
33659         this.header.update(this.title);
33660     }
33661     // this element allows the dialog to be focused for keyboard event
33662     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33663     this.focusEl.swallowEvent("click", true);
33664
33665     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33666
33667     // wrap the body and footer for special rendering
33668     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33669     if(this.footer){
33670         this.bwrap.dom.appendChild(this.footer.dom);
33671     }
33672
33673     this.bg = this.el.createChild({
33674         tag: "div", cls:"x-dlg-bg",
33675         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33676     });
33677     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33678
33679
33680     if(this.autoScroll !== false && !this.autoTabs){
33681         this.body.setStyle("overflow", "auto");
33682     }
33683
33684     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33685
33686     if(this.closable !== false){
33687         this.el.addClass("x-dlg-closable");
33688         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33689         this.close.on("click", this.closeClick, this);
33690         this.close.addClassOnOver("x-dlg-close-over");
33691     }
33692     if(this.collapsible !== false){
33693         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33694         this.collapseBtn.on("click", this.collapseClick, this);
33695         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33696         this.header.on("dblclick", this.collapseClick, this);
33697     }
33698     if(this.resizable !== false){
33699         this.el.addClass("x-dlg-resizable");
33700         this.resizer = new Roo.Resizable(el, {
33701             minWidth: this.minWidth || 80,
33702             minHeight:this.minHeight || 80,
33703             handles: this.resizeHandles || "all",
33704             pinned: true
33705         });
33706         this.resizer.on("beforeresize", this.beforeResize, this);
33707         this.resizer.on("resize", this.onResize, this);
33708     }
33709     if(this.draggable !== false){
33710         el.addClass("x-dlg-draggable");
33711         if (!this.proxyDrag) {
33712             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33713         }
33714         else {
33715             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33716         }
33717         dd.setHandleElId(this.header.id);
33718         dd.endDrag = this.endMove.createDelegate(this);
33719         dd.startDrag = this.startMove.createDelegate(this);
33720         dd.onDrag = this.onDrag.createDelegate(this);
33721         dd.scroll = false;
33722         this.dd = dd;
33723     }
33724     if(this.modal){
33725         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33726         this.mask.enableDisplayMode("block");
33727         this.mask.hide();
33728         this.el.addClass("x-dlg-modal");
33729     }
33730     if(this.shadow){
33731         this.shadow = new Roo.Shadow({
33732             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33733             offset : this.shadowOffset
33734         });
33735     }else{
33736         this.shadowOffset = 0;
33737     }
33738     if(Roo.useShims && this.shim !== false){
33739         this.shim = this.el.createShim();
33740         this.shim.hide = this.hideAction;
33741         this.shim.hide();
33742     }else{
33743         this.shim = false;
33744     }
33745     if(this.autoTabs){
33746         this.initTabs();
33747     }
33748     if (this.buttons) { 
33749         var bts= this.buttons;
33750         this.buttons = [];
33751         Roo.each(bts, function(b) {
33752             this.addButton(b);
33753         }, this);
33754     }
33755     
33756     
33757     this.addEvents({
33758         /**
33759          * @event keydown
33760          * Fires when a key is pressed
33761          * @param {Roo.BasicDialog} this
33762          * @param {Roo.EventObject} e
33763          */
33764         "keydown" : true,
33765         /**
33766          * @event move
33767          * Fires when this dialog is moved by the user.
33768          * @param {Roo.BasicDialog} this
33769          * @param {Number} x The new page X
33770          * @param {Number} y The new page Y
33771          */
33772         "move" : true,
33773         /**
33774          * @event resize
33775          * Fires when this dialog is resized by the user.
33776          * @param {Roo.BasicDialog} this
33777          * @param {Number} width The new width
33778          * @param {Number} height The new height
33779          */
33780         "resize" : true,
33781         /**
33782          * @event beforehide
33783          * Fires before this dialog is hidden.
33784          * @param {Roo.BasicDialog} this
33785          */
33786         "beforehide" : true,
33787         /**
33788          * @event hide
33789          * Fires when this dialog is hidden.
33790          * @param {Roo.BasicDialog} this
33791          */
33792         "hide" : true,
33793         /**
33794          * @event beforeshow
33795          * Fires before this dialog is shown.
33796          * @param {Roo.BasicDialog} this
33797          */
33798         "beforeshow" : true,
33799         /**
33800          * @event show
33801          * Fires when this dialog is shown.
33802          * @param {Roo.BasicDialog} this
33803          */
33804         "show" : true
33805     });
33806     el.on("keydown", this.onKeyDown, this);
33807     el.on("mousedown", this.toFront, this);
33808     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33809     this.el.hide();
33810     Roo.DialogManager.register(this);
33811     Roo.BasicDialog.superclass.constructor.call(this);
33812 };
33813
33814 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33815     shadowOffset: Roo.isIE ? 6 : 5,
33816     minHeight: 80,
33817     minWidth: 200,
33818     minButtonWidth: 75,
33819     defaultButton: null,
33820     buttonAlign: "right",
33821     tabTag: 'div',
33822     firstShow: true,
33823
33824     /**
33825      * Sets the dialog title text
33826      * @param {String} text The title text to display
33827      * @return {Roo.BasicDialog} this
33828      */
33829     setTitle : function(text){
33830         this.header.update(text);
33831         return this;
33832     },
33833
33834     // private
33835     closeClick : function(){
33836         this.hide();
33837     },
33838
33839     // private
33840     collapseClick : function(){
33841         this[this.collapsed ? "expand" : "collapse"]();
33842     },
33843
33844     /**
33845      * Collapses the dialog to its minimized state (only the title bar is visible).
33846      * Equivalent to the user clicking the collapse dialog button.
33847      */
33848     collapse : function(){
33849         if(!this.collapsed){
33850             this.collapsed = true;
33851             this.el.addClass("x-dlg-collapsed");
33852             this.restoreHeight = this.el.getHeight();
33853             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33854         }
33855     },
33856
33857     /**
33858      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33859      * clicking the expand dialog button.
33860      */
33861     expand : function(){
33862         if(this.collapsed){
33863             this.collapsed = false;
33864             this.el.removeClass("x-dlg-collapsed");
33865             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33866         }
33867     },
33868
33869     /**
33870      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33871      * @return {Roo.TabPanel} The tabs component
33872      */
33873     initTabs : function(){
33874         var tabs = this.getTabs();
33875         while(tabs.getTab(0)){
33876             tabs.removeTab(0);
33877         }
33878         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33879             var dom = el.dom;
33880             tabs.addTab(Roo.id(dom), dom.title);
33881             dom.title = "";
33882         });
33883         tabs.activate(0);
33884         return tabs;
33885     },
33886
33887     // private
33888     beforeResize : function(){
33889         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33890     },
33891
33892     // private
33893     onResize : function(){
33894         this.refreshSize();
33895         this.syncBodyHeight();
33896         this.adjustAssets();
33897         this.focus();
33898         this.fireEvent("resize", this, this.size.width, this.size.height);
33899     },
33900
33901     // private
33902     onKeyDown : function(e){
33903         if(this.isVisible()){
33904             this.fireEvent("keydown", this, e);
33905         }
33906     },
33907
33908     /**
33909      * Resizes the dialog.
33910      * @param {Number} width
33911      * @param {Number} height
33912      * @return {Roo.BasicDialog} this
33913      */
33914     resizeTo : function(width, height){
33915         this.el.setSize(width, height);
33916         this.size = {width: width, height: height};
33917         this.syncBodyHeight();
33918         if(this.fixedcenter){
33919             this.center();
33920         }
33921         if(this.isVisible()){
33922             this.constrainXY();
33923             this.adjustAssets();
33924         }
33925         this.fireEvent("resize", this, width, height);
33926         return this;
33927     },
33928
33929
33930     /**
33931      * Resizes the dialog to fit the specified content size.
33932      * @param {Number} width
33933      * @param {Number} height
33934      * @return {Roo.BasicDialog} this
33935      */
33936     setContentSize : function(w, h){
33937         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33938         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33939         //if(!this.el.isBorderBox()){
33940             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33941             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33942         //}
33943         if(this.tabs){
33944             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33945             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33946         }
33947         this.resizeTo(w, h);
33948         return this;
33949     },
33950
33951     /**
33952      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33953      * executed in response to a particular key being pressed while the dialog is active.
33954      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33955      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33956      * @param {Function} fn The function to call
33957      * @param {Object} scope (optional) The scope of the function
33958      * @return {Roo.BasicDialog} this
33959      */
33960     addKeyListener : function(key, fn, scope){
33961         var keyCode, shift, ctrl, alt;
33962         if(typeof key == "object" && !(key instanceof Array)){
33963             keyCode = key["key"];
33964             shift = key["shift"];
33965             ctrl = key["ctrl"];
33966             alt = key["alt"];
33967         }else{
33968             keyCode = key;
33969         }
33970         var handler = function(dlg, e){
33971             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33972                 var k = e.getKey();
33973                 if(keyCode instanceof Array){
33974                     for(var i = 0, len = keyCode.length; i < len; i++){
33975                         if(keyCode[i] == k){
33976                           fn.call(scope || window, dlg, k, e);
33977                           return;
33978                         }
33979                     }
33980                 }else{
33981                     if(k == keyCode){
33982                         fn.call(scope || window, dlg, k, e);
33983                     }
33984                 }
33985             }
33986         };
33987         this.on("keydown", handler);
33988         return this;
33989     },
33990
33991     /**
33992      * Returns the TabPanel component (creates it if it doesn't exist).
33993      * Note: If you wish to simply check for the existence of tabs without creating them,
33994      * check for a null 'tabs' property.
33995      * @return {Roo.TabPanel} The tabs component
33996      */
33997     getTabs : function(){
33998         if(!this.tabs){
33999             this.el.addClass("x-dlg-auto-tabs");
34000             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
34001             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
34002         }
34003         return this.tabs;
34004     },
34005
34006     /**
34007      * Adds a button to the footer section of the dialog.
34008      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
34009      * object or a valid Roo.DomHelper element config
34010      * @param {Function} handler The function called when the button is clicked
34011      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
34012      * @return {Roo.Button} The new button
34013      */
34014     addButton : function(config, handler, scope){
34015         var dh = Roo.DomHelper;
34016         if(!this.footer){
34017             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
34018         }
34019         if(!this.btnContainer){
34020             var tb = this.footer.createChild({
34021
34022                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
34023                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
34024             }, null, true);
34025             this.btnContainer = tb.firstChild.firstChild.firstChild;
34026         }
34027         var bconfig = {
34028             handler: handler,
34029             scope: scope,
34030             minWidth: this.minButtonWidth,
34031             hideParent:true
34032         };
34033         if(typeof config == "string"){
34034             bconfig.text = config;
34035         }else{
34036             if(config.tag){
34037                 bconfig.dhconfig = config;
34038             }else{
34039                 Roo.apply(bconfig, config);
34040             }
34041         }
34042         var fc = false;
34043         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
34044             bconfig.position = Math.max(0, bconfig.position);
34045             fc = this.btnContainer.childNodes[bconfig.position];
34046         }
34047          
34048         var btn = new Roo.Button(
34049             fc ? 
34050                 this.btnContainer.insertBefore(document.createElement("td"),fc)
34051                 : this.btnContainer.appendChild(document.createElement("td")),
34052             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
34053             bconfig
34054         );
34055         this.syncBodyHeight();
34056         if(!this.buttons){
34057             /**
34058              * Array of all the buttons that have been added to this dialog via addButton
34059              * @type Array
34060              */
34061             this.buttons = [];
34062         }
34063         this.buttons.push(btn);
34064         return btn;
34065     },
34066
34067     /**
34068      * Sets the default button to be focused when the dialog is displayed.
34069      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
34070      * @return {Roo.BasicDialog} this
34071      */
34072     setDefaultButton : function(btn){
34073         this.defaultButton = btn;
34074         return this;
34075     },
34076
34077     // private
34078     getHeaderFooterHeight : function(safe){
34079         var height = 0;
34080         if(this.header){
34081            height += this.header.getHeight();
34082         }
34083         if(this.footer){
34084            var fm = this.footer.getMargins();
34085             height += (this.footer.getHeight()+fm.top+fm.bottom);
34086         }
34087         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
34088         height += this.centerBg.getPadding("tb");
34089         return height;
34090     },
34091
34092     // private
34093     syncBodyHeight : function()
34094     {
34095         var bd = this.body, // the text
34096             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
34097             bw = this.bwrap;
34098         var height = this.size.height - this.getHeaderFooterHeight(false);
34099         bd.setHeight(height-bd.getMargins("tb"));
34100         var hh = this.header.getHeight();
34101         var h = this.size.height-hh;
34102         cb.setHeight(h);
34103         
34104         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
34105         bw.setHeight(h-cb.getPadding("tb"));
34106         
34107         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
34108         bd.setWidth(bw.getWidth(true));
34109         if(this.tabs){
34110             this.tabs.syncHeight();
34111             if(Roo.isIE){
34112                 this.tabs.el.repaint();
34113             }
34114         }
34115     },
34116
34117     /**
34118      * Restores the previous state of the dialog if Roo.state is configured.
34119      * @return {Roo.BasicDialog} this
34120      */
34121     restoreState : function(){
34122         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
34123         if(box && box.width){
34124             this.xy = [box.x, box.y];
34125             this.resizeTo(box.width, box.height);
34126         }
34127         return this;
34128     },
34129
34130     // private
34131     beforeShow : function(){
34132         this.expand();
34133         if(this.fixedcenter){
34134             this.xy = this.el.getCenterXY(true);
34135         }
34136         if(this.modal){
34137             Roo.get(document.body).addClass("x-body-masked");
34138             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34139             this.mask.show();
34140         }
34141         this.constrainXY();
34142     },
34143
34144     // private
34145     animShow : function(){
34146         var b = Roo.get(this.animateTarget).getBox();
34147         this.proxy.setSize(b.width, b.height);
34148         this.proxy.setLocation(b.x, b.y);
34149         this.proxy.show();
34150         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
34151                     true, .35, this.showEl.createDelegate(this));
34152     },
34153
34154     /**
34155      * Shows the dialog.
34156      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
34157      * @return {Roo.BasicDialog} this
34158      */
34159     show : function(animateTarget){
34160         if (this.fireEvent("beforeshow", this) === false){
34161             return;
34162         }
34163         if(this.syncHeightBeforeShow){
34164             this.syncBodyHeight();
34165         }else if(this.firstShow){
34166             this.firstShow = false;
34167             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
34168         }
34169         this.animateTarget = animateTarget || this.animateTarget;
34170         if(!this.el.isVisible()){
34171             this.beforeShow();
34172             if(this.animateTarget && Roo.get(this.animateTarget)){
34173                 this.animShow();
34174             }else{
34175                 this.showEl();
34176             }
34177         }
34178         return this;
34179     },
34180
34181     // private
34182     showEl : function(){
34183         this.proxy.hide();
34184         this.el.setXY(this.xy);
34185         this.el.show();
34186         this.adjustAssets(true);
34187         this.toFront();
34188         this.focus();
34189         // IE peekaboo bug - fix found by Dave Fenwick
34190         if(Roo.isIE){
34191             this.el.repaint();
34192         }
34193         this.fireEvent("show", this);
34194     },
34195
34196     /**
34197      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
34198      * dialog itself will receive focus.
34199      */
34200     focus : function(){
34201         if(this.defaultButton){
34202             this.defaultButton.focus();
34203         }else{
34204             this.focusEl.focus();
34205         }
34206     },
34207
34208     // private
34209     constrainXY : function(){
34210         if(this.constraintoviewport !== false){
34211             if(!this.viewSize){
34212                 if(this.container){
34213                     var s = this.container.getSize();
34214                     this.viewSize = [s.width, s.height];
34215                 }else{
34216                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
34217                 }
34218             }
34219             var s = Roo.get(this.container||document).getScroll();
34220
34221             var x = this.xy[0], y = this.xy[1];
34222             var w = this.size.width, h = this.size.height;
34223             var vw = this.viewSize[0], vh = this.viewSize[1];
34224             // only move it if it needs it
34225             var moved = false;
34226             // first validate right/bottom
34227             if(x + w > vw+s.left){
34228                 x = vw - w;
34229                 moved = true;
34230             }
34231             if(y + h > vh+s.top){
34232                 y = vh - h;
34233                 moved = true;
34234             }
34235             // then make sure top/left isn't negative
34236             if(x < s.left){
34237                 x = s.left;
34238                 moved = true;
34239             }
34240             if(y < s.top){
34241                 y = s.top;
34242                 moved = true;
34243             }
34244             if(moved){
34245                 // cache xy
34246                 this.xy = [x, y];
34247                 if(this.isVisible()){
34248                     this.el.setLocation(x, y);
34249                     this.adjustAssets();
34250                 }
34251             }
34252         }
34253     },
34254
34255     // private
34256     onDrag : function(){
34257         if(!this.proxyDrag){
34258             this.xy = this.el.getXY();
34259             this.adjustAssets();
34260         }
34261     },
34262
34263     // private
34264     adjustAssets : function(doShow){
34265         var x = this.xy[0], y = this.xy[1];
34266         var w = this.size.width, h = this.size.height;
34267         if(doShow === true){
34268             if(this.shadow){
34269                 this.shadow.show(this.el);
34270             }
34271             if(this.shim){
34272                 this.shim.show();
34273             }
34274         }
34275         if(this.shadow && this.shadow.isVisible()){
34276             this.shadow.show(this.el);
34277         }
34278         if(this.shim && this.shim.isVisible()){
34279             this.shim.setBounds(x, y, w, h);
34280         }
34281     },
34282
34283     // private
34284     adjustViewport : function(w, h){
34285         if(!w || !h){
34286             w = Roo.lib.Dom.getViewWidth();
34287             h = Roo.lib.Dom.getViewHeight();
34288         }
34289         // cache the size
34290         this.viewSize = [w, h];
34291         if(this.modal && this.mask.isVisible()){
34292             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34293             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34294         }
34295         if(this.isVisible()){
34296             this.constrainXY();
34297         }
34298     },
34299
34300     /**
34301      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34302      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34303      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34304      */
34305     destroy : function(removeEl){
34306         if(this.isVisible()){
34307             this.animateTarget = null;
34308             this.hide();
34309         }
34310         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34311         if(this.tabs){
34312             this.tabs.destroy(removeEl);
34313         }
34314         Roo.destroy(
34315              this.shim,
34316              this.proxy,
34317              this.resizer,
34318              this.close,
34319              this.mask
34320         );
34321         if(this.dd){
34322             this.dd.unreg();
34323         }
34324         if(this.buttons){
34325            for(var i = 0, len = this.buttons.length; i < len; i++){
34326                this.buttons[i].destroy();
34327            }
34328         }
34329         this.el.removeAllListeners();
34330         if(removeEl === true){
34331             this.el.update("");
34332             this.el.remove();
34333         }
34334         Roo.DialogManager.unregister(this);
34335     },
34336
34337     // private
34338     startMove : function(){
34339         if(this.proxyDrag){
34340             this.proxy.show();
34341         }
34342         if(this.constraintoviewport !== false){
34343             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34344         }
34345     },
34346
34347     // private
34348     endMove : function(){
34349         if(!this.proxyDrag){
34350             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34351         }else{
34352             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34353             this.proxy.hide();
34354         }
34355         this.refreshSize();
34356         this.adjustAssets();
34357         this.focus();
34358         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34359     },
34360
34361     /**
34362      * Brings this dialog to the front of any other visible dialogs
34363      * @return {Roo.BasicDialog} this
34364      */
34365     toFront : function(){
34366         Roo.DialogManager.bringToFront(this);
34367         return this;
34368     },
34369
34370     /**
34371      * Sends this dialog to the back (under) of any other visible dialogs
34372      * @return {Roo.BasicDialog} this
34373      */
34374     toBack : function(){
34375         Roo.DialogManager.sendToBack(this);
34376         return this;
34377     },
34378
34379     /**
34380      * Centers this dialog in the viewport
34381      * @return {Roo.BasicDialog} this
34382      */
34383     center : function(){
34384         var xy = this.el.getCenterXY(true);
34385         this.moveTo(xy[0], xy[1]);
34386         return this;
34387     },
34388
34389     /**
34390      * Moves the dialog's top-left corner to the specified point
34391      * @param {Number} x
34392      * @param {Number} y
34393      * @return {Roo.BasicDialog} this
34394      */
34395     moveTo : function(x, y){
34396         this.xy = [x,y];
34397         if(this.isVisible()){
34398             this.el.setXY(this.xy);
34399             this.adjustAssets();
34400         }
34401         return this;
34402     },
34403
34404     /**
34405      * Aligns the dialog to the specified element
34406      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34407      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34408      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34409      * @return {Roo.BasicDialog} this
34410      */
34411     alignTo : function(element, position, offsets){
34412         this.xy = this.el.getAlignToXY(element, position, offsets);
34413         if(this.isVisible()){
34414             this.el.setXY(this.xy);
34415             this.adjustAssets();
34416         }
34417         return this;
34418     },
34419
34420     /**
34421      * Anchors an element to another element and realigns it when the window is resized.
34422      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34423      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34424      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34425      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34426      * is a number, it is used as the buffer delay (defaults to 50ms).
34427      * @return {Roo.BasicDialog} this
34428      */
34429     anchorTo : function(el, alignment, offsets, monitorScroll){
34430         var action = function(){
34431             this.alignTo(el, alignment, offsets);
34432         };
34433         Roo.EventManager.onWindowResize(action, this);
34434         var tm = typeof monitorScroll;
34435         if(tm != 'undefined'){
34436             Roo.EventManager.on(window, 'scroll', action, this,
34437                 {buffer: tm == 'number' ? monitorScroll : 50});
34438         }
34439         action.call(this);
34440         return this;
34441     },
34442
34443     /**
34444      * Returns true if the dialog is visible
34445      * @return {Boolean}
34446      */
34447     isVisible : function(){
34448         return this.el.isVisible();
34449     },
34450
34451     // private
34452     animHide : function(callback){
34453         var b = Roo.get(this.animateTarget).getBox();
34454         this.proxy.show();
34455         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34456         this.el.hide();
34457         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34458                     this.hideEl.createDelegate(this, [callback]));
34459     },
34460
34461     /**
34462      * Hides the dialog.
34463      * @param {Function} callback (optional) Function to call when the dialog is hidden
34464      * @return {Roo.BasicDialog} this
34465      */
34466     hide : function(callback){
34467         if (this.fireEvent("beforehide", this) === false){
34468             return;
34469         }
34470         if(this.shadow){
34471             this.shadow.hide();
34472         }
34473         if(this.shim) {
34474           this.shim.hide();
34475         }
34476         // sometimes animateTarget seems to get set.. causing problems...
34477         // this just double checks..
34478         if(this.animateTarget && Roo.get(this.animateTarget)) {
34479            this.animHide(callback);
34480         }else{
34481             this.el.hide();
34482             this.hideEl(callback);
34483         }
34484         return this;
34485     },
34486
34487     // private
34488     hideEl : function(callback){
34489         this.proxy.hide();
34490         if(this.modal){
34491             this.mask.hide();
34492             Roo.get(document.body).removeClass("x-body-masked");
34493         }
34494         this.fireEvent("hide", this);
34495         if(typeof callback == "function"){
34496             callback();
34497         }
34498     },
34499
34500     // private
34501     hideAction : function(){
34502         this.setLeft("-10000px");
34503         this.setTop("-10000px");
34504         this.setStyle("visibility", "hidden");
34505     },
34506
34507     // private
34508     refreshSize : function(){
34509         this.size = this.el.getSize();
34510         this.xy = this.el.getXY();
34511         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34512     },
34513
34514     // private
34515     // z-index is managed by the DialogManager and may be overwritten at any time
34516     setZIndex : function(index){
34517         if(this.modal){
34518             this.mask.setStyle("z-index", index);
34519         }
34520         if(this.shim){
34521             this.shim.setStyle("z-index", ++index);
34522         }
34523         if(this.shadow){
34524             this.shadow.setZIndex(++index);
34525         }
34526         this.el.setStyle("z-index", ++index);
34527         if(this.proxy){
34528             this.proxy.setStyle("z-index", ++index);
34529         }
34530         if(this.resizer){
34531             this.resizer.proxy.setStyle("z-index", ++index);
34532         }
34533
34534         this.lastZIndex = index;
34535     },
34536
34537     /**
34538      * Returns the element for this dialog
34539      * @return {Roo.Element} The underlying dialog Element
34540      */
34541     getEl : function(){
34542         return this.el;
34543     }
34544 });
34545
34546 /**
34547  * @class Roo.DialogManager
34548  * Provides global access to BasicDialogs that have been created and
34549  * support for z-indexing (layering) multiple open dialogs.
34550  */
34551 Roo.DialogManager = function(){
34552     var list = {};
34553     var accessList = [];
34554     var front = null;
34555
34556     // private
34557     var sortDialogs = function(d1, d2){
34558         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34559     };
34560
34561     // private
34562     var orderDialogs = function(){
34563         accessList.sort(sortDialogs);
34564         var seed = Roo.DialogManager.zseed;
34565         for(var i = 0, len = accessList.length; i < len; i++){
34566             var dlg = accessList[i];
34567             if(dlg){
34568                 dlg.setZIndex(seed + (i*10));
34569             }
34570         }
34571     };
34572
34573     return {
34574         /**
34575          * The starting z-index for BasicDialogs (defaults to 9000)
34576          * @type Number The z-index value
34577          */
34578         zseed : 9000,
34579
34580         // private
34581         register : function(dlg){
34582             list[dlg.id] = dlg;
34583             accessList.push(dlg);
34584         },
34585
34586         // private
34587         unregister : function(dlg){
34588             delete list[dlg.id];
34589             var i=0;
34590             var len=0;
34591             if(!accessList.indexOf){
34592                 for(  i = 0, len = accessList.length; i < len; i++){
34593                     if(accessList[i] == dlg){
34594                         accessList.splice(i, 1);
34595                         return;
34596                     }
34597                 }
34598             }else{
34599                  i = accessList.indexOf(dlg);
34600                 if(i != -1){
34601                     accessList.splice(i, 1);
34602                 }
34603             }
34604         },
34605
34606         /**
34607          * Gets a registered dialog by id
34608          * @param {String/Object} id The id of the dialog or a dialog
34609          * @return {Roo.BasicDialog} this
34610          */
34611         get : function(id){
34612             return typeof id == "object" ? id : list[id];
34613         },
34614
34615         /**
34616          * Brings the specified dialog to the front
34617          * @param {String/Object} dlg The id of the dialog or a dialog
34618          * @return {Roo.BasicDialog} this
34619          */
34620         bringToFront : function(dlg){
34621             dlg = this.get(dlg);
34622             if(dlg != front){
34623                 front = dlg;
34624                 dlg._lastAccess = new Date().getTime();
34625                 orderDialogs();
34626             }
34627             return dlg;
34628         },
34629
34630         /**
34631          * Sends the specified dialog to the back
34632          * @param {String/Object} dlg The id of the dialog or a dialog
34633          * @return {Roo.BasicDialog} this
34634          */
34635         sendToBack : function(dlg){
34636             dlg = this.get(dlg);
34637             dlg._lastAccess = -(new Date().getTime());
34638             orderDialogs();
34639             return dlg;
34640         },
34641
34642         /**
34643          * Hides all dialogs
34644          */
34645         hideAll : function(){
34646             for(var id in list){
34647                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34648                     list[id].hide();
34649                 }
34650             }
34651         }
34652     };
34653 }();
34654
34655 /**
34656  * @class Roo.LayoutDialog
34657  * @extends Roo.BasicDialog
34658  * @children Roo.ContentPanel
34659  * @parent builder none
34660  * Dialog which provides adjustments for working with a layout in a Dialog.
34661  * Add your necessary layout config options to the dialog's config.<br>
34662  * Example usage (including a nested layout):
34663  * <pre><code>
34664 if(!dialog){
34665     dialog = new Roo.LayoutDialog("download-dlg", {
34666         modal: true,
34667         width:600,
34668         height:450,
34669         shadow:true,
34670         minWidth:500,
34671         minHeight:350,
34672         autoTabs:true,
34673         proxyDrag:true,
34674         // layout config merges with the dialog config
34675         center:{
34676             tabPosition: "top",
34677             alwaysShowTabs: true
34678         }
34679     });
34680     dialog.addKeyListener(27, dialog.hide, dialog);
34681     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34682     dialog.addButton("Build It!", this.getDownload, this);
34683
34684     // we can even add nested layouts
34685     var innerLayout = new Roo.BorderLayout("dl-inner", {
34686         east: {
34687             initialSize: 200,
34688             autoScroll:true,
34689             split:true
34690         },
34691         center: {
34692             autoScroll:true
34693         }
34694     });
34695     innerLayout.beginUpdate();
34696     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34697     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34698     innerLayout.endUpdate(true);
34699
34700     var layout = dialog.getLayout();
34701     layout.beginUpdate();
34702     layout.add("center", new Roo.ContentPanel("standard-panel",
34703                         {title: "Download the Source", fitToFrame:true}));
34704     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34705                {title: "Build your own roo.js"}));
34706     layout.getRegion("center").showPanel(sp);
34707     layout.endUpdate();
34708 }
34709 </code></pre>
34710     * @constructor
34711     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34712     * @param {Object} config configuration options
34713   */
34714 Roo.LayoutDialog = function(el, cfg){
34715     
34716     var config=  cfg;
34717     if (typeof(cfg) == 'undefined') {
34718         config = Roo.apply({}, el);
34719         // not sure why we use documentElement here.. - it should always be body.
34720         // IE7 borks horribly if we use documentElement.
34721         // webkit also does not like documentElement - it creates a body element...
34722         el = Roo.get( document.body || document.documentElement ).createChild();
34723         //config.autoCreate = true;
34724     }
34725     
34726     
34727     config.autoTabs = false;
34728     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34729     this.body.setStyle({overflow:"hidden", position:"relative"});
34730     this.layout = new Roo.BorderLayout(this.body.dom, config);
34731     this.layout.monitorWindowResize = false;
34732     this.el.addClass("x-dlg-auto-layout");
34733     // fix case when center region overwrites center function
34734     this.center = Roo.BasicDialog.prototype.center;
34735     this.on("show", this.layout.layout, this.layout, true);
34736     if (config.items) {
34737         var xitems = config.items;
34738         delete config.items;
34739         Roo.each(xitems, this.addxtype, this);
34740     }
34741     
34742     
34743 };
34744 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34745     
34746     
34747     /**
34748      * @cfg {Roo.LayoutRegion} east  
34749      */
34750     /**
34751      * @cfg {Roo.LayoutRegion} west
34752      */
34753     /**
34754      * @cfg {Roo.LayoutRegion} south
34755      */
34756     /**
34757      * @cfg {Roo.LayoutRegion} north
34758      */
34759     /**
34760      * @cfg {Roo.LayoutRegion} center
34761      */
34762     /**
34763      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34764      */
34765     
34766     
34767     /**
34768      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34769      * @deprecated
34770      */
34771     endUpdate : function(){
34772         this.layout.endUpdate();
34773     },
34774
34775     /**
34776      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34777      *  @deprecated
34778      */
34779     beginUpdate : function(){
34780         this.layout.beginUpdate();
34781     },
34782
34783     /**
34784      * Get the BorderLayout for this dialog
34785      * @return {Roo.BorderLayout}
34786      */
34787     getLayout : function(){
34788         return this.layout;
34789     },
34790
34791     showEl : function(){
34792         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34793         if(Roo.isIE7){
34794             this.layout.layout();
34795         }
34796     },
34797
34798     // private
34799     // Use the syncHeightBeforeShow config option to control this automatically
34800     syncBodyHeight : function(){
34801         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34802         if(this.layout){this.layout.layout();}
34803     },
34804     
34805       /**
34806      * Add an xtype element (actually adds to the layout.)
34807      * @return {Object} xdata xtype object data.
34808      */
34809     
34810     addxtype : function(c) {
34811         return this.layout.addxtype(c);
34812     }
34813 });/*
34814  * Based on:
34815  * Ext JS Library 1.1.1
34816  * Copyright(c) 2006-2007, Ext JS, LLC.
34817  *
34818  * Originally Released Under LGPL - original licence link has changed is not relivant.
34819  *
34820  * Fork - LGPL
34821  * <script type="text/javascript">
34822  */
34823  
34824 /**
34825  * @class Roo.MessageBox
34826  * @static
34827  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34828  * Example usage:
34829  *<pre><code>
34830 // Basic alert:
34831 Roo.Msg.alert('Status', 'Changes saved successfully.');
34832
34833 // Prompt for user data:
34834 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34835     if (btn == 'ok'){
34836         // process text value...
34837     }
34838 });
34839
34840 // Show a dialog using config options:
34841 Roo.Msg.show({
34842    title:'Save Changes?',
34843    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34844    buttons: Roo.Msg.YESNOCANCEL,
34845    fn: processResult,
34846    animEl: 'elId'
34847 });
34848 </code></pre>
34849  * @static
34850  */
34851 Roo.MessageBox = function(){
34852     var dlg, opt, mask, waitTimer;
34853     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34854     var buttons, activeTextEl, bwidth;
34855
34856     // private
34857     var handleButton = function(button){
34858         dlg.hide();
34859         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34860     };
34861
34862     // private
34863     var handleHide = function(){
34864         if(opt && opt.cls){
34865             dlg.el.removeClass(opt.cls);
34866         }
34867         if(waitTimer){
34868             Roo.TaskMgr.stop(waitTimer);
34869             waitTimer = null;
34870         }
34871     };
34872
34873     // private
34874     var updateButtons = function(b){
34875         var width = 0;
34876         if(!b){
34877             buttons["ok"].hide();
34878             buttons["cancel"].hide();
34879             buttons["yes"].hide();
34880             buttons["no"].hide();
34881             dlg.footer.dom.style.display = 'none';
34882             return width;
34883         }
34884         dlg.footer.dom.style.display = '';
34885         for(var k in buttons){
34886             if(typeof buttons[k] != "function"){
34887                 if(b[k]){
34888                     buttons[k].show();
34889                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34890                     width += buttons[k].el.getWidth()+15;
34891                 }else{
34892                     buttons[k].hide();
34893                 }
34894             }
34895         }
34896         return width;
34897     };
34898
34899     // private
34900     var handleEsc = function(d, k, e){
34901         if(opt && opt.closable !== false){
34902             dlg.hide();
34903         }
34904         if(e){
34905             e.stopEvent();
34906         }
34907     };
34908
34909     return {
34910         /**
34911          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34912          * @return {Roo.BasicDialog} The BasicDialog element
34913          */
34914         getDialog : function(){
34915            if(!dlg){
34916                 dlg = new Roo.BasicDialog("x-msg-box", {
34917                     autoCreate : true,
34918                     shadow: true,
34919                     draggable: true,
34920                     resizable:false,
34921                     constraintoviewport:false,
34922                     fixedcenter:true,
34923                     collapsible : false,
34924                     shim:true,
34925                     modal: true,
34926                     width:400, height:100,
34927                     buttonAlign:"center",
34928                     closeClick : function(){
34929                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34930                             handleButton("no");
34931                         }else{
34932                             handleButton("cancel");
34933                         }
34934                     }
34935                 });
34936               
34937                 dlg.on("hide", handleHide);
34938                 mask = dlg.mask;
34939                 dlg.addKeyListener(27, handleEsc);
34940                 buttons = {};
34941                 var bt = this.buttonText;
34942                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34943                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34944                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34945                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34946                 bodyEl = dlg.body.createChild({
34947
34948                     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>'
34949                 });
34950                 msgEl = bodyEl.dom.firstChild;
34951                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34952                 textboxEl.enableDisplayMode();
34953                 textboxEl.addKeyListener([10,13], function(){
34954                     if(dlg.isVisible() && opt && opt.buttons){
34955                         if(opt.buttons.ok){
34956                             handleButton("ok");
34957                         }else if(opt.buttons.yes){
34958                             handleButton("yes");
34959                         }
34960                     }
34961                 });
34962                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34963                 textareaEl.enableDisplayMode();
34964                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34965                 progressEl.enableDisplayMode();
34966                 var pf = progressEl.dom.firstChild;
34967                 if (pf) {
34968                     pp = Roo.get(pf.firstChild);
34969                     pp.setHeight(pf.offsetHeight);
34970                 }
34971                 
34972             }
34973             return dlg;
34974         },
34975
34976         /**
34977          * Updates the message box body text
34978          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34979          * the XHTML-compliant non-breaking space character '&amp;#160;')
34980          * @return {Roo.MessageBox} This message box
34981          */
34982         updateText : function(text){
34983             if(!dlg.isVisible() && !opt.width){
34984                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34985             }
34986             msgEl.innerHTML = text || '&#160;';
34987       
34988             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34989             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34990             var w = Math.max(
34991                     Math.min(opt.width || cw , this.maxWidth), 
34992                     Math.max(opt.minWidth || this.minWidth, bwidth)
34993             );
34994             if(opt.prompt){
34995                 activeTextEl.setWidth(w);
34996             }
34997             if(dlg.isVisible()){
34998                 dlg.fixedcenter = false;
34999             }
35000             // to big, make it scroll. = But as usual stupid IE does not support
35001             // !important..
35002             
35003             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
35004                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
35005                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
35006             } else {
35007                 bodyEl.dom.style.height = '';
35008                 bodyEl.dom.style.overflowY = '';
35009             }
35010             if (cw > w) {
35011                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
35012             } else {
35013                 bodyEl.dom.style.overflowX = '';
35014             }
35015             
35016             dlg.setContentSize(w, bodyEl.getHeight());
35017             if(dlg.isVisible()){
35018                 dlg.fixedcenter = true;
35019             }
35020             return this;
35021         },
35022
35023         /**
35024          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
35025          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
35026          * @param {Number} value Any number between 0 and 1 (e.g., .5)
35027          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
35028          * @return {Roo.MessageBox} This message box
35029          */
35030         updateProgress : function(value, text){
35031             if(text){
35032                 this.updateText(text);
35033             }
35034             if (pp) { // weird bug on my firefox - for some reason this is not defined
35035                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
35036             }
35037             return this;
35038         },        
35039
35040         /**
35041          * Returns true if the message box is currently displayed
35042          * @return {Boolean} True if the message box is visible, else false
35043          */
35044         isVisible : function(){
35045             return dlg && dlg.isVisible();  
35046         },
35047
35048         /**
35049          * Hides the message box if it is displayed
35050          */
35051         hide : function(){
35052             if(this.isVisible()){
35053                 dlg.hide();
35054             }  
35055         },
35056
35057         /**
35058          * Displays a new message box, or reinitializes an existing message box, based on the config options
35059          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
35060          * The following config object properties are supported:
35061          * <pre>
35062 Property    Type             Description
35063 ----------  ---------------  ------------------------------------------------------------------------------------
35064 animEl            String/Element   An id or Element from which the message box should animate as it opens and
35065                                    closes (defaults to undefined)
35066 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
35067                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
35068 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
35069                                    progress and wait dialogs will ignore this property and always hide the
35070                                    close button as they can only be closed programmatically.
35071 cls               String           A custom CSS class to apply to the message box element
35072 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
35073                                    displayed (defaults to 75)
35074 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
35075                                    function will be btn (the name of the button that was clicked, if applicable,
35076                                    e.g. "ok"), and text (the value of the active text field, if applicable).
35077                                    Progress and wait dialogs will ignore this option since they do not respond to
35078                                    user actions and can only be closed programmatically, so any required function
35079                                    should be called by the same code after it closes the dialog.
35080 icon              String           A CSS class that provides a background image to be used as an icon for
35081                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
35082 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
35083 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
35084 modal             Boolean          False to allow user interaction with the page while the message box is
35085                                    displayed (defaults to true)
35086 msg               String           A string that will replace the existing message box body text (defaults
35087                                    to the XHTML-compliant non-breaking space character '&#160;')
35088 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
35089 progress          Boolean          True to display a progress bar (defaults to false)
35090 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
35091 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
35092 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
35093 title             String           The title text
35094 value             String           The string value to set into the active textbox element if displayed
35095 wait              Boolean          True to display a progress bar (defaults to false)
35096 width             Number           The width of the dialog in pixels
35097 </pre>
35098          *
35099          * Example usage:
35100          * <pre><code>
35101 Roo.Msg.show({
35102    title: 'Address',
35103    msg: 'Please enter your address:',
35104    width: 300,
35105    buttons: Roo.MessageBox.OKCANCEL,
35106    multiline: true,
35107    fn: saveAddress,
35108    animEl: 'addAddressBtn'
35109 });
35110 </code></pre>
35111          * @param {Object} config Configuration options
35112          * @return {Roo.MessageBox} This message box
35113          */
35114         show : function(options)
35115         {
35116             
35117             // this causes nightmares if you show one dialog after another
35118             // especially on callbacks..
35119              
35120             if(this.isVisible()){
35121                 
35122                 this.hide();
35123                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
35124                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
35125                 Roo.log("New Dialog Message:" +  options.msg )
35126                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
35127                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
35128                 
35129             }
35130             var d = this.getDialog();
35131             opt = options;
35132             d.setTitle(opt.title || "&#160;");
35133             d.close.setDisplayed(opt.closable !== false);
35134             activeTextEl = textboxEl;
35135             opt.prompt = opt.prompt || (opt.multiline ? true : false);
35136             if(opt.prompt){
35137                 if(opt.multiline){
35138                     textboxEl.hide();
35139                     textareaEl.show();
35140                     textareaEl.setHeight(typeof opt.multiline == "number" ?
35141                         opt.multiline : this.defaultTextHeight);
35142                     activeTextEl = textareaEl;
35143                 }else{
35144                     textboxEl.show();
35145                     textareaEl.hide();
35146                 }
35147             }else{
35148                 textboxEl.hide();
35149                 textareaEl.hide();
35150             }
35151             progressEl.setDisplayed(opt.progress === true);
35152             this.updateProgress(0);
35153             activeTextEl.dom.value = opt.value || "";
35154             if(opt.prompt){
35155                 dlg.setDefaultButton(activeTextEl);
35156             }else{
35157                 var bs = opt.buttons;
35158                 var db = null;
35159                 if(bs && bs.ok){
35160                     db = buttons["ok"];
35161                 }else if(bs && bs.yes){
35162                     db = buttons["yes"];
35163                 }
35164                 dlg.setDefaultButton(db);
35165             }
35166             bwidth = updateButtons(opt.buttons);
35167             this.updateText(opt.msg);
35168             if(opt.cls){
35169                 d.el.addClass(opt.cls);
35170             }
35171             d.proxyDrag = opt.proxyDrag === true;
35172             d.modal = opt.modal !== false;
35173             d.mask = opt.modal !== false ? mask : false;
35174             if(!d.isVisible()){
35175                 // force it to the end of the z-index stack so it gets a cursor in FF
35176                 document.body.appendChild(dlg.el.dom);
35177                 d.animateTarget = null;
35178                 d.show(options.animEl);
35179             }
35180             dlg.toFront();
35181             return this;
35182         },
35183
35184         /**
35185          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
35186          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
35187          * and closing the message box when the process is complete.
35188          * @param {String} title The title bar text
35189          * @param {String} msg The message box body text
35190          * @return {Roo.MessageBox} This message box
35191          */
35192         progress : function(title, msg){
35193             this.show({
35194                 title : title,
35195                 msg : msg,
35196                 buttons: false,
35197                 progress:true,
35198                 closable:false,
35199                 minWidth: this.minProgressWidth,
35200                 modal : true
35201             });
35202             return this;
35203         },
35204
35205         /**
35206          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
35207          * If a callback function is passed it will be called after the user clicks the button, and the
35208          * id of the button that was clicked will be passed as the only parameter to the callback
35209          * (could also be the top-right close button).
35210          * @param {String} title The title bar text
35211          * @param {String} msg The message box body text
35212          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35213          * @param {Object} scope (optional) The scope of the callback function
35214          * @return {Roo.MessageBox} This message box
35215          */
35216         alert : function(title, msg, fn, scope){
35217             this.show({
35218                 title : title,
35219                 msg : msg,
35220                 buttons: this.OK,
35221                 fn: fn,
35222                 scope : scope,
35223                 modal : true
35224             });
35225             return this;
35226         },
35227
35228         /**
35229          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
35230          * interaction while waiting for a long-running process to complete that does not have defined intervals.
35231          * You are responsible for closing the message box when the process is complete.
35232          * @param {String} msg The message box body text
35233          * @param {String} title (optional) The title bar text
35234          * @return {Roo.MessageBox} This message box
35235          */
35236         wait : function(msg, title){
35237             this.show({
35238                 title : title,
35239                 msg : msg,
35240                 buttons: false,
35241                 closable:false,
35242                 progress:true,
35243                 modal:true,
35244                 width:300,
35245                 wait:true
35246             });
35247             waitTimer = Roo.TaskMgr.start({
35248                 run: function(i){
35249                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35250                 },
35251                 interval: 1000
35252             });
35253             return this;
35254         },
35255
35256         /**
35257          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35258          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35259          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
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          * @return {Roo.MessageBox} This message box
35265          */
35266         confirm : function(title, msg, fn, scope){
35267             this.show({
35268                 title : title,
35269                 msg : msg,
35270                 buttons: this.YESNO,
35271                 fn: fn,
35272                 scope : scope,
35273                 modal : true
35274             });
35275             return this;
35276         },
35277
35278         /**
35279          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35280          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35281          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35282          * (could also be the top-right close button) and the text that was entered will be passed as the two
35283          * parameters to the callback.
35284          * @param {String} title The title bar text
35285          * @param {String} msg The message box body text
35286          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35287          * @param {Object} scope (optional) The scope of the callback function
35288          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35289          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35290          * @return {Roo.MessageBox} This message box
35291          */
35292         prompt : function(title, msg, fn, scope, multiline){
35293             this.show({
35294                 title : title,
35295                 msg : msg,
35296                 buttons: this.OKCANCEL,
35297                 fn: fn,
35298                 minWidth:250,
35299                 scope : scope,
35300                 prompt:true,
35301                 multiline: multiline,
35302                 modal : true
35303             });
35304             return this;
35305         },
35306
35307         /**
35308          * Button config that displays a single OK button
35309          * @type Object
35310          */
35311         OK : {ok:true},
35312         /**
35313          * Button config that displays Yes and No buttons
35314          * @type Object
35315          */
35316         YESNO : {yes:true, no:true},
35317         /**
35318          * Button config that displays OK and Cancel buttons
35319          * @type Object
35320          */
35321         OKCANCEL : {ok:true, cancel:true},
35322         /**
35323          * Button config that displays Yes, No and Cancel buttons
35324          * @type Object
35325          */
35326         YESNOCANCEL : {yes:true, no:true, cancel:true},
35327
35328         /**
35329          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35330          * @type Number
35331          */
35332         defaultTextHeight : 75,
35333         /**
35334          * The maximum width in pixels of the message box (defaults to 600)
35335          * @type Number
35336          */
35337         maxWidth : 600,
35338         /**
35339          * The minimum width in pixels of the message box (defaults to 100)
35340          * @type Number
35341          */
35342         minWidth : 100,
35343         /**
35344          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35345          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35346          * @type Number
35347          */
35348         minProgressWidth : 250,
35349         /**
35350          * An object containing the default button text strings that can be overriden for localized language support.
35351          * Supported properties are: ok, cancel, yes and no.
35352          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35353          * @type Object
35354          */
35355         buttonText : {
35356             ok : "OK",
35357             cancel : "Cancel",
35358             yes : "Yes",
35359             no : "No"
35360         }
35361     };
35362 }();
35363
35364 /**
35365  * Shorthand for {@link Roo.MessageBox}
35366  */
35367 Roo.Msg = Roo.MessageBox;/*
35368  * Based on:
35369  * Ext JS Library 1.1.1
35370  * Copyright(c) 2006-2007, Ext JS, LLC.
35371  *
35372  * Originally Released Under LGPL - original licence link has changed is not relivant.
35373  *
35374  * Fork - LGPL
35375  * <script type="text/javascript">
35376  */
35377 /**
35378  * @class Roo.QuickTips
35379  * Provides attractive and customizable tooltips for any element.
35380  * @static
35381  */
35382 Roo.QuickTips = function(){
35383     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35384     var ce, bd, xy, dd;
35385     var visible = false, disabled = true, inited = false;
35386     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35387     
35388     var onOver = function(e){
35389         if(disabled){
35390             return;
35391         }
35392         var t = e.getTarget();
35393         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35394             return;
35395         }
35396         if(ce && t == ce.el){
35397             clearTimeout(hideProc);
35398             return;
35399         }
35400         if(t && tagEls[t.id]){
35401             tagEls[t.id].el = t;
35402             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35403             return;
35404         }
35405         var ttp, et = Roo.fly(t);
35406         var ns = cfg.namespace;
35407         if(tm.interceptTitles && t.title){
35408             ttp = t.title;
35409             t.qtip = ttp;
35410             t.removeAttribute("title");
35411             e.preventDefault();
35412         }else{
35413             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35414         }
35415         if(ttp){
35416             showProc = show.defer(tm.showDelay, tm, [{
35417                 el: t, 
35418                 text: ttp.replace(/\\n/g,'<br/>'),
35419                 width: et.getAttributeNS(ns, cfg.width),
35420                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35421                 title: et.getAttributeNS(ns, cfg.title),
35422                     cls: et.getAttributeNS(ns, cfg.cls)
35423             }]);
35424         }
35425     };
35426     
35427     var onOut = function(e){
35428         clearTimeout(showProc);
35429         var t = e.getTarget();
35430         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35431             hideProc = setTimeout(hide, tm.hideDelay);
35432         }
35433     };
35434     
35435     var onMove = function(e){
35436         if(disabled){
35437             return;
35438         }
35439         xy = e.getXY();
35440         xy[1] += 18;
35441         if(tm.trackMouse && ce){
35442             el.setXY(xy);
35443         }
35444     };
35445     
35446     var onDown = function(e){
35447         clearTimeout(showProc);
35448         clearTimeout(hideProc);
35449         if(!e.within(el)){
35450             if(tm.hideOnClick){
35451                 hide();
35452                 tm.disable();
35453                 tm.enable.defer(100, tm);
35454             }
35455         }
35456     };
35457     
35458     var getPad = function(){
35459         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35460     };
35461
35462     var show = function(o){
35463         if(disabled){
35464             return;
35465         }
35466         clearTimeout(dismissProc);
35467         ce = o;
35468         if(removeCls){ // in case manually hidden
35469             el.removeClass(removeCls);
35470             removeCls = null;
35471         }
35472         if(ce.cls){
35473             el.addClass(ce.cls);
35474             removeCls = ce.cls;
35475         }
35476         if(ce.title){
35477             tipTitle.update(ce.title);
35478             tipTitle.show();
35479         }else{
35480             tipTitle.update('');
35481             tipTitle.hide();
35482         }
35483         el.dom.style.width  = tm.maxWidth+'px';
35484         //tipBody.dom.style.width = '';
35485         tipBodyText.update(o.text);
35486         var p = getPad(), w = ce.width;
35487         if(!w){
35488             var td = tipBodyText.dom;
35489             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35490             if(aw > tm.maxWidth){
35491                 w = tm.maxWidth;
35492             }else if(aw < tm.minWidth){
35493                 w = tm.minWidth;
35494             }else{
35495                 w = aw;
35496             }
35497         }
35498         //tipBody.setWidth(w);
35499         el.setWidth(parseInt(w, 10) + p);
35500         if(ce.autoHide === false){
35501             close.setDisplayed(true);
35502             if(dd){
35503                 dd.unlock();
35504             }
35505         }else{
35506             close.setDisplayed(false);
35507             if(dd){
35508                 dd.lock();
35509             }
35510         }
35511         if(xy){
35512             el.avoidY = xy[1]-18;
35513             el.setXY(xy);
35514         }
35515         if(tm.animate){
35516             el.setOpacity(.1);
35517             el.setStyle("visibility", "visible");
35518             el.fadeIn({callback: afterShow});
35519         }else{
35520             afterShow();
35521         }
35522     };
35523     
35524     var afterShow = function(){
35525         if(ce){
35526             el.show();
35527             esc.enable();
35528             if(tm.autoDismiss && ce.autoHide !== false){
35529                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35530             }
35531         }
35532     };
35533     
35534     var hide = function(noanim){
35535         clearTimeout(dismissProc);
35536         clearTimeout(hideProc);
35537         ce = null;
35538         if(el.isVisible()){
35539             esc.disable();
35540             if(noanim !== true && tm.animate){
35541                 el.fadeOut({callback: afterHide});
35542             }else{
35543                 afterHide();
35544             } 
35545         }
35546     };
35547     
35548     var afterHide = function(){
35549         el.hide();
35550         if(removeCls){
35551             el.removeClass(removeCls);
35552             removeCls = null;
35553         }
35554     };
35555     
35556     return {
35557         /**
35558         * @cfg {Number} minWidth
35559         * The minimum width of the quick tip (defaults to 40)
35560         */
35561        minWidth : 40,
35562         /**
35563         * @cfg {Number} maxWidth
35564         * The maximum width of the quick tip (defaults to 300)
35565         */
35566        maxWidth : 300,
35567         /**
35568         * @cfg {Boolean} interceptTitles
35569         * True to automatically use the element's DOM title value if available (defaults to false)
35570         */
35571        interceptTitles : false,
35572         /**
35573         * @cfg {Boolean} trackMouse
35574         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35575         */
35576        trackMouse : false,
35577         /**
35578         * @cfg {Boolean} hideOnClick
35579         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35580         */
35581        hideOnClick : true,
35582         /**
35583         * @cfg {Number} showDelay
35584         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35585         */
35586        showDelay : 500,
35587         /**
35588         * @cfg {Number} hideDelay
35589         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35590         */
35591        hideDelay : 200,
35592         /**
35593         * @cfg {Boolean} autoHide
35594         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35595         * Used in conjunction with hideDelay.
35596         */
35597        autoHide : true,
35598         /**
35599         * @cfg {Boolean}
35600         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35601         * (defaults to true).  Used in conjunction with autoDismissDelay.
35602         */
35603        autoDismiss : true,
35604         /**
35605         * @cfg {Number}
35606         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35607         */
35608        autoDismissDelay : 5000,
35609        /**
35610         * @cfg {Boolean} animate
35611         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35612         */
35613        animate : false,
35614
35615        /**
35616         * @cfg {String} title
35617         * Title text to display (defaults to '').  This can be any valid HTML markup.
35618         */
35619         title: '',
35620        /**
35621         * @cfg {String} text
35622         * Body text to display (defaults to '').  This can be any valid HTML markup.
35623         */
35624         text : '',
35625        /**
35626         * @cfg {String} cls
35627         * A CSS class to apply to the base quick tip element (defaults to '').
35628         */
35629         cls : '',
35630        /**
35631         * @cfg {Number} width
35632         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35633         * minWidth or maxWidth.
35634         */
35635         width : null,
35636
35637     /**
35638      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35639      * or display QuickTips in a page.
35640      */
35641        init : function(){
35642           tm = Roo.QuickTips;
35643           cfg = tm.tagConfig;
35644           if(!inited){
35645               if(!Roo.isReady){ // allow calling of init() before onReady
35646                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35647                   return;
35648               }
35649               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35650               el.fxDefaults = {stopFx: true};
35651               // maximum custom styling
35652               //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>');
35653               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>');              
35654               tipTitle = el.child('h3');
35655               tipTitle.enableDisplayMode("block");
35656               tipBody = el.child('div.x-tip-bd');
35657               tipBodyText = el.child('div.x-tip-bd-inner');
35658               //bdLeft = el.child('div.x-tip-bd-left');
35659               //bdRight = el.child('div.x-tip-bd-right');
35660               close = el.child('div.x-tip-close');
35661               close.enableDisplayMode("block");
35662               close.on("click", hide);
35663               var d = Roo.get(document);
35664               d.on("mousedown", onDown);
35665               d.on("mouseover", onOver);
35666               d.on("mouseout", onOut);
35667               d.on("mousemove", onMove);
35668               esc = d.addKeyListener(27, hide);
35669               esc.disable();
35670               if(Roo.dd.DD){
35671                   dd = el.initDD("default", null, {
35672                       onDrag : function(){
35673                           el.sync();  
35674                       }
35675                   });
35676                   dd.setHandleElId(tipTitle.id);
35677                   dd.lock();
35678               }
35679               inited = true;
35680           }
35681           this.enable(); 
35682        },
35683
35684     /**
35685      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35686      * are supported:
35687      * <pre>
35688 Property    Type                   Description
35689 ----------  ---------------------  ------------------------------------------------------------------------
35690 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35691      * </ul>
35692      * @param {Object} config The config object
35693      */
35694        register : function(config){
35695            var cs = config instanceof Array ? config : arguments;
35696            for(var i = 0, len = cs.length; i < len; i++) {
35697                var c = cs[i];
35698                var target = c.target;
35699                if(target){
35700                    if(target instanceof Array){
35701                        for(var j = 0, jlen = target.length; j < jlen; j++){
35702                            tagEls[target[j]] = c;
35703                        }
35704                    }else{
35705                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35706                    }
35707                }
35708            }
35709        },
35710
35711     /**
35712      * Removes this quick tip from its element and destroys it.
35713      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35714      */
35715        unregister : function(el){
35716            delete tagEls[Roo.id(el)];
35717        },
35718
35719     /**
35720      * Enable this quick tip.
35721      */
35722        enable : function(){
35723            if(inited && disabled){
35724                locks.pop();
35725                if(locks.length < 1){
35726                    disabled = false;
35727                }
35728            }
35729        },
35730
35731     /**
35732      * Disable this quick tip.
35733      */
35734        disable : function(){
35735           disabled = true;
35736           clearTimeout(showProc);
35737           clearTimeout(hideProc);
35738           clearTimeout(dismissProc);
35739           if(ce){
35740               hide(true);
35741           }
35742           locks.push(1);
35743        },
35744
35745     /**
35746      * Returns true if the quick tip is enabled, else false.
35747      */
35748        isEnabled : function(){
35749             return !disabled;
35750        },
35751
35752         // private
35753        tagConfig : {
35754            namespace : "roo", // was ext?? this may break..
35755            alt_namespace : "ext",
35756            attribute : "qtip",
35757            width : "width",
35758            target : "target",
35759            title : "qtitle",
35760            hide : "hide",
35761            cls : "qclass"
35762        }
35763    };
35764 }();
35765
35766 // backwards compat
35767 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35768  * Based on:
35769  * Ext JS Library 1.1.1
35770  * Copyright(c) 2006-2007, Ext JS, LLC.
35771  *
35772  * Originally Released Under LGPL - original licence link has changed is not relivant.
35773  *
35774  * Fork - LGPL
35775  * <script type="text/javascript">
35776  */
35777  
35778
35779 /**
35780  * @class Roo.tree.TreePanel
35781  * @extends Roo.data.Tree
35782  * @cfg {Roo.tree.TreeNode} root The root node
35783  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35784  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35785  * @cfg {Boolean} enableDD true to enable drag and drop
35786  * @cfg {Boolean} enableDrag true to enable just drag
35787  * @cfg {Boolean} enableDrop true to enable just drop
35788  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35789  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35790  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35791  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35792  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35793  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35794  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35795  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35796  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35797  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35798  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35799  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35800  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35801  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35802  * @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>
35803  * @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>
35804  * 
35805  * @constructor
35806  * @param {String/HTMLElement/Element} el The container element
35807  * @param {Object} config
35808  */
35809 Roo.tree.TreePanel = function(el, config){
35810     var root = false;
35811     var loader = false;
35812     if (config.root) {
35813         root = config.root;
35814         delete config.root;
35815     }
35816     if (config.loader) {
35817         loader = config.loader;
35818         delete config.loader;
35819     }
35820     
35821     Roo.apply(this, config);
35822     Roo.tree.TreePanel.superclass.constructor.call(this);
35823     this.el = Roo.get(el);
35824     this.el.addClass('x-tree');
35825     //console.log(root);
35826     if (root) {
35827         this.setRootNode( Roo.factory(root, Roo.tree));
35828     }
35829     if (loader) {
35830         this.loader = Roo.factory(loader, Roo.tree);
35831     }
35832    /**
35833     * Read-only. The id of the container element becomes this TreePanel's id.
35834     */
35835     this.id = this.el.id;
35836     this.addEvents({
35837         /**
35838         * @event beforeload
35839         * Fires before a node is loaded, return false to cancel
35840         * @param {Node} node The node being loaded
35841         */
35842         "beforeload" : true,
35843         /**
35844         * @event load
35845         * Fires when a node is loaded
35846         * @param {Node} node The node that was loaded
35847         */
35848         "load" : true,
35849         /**
35850         * @event textchange
35851         * Fires when the text for a node is changed
35852         * @param {Node} node The node
35853         * @param {String} text The new text
35854         * @param {String} oldText The old text
35855         */
35856         "textchange" : true,
35857         /**
35858         * @event beforeexpand
35859         * Fires before a node is expanded, return false to cancel.
35860         * @param {Node} node The node
35861         * @param {Boolean} deep
35862         * @param {Boolean} anim
35863         */
35864         "beforeexpand" : true,
35865         /**
35866         * @event beforecollapse
35867         * Fires before a node is collapsed, return false to cancel.
35868         * @param {Node} node The node
35869         * @param {Boolean} deep
35870         * @param {Boolean} anim
35871         */
35872         "beforecollapse" : true,
35873         /**
35874         * @event expand
35875         * Fires when a node is expanded
35876         * @param {Node} node The node
35877         */
35878         "expand" : true,
35879         /**
35880         * @event disabledchange
35881         * Fires when the disabled status of a node changes
35882         * @param {Node} node The node
35883         * @param {Boolean} disabled
35884         */
35885         "disabledchange" : true,
35886         /**
35887         * @event collapse
35888         * Fires when a node is collapsed
35889         * @param {Node} node The node
35890         */
35891         "collapse" : true,
35892         /**
35893         * @event beforeclick
35894         * Fires before click processing on a node. Return false to cancel the default action.
35895         * @param {Node} node The node
35896         * @param {Roo.EventObject} e The event object
35897         */
35898         "beforeclick":true,
35899         /**
35900         * @event checkchange
35901         * Fires when a node with a checkbox's checked property changes
35902         * @param {Node} this This node
35903         * @param {Boolean} checked
35904         */
35905         "checkchange":true,
35906         /**
35907         * @event click
35908         * Fires when a node is clicked
35909         * @param {Node} node The node
35910         * @param {Roo.EventObject} e The event object
35911         */
35912         "click":true,
35913         /**
35914         * @event dblclick
35915         * Fires when a node is double clicked
35916         * @param {Node} node The node
35917         * @param {Roo.EventObject} e The event object
35918         */
35919         "dblclick":true,
35920         /**
35921         * @event contextmenu
35922         * Fires when a node is right clicked
35923         * @param {Node} node The node
35924         * @param {Roo.EventObject} e The event object
35925         */
35926         "contextmenu":true,
35927         /**
35928         * @event beforechildrenrendered
35929         * Fires right before the child nodes for a node are rendered
35930         * @param {Node} node The node
35931         */
35932         "beforechildrenrendered":true,
35933         /**
35934         * @event startdrag
35935         * Fires when a node starts being dragged
35936         * @param {Roo.tree.TreePanel} this
35937         * @param {Roo.tree.TreeNode} node
35938         * @param {event} e The raw browser event
35939         */ 
35940        "startdrag" : true,
35941        /**
35942         * @event enddrag
35943         * Fires when a drag operation is complete
35944         * @param {Roo.tree.TreePanel} this
35945         * @param {Roo.tree.TreeNode} node
35946         * @param {event} e The raw browser event
35947         */
35948        "enddrag" : true,
35949        /**
35950         * @event dragdrop
35951         * Fires when a dragged node is dropped on a valid DD target
35952         * @param {Roo.tree.TreePanel} this
35953         * @param {Roo.tree.TreeNode} node
35954         * @param {DD} dd The dd it was dropped on
35955         * @param {event} e The raw browser event
35956         */
35957        "dragdrop" : true,
35958        /**
35959         * @event beforenodedrop
35960         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35961         * passed to handlers has the following properties:<br />
35962         * <ul style="padding:5px;padding-left:16px;">
35963         * <li>tree - The TreePanel</li>
35964         * <li>target - The node being targeted for the drop</li>
35965         * <li>data - The drag data from the drag source</li>
35966         * <li>point - The point of the drop - append, above or below</li>
35967         * <li>source - The drag source</li>
35968         * <li>rawEvent - Raw mouse event</li>
35969         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35970         * to be inserted by setting them on this object.</li>
35971         * <li>cancel - Set this to true to cancel the drop.</li>
35972         * </ul>
35973         * @param {Object} dropEvent
35974         */
35975        "beforenodedrop" : true,
35976        /**
35977         * @event nodedrop
35978         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35979         * passed to handlers has the following properties:<br />
35980         * <ul style="padding:5px;padding-left:16px;">
35981         * <li>tree - The TreePanel</li>
35982         * <li>target - The node being targeted for the drop</li>
35983         * <li>data - The drag data from the drag source</li>
35984         * <li>point - The point of the drop - append, above or below</li>
35985         * <li>source - The drag source</li>
35986         * <li>rawEvent - Raw mouse event</li>
35987         * <li>dropNode - Dropped node(s).</li>
35988         * </ul>
35989         * @param {Object} dropEvent
35990         */
35991        "nodedrop" : true,
35992         /**
35993         * @event nodedragover
35994         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35995         * passed to handlers has the following properties:<br />
35996         * <ul style="padding:5px;padding-left:16px;">
35997         * <li>tree - The TreePanel</li>
35998         * <li>target - The node being targeted for the drop</li>
35999         * <li>data - The drag data from the drag source</li>
36000         * <li>point - The point of the drop - append, above or below</li>
36001         * <li>source - The drag source</li>
36002         * <li>rawEvent - Raw mouse event</li>
36003         * <li>dropNode - Drop node(s) provided by the source.</li>
36004         * <li>cancel - Set this to true to signal drop not allowed.</li>
36005         * </ul>
36006         * @param {Object} dragOverEvent
36007         */
36008        "nodedragover" : true,
36009        /**
36010         * @event appendnode
36011         * Fires when append node to the tree
36012         * @param {Roo.tree.TreePanel} this
36013         * @param {Roo.tree.TreeNode} node
36014         * @param {Number} index The index of the newly appended node
36015         */
36016        "appendnode" : true
36017         
36018     });
36019     if(this.singleExpand){
36020        this.on("beforeexpand", this.restrictExpand, this);
36021     }
36022     if (this.editor) {
36023         this.editor.tree = this;
36024         this.editor = Roo.factory(this.editor, Roo.tree);
36025     }
36026     
36027     if (this.selModel) {
36028         this.selModel = Roo.factory(this.selModel, Roo.tree);
36029     }
36030    
36031 };
36032 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
36033     rootVisible : true,
36034     animate: Roo.enableFx,
36035     lines : true,
36036     enableDD : false,
36037     hlDrop : Roo.enableFx,
36038   
36039     renderer: false,
36040     
36041     rendererTip: false,
36042     // private
36043     restrictExpand : function(node){
36044         var p = node.parentNode;
36045         if(p){
36046             if(p.expandedChild && p.expandedChild.parentNode == p){
36047                 p.expandedChild.collapse();
36048             }
36049             p.expandedChild = node;
36050         }
36051     },
36052
36053     // private override
36054     setRootNode : function(node){
36055         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
36056         if(!this.rootVisible){
36057             node.ui = new Roo.tree.RootTreeNodeUI(node);
36058         }
36059         return node;
36060     },
36061
36062     /**
36063      * Returns the container element for this TreePanel
36064      */
36065     getEl : function(){
36066         return this.el;
36067     },
36068
36069     /**
36070      * Returns the default TreeLoader for this TreePanel
36071      */
36072     getLoader : function(){
36073         return this.loader;
36074     },
36075
36076     /**
36077      * Expand all nodes
36078      */
36079     expandAll : function(){
36080         this.root.expand(true);
36081     },
36082
36083     /**
36084      * Collapse all nodes
36085      */
36086     collapseAll : function(){
36087         this.root.collapse(true);
36088     },
36089
36090     /**
36091      * Returns the selection model used by this TreePanel
36092      */
36093     getSelectionModel : function(){
36094         if(!this.selModel){
36095             this.selModel = new Roo.tree.DefaultSelectionModel();
36096         }
36097         return this.selModel;
36098     },
36099
36100     /**
36101      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
36102      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
36103      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
36104      * @return {Array}
36105      */
36106     getChecked : function(a, startNode){
36107         startNode = startNode || this.root;
36108         var r = [];
36109         var f = function(){
36110             if(this.attributes.checked){
36111                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
36112             }
36113         }
36114         startNode.cascade(f);
36115         return r;
36116     },
36117
36118     /**
36119      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36120      * @param {String} path
36121      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36122      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
36123      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
36124      */
36125     expandPath : function(path, attr, callback){
36126         attr = attr || "id";
36127         var keys = path.split(this.pathSeparator);
36128         var curNode = this.root;
36129         if(curNode.attributes[attr] != keys[1]){ // invalid root
36130             if(callback){
36131                 callback(false, null);
36132             }
36133             return;
36134         }
36135         var index = 1;
36136         var f = function(){
36137             if(++index == keys.length){
36138                 if(callback){
36139                     callback(true, curNode);
36140                 }
36141                 return;
36142             }
36143             var c = curNode.findChild(attr, keys[index]);
36144             if(!c){
36145                 if(callback){
36146                     callback(false, curNode);
36147                 }
36148                 return;
36149             }
36150             curNode = c;
36151             c.expand(false, false, f);
36152         };
36153         curNode.expand(false, false, f);
36154     },
36155
36156     /**
36157      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36158      * @param {String} path
36159      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36160      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
36161      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
36162      */
36163     selectPath : function(path, attr, callback){
36164         attr = attr || "id";
36165         var keys = path.split(this.pathSeparator);
36166         var v = keys.pop();
36167         if(keys.length > 0){
36168             var f = function(success, node){
36169                 if(success && node){
36170                     var n = node.findChild(attr, v);
36171                     if(n){
36172                         n.select();
36173                         if(callback){
36174                             callback(true, n);
36175                         }
36176                     }else if(callback){
36177                         callback(false, n);
36178                     }
36179                 }else{
36180                     if(callback){
36181                         callback(false, n);
36182                     }
36183                 }
36184             };
36185             this.expandPath(keys.join(this.pathSeparator), attr, f);
36186         }else{
36187             this.root.select();
36188             if(callback){
36189                 callback(true, this.root);
36190             }
36191         }
36192     },
36193
36194     getTreeEl : function(){
36195         return this.el;
36196     },
36197
36198     /**
36199      * Trigger rendering of this TreePanel
36200      */
36201     render : function(){
36202         if (this.innerCt) {
36203             return this; // stop it rendering more than once!!
36204         }
36205         
36206         this.innerCt = this.el.createChild({tag:"ul",
36207                cls:"x-tree-root-ct " +
36208                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
36209
36210         if(this.containerScroll){
36211             Roo.dd.ScrollManager.register(this.el);
36212         }
36213         if((this.enableDD || this.enableDrop) && !this.dropZone){
36214            /**
36215             * The dropZone used by this tree if drop is enabled
36216             * @type Roo.tree.TreeDropZone
36217             */
36218              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
36219                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
36220            });
36221         }
36222         if((this.enableDD || this.enableDrag) && !this.dragZone){
36223            /**
36224             * The dragZone used by this tree if drag is enabled
36225             * @type Roo.tree.TreeDragZone
36226             */
36227             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
36228                ddGroup: this.ddGroup || "TreeDD",
36229                scroll: this.ddScroll
36230            });
36231         }
36232         this.getSelectionModel().init(this);
36233         if (!this.root) {
36234             Roo.log("ROOT not set in tree");
36235             return this;
36236         }
36237         this.root.render();
36238         if(!this.rootVisible){
36239             this.root.renderChildren();
36240         }
36241         return this;
36242     }
36243 });/*
36244  * Based on:
36245  * Ext JS Library 1.1.1
36246  * Copyright(c) 2006-2007, Ext JS, LLC.
36247  *
36248  * Originally Released Under LGPL - original licence link has changed is not relivant.
36249  *
36250  * Fork - LGPL
36251  * <script type="text/javascript">
36252  */
36253  
36254
36255 /**
36256  * @class Roo.tree.DefaultSelectionModel
36257  * @extends Roo.util.Observable
36258  * The default single selection for a TreePanel.
36259  * @param {Object} cfg Configuration
36260  */
36261 Roo.tree.DefaultSelectionModel = function(cfg){
36262    this.selNode = null;
36263    
36264    
36265    
36266    this.addEvents({
36267        /**
36268         * @event selectionchange
36269         * Fires when the selected node changes
36270         * @param {DefaultSelectionModel} this
36271         * @param {TreeNode} node the new selection
36272         */
36273        "selectionchange" : true,
36274
36275        /**
36276         * @event beforeselect
36277         * Fires before the selected node changes, return false to cancel the change
36278         * @param {DefaultSelectionModel} this
36279         * @param {TreeNode} node the new selection
36280         * @param {TreeNode} node the old selection
36281         */
36282        "beforeselect" : true
36283    });
36284    
36285     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36286 };
36287
36288 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36289     init : function(tree){
36290         this.tree = tree;
36291         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36292         tree.on("click", this.onNodeClick, this);
36293     },
36294     
36295     onNodeClick : function(node, e){
36296         if (e.ctrlKey && this.selNode == node)  {
36297             this.unselect(node);
36298             return;
36299         }
36300         this.select(node);
36301     },
36302     
36303     /**
36304      * Select a node.
36305      * @param {TreeNode} node The node to select
36306      * @return {TreeNode} The selected node
36307      */
36308     select : function(node){
36309         var last = this.selNode;
36310         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36311             if(last){
36312                 last.ui.onSelectedChange(false);
36313             }
36314             this.selNode = node;
36315             node.ui.onSelectedChange(true);
36316             this.fireEvent("selectionchange", this, node, last);
36317         }
36318         return node;
36319     },
36320     
36321     /**
36322      * Deselect a node.
36323      * @param {TreeNode} node The node to unselect
36324      */
36325     unselect : function(node){
36326         if(this.selNode == node){
36327             this.clearSelections();
36328         }    
36329     },
36330     
36331     /**
36332      * Clear all selections
36333      */
36334     clearSelections : function(){
36335         var n = this.selNode;
36336         if(n){
36337             n.ui.onSelectedChange(false);
36338             this.selNode = null;
36339             this.fireEvent("selectionchange", this, null);
36340         }
36341         return n;
36342     },
36343     
36344     /**
36345      * Get the selected node
36346      * @return {TreeNode} The selected node
36347      */
36348     getSelectedNode : function(){
36349         return this.selNode;    
36350     },
36351     
36352     /**
36353      * Returns true if the node is selected
36354      * @param {TreeNode} node The node to check
36355      * @return {Boolean}
36356      */
36357     isSelected : function(node){
36358         return this.selNode == node;  
36359     },
36360
36361     /**
36362      * Selects the node above the selected node in the tree, intelligently walking the nodes
36363      * @return TreeNode The new selection
36364      */
36365     selectPrevious : function(){
36366         var s = this.selNode || this.lastSelNode;
36367         if(!s){
36368             return null;
36369         }
36370         var ps = s.previousSibling;
36371         if(ps){
36372             if(!ps.isExpanded() || ps.childNodes.length < 1){
36373                 return this.select(ps);
36374             } else{
36375                 var lc = ps.lastChild;
36376                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36377                     lc = lc.lastChild;
36378                 }
36379                 return this.select(lc);
36380             }
36381         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36382             return this.select(s.parentNode);
36383         }
36384         return null;
36385     },
36386
36387     /**
36388      * Selects the node above the selected node in the tree, intelligently walking the nodes
36389      * @return TreeNode The new selection
36390      */
36391     selectNext : function(){
36392         var s = this.selNode || this.lastSelNode;
36393         if(!s){
36394             return null;
36395         }
36396         if(s.firstChild && s.isExpanded()){
36397              return this.select(s.firstChild);
36398          }else if(s.nextSibling){
36399              return this.select(s.nextSibling);
36400          }else if(s.parentNode){
36401             var newS = null;
36402             s.parentNode.bubble(function(){
36403                 if(this.nextSibling){
36404                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36405                     return false;
36406                 }
36407             });
36408             return newS;
36409          }
36410         return null;
36411     },
36412
36413     onKeyDown : function(e){
36414         var s = this.selNode || this.lastSelNode;
36415         // undesirable, but required
36416         var sm = this;
36417         if(!s){
36418             return;
36419         }
36420         var k = e.getKey();
36421         switch(k){
36422              case e.DOWN:
36423                  e.stopEvent();
36424                  this.selectNext();
36425              break;
36426              case e.UP:
36427                  e.stopEvent();
36428                  this.selectPrevious();
36429              break;
36430              case e.RIGHT:
36431                  e.preventDefault();
36432                  if(s.hasChildNodes()){
36433                      if(!s.isExpanded()){
36434                          s.expand();
36435                      }else if(s.firstChild){
36436                          this.select(s.firstChild, e);
36437                      }
36438                  }
36439              break;
36440              case e.LEFT:
36441                  e.preventDefault();
36442                  if(s.hasChildNodes() && s.isExpanded()){
36443                      s.collapse();
36444                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36445                      this.select(s.parentNode, e);
36446                  }
36447              break;
36448         };
36449     }
36450 });
36451
36452 /**
36453  * @class Roo.tree.MultiSelectionModel
36454  * @extends Roo.util.Observable
36455  * Multi selection for a TreePanel.
36456  * @param {Object} cfg Configuration
36457  */
36458 Roo.tree.MultiSelectionModel = function(){
36459    this.selNodes = [];
36460    this.selMap = {};
36461    this.addEvents({
36462        /**
36463         * @event selectionchange
36464         * Fires when the selected nodes change
36465         * @param {MultiSelectionModel} this
36466         * @param {Array} nodes Array of the selected nodes
36467         */
36468        "selectionchange" : true
36469    });
36470    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36471    
36472 };
36473
36474 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36475     init : function(tree){
36476         this.tree = tree;
36477         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36478         tree.on("click", this.onNodeClick, this);
36479     },
36480     
36481     onNodeClick : function(node, e){
36482         this.select(node, e, e.ctrlKey);
36483     },
36484     
36485     /**
36486      * Select a node.
36487      * @param {TreeNode} node The node to select
36488      * @param {EventObject} e (optional) An event associated with the selection
36489      * @param {Boolean} keepExisting True to retain existing selections
36490      * @return {TreeNode} The selected node
36491      */
36492     select : function(node, e, keepExisting){
36493         if(keepExisting !== true){
36494             this.clearSelections(true);
36495         }
36496         if(this.isSelected(node)){
36497             this.lastSelNode = node;
36498             return node;
36499         }
36500         this.selNodes.push(node);
36501         this.selMap[node.id] = node;
36502         this.lastSelNode = node;
36503         node.ui.onSelectedChange(true);
36504         this.fireEvent("selectionchange", this, this.selNodes);
36505         return node;
36506     },
36507     
36508     /**
36509      * Deselect a node.
36510      * @param {TreeNode} node The node to unselect
36511      */
36512     unselect : function(node){
36513         if(this.selMap[node.id]){
36514             node.ui.onSelectedChange(false);
36515             var sn = this.selNodes;
36516             var index = -1;
36517             if(sn.indexOf){
36518                 index = sn.indexOf(node);
36519             }else{
36520                 for(var i = 0, len = sn.length; i < len; i++){
36521                     if(sn[i] == node){
36522                         index = i;
36523                         break;
36524                     }
36525                 }
36526             }
36527             if(index != -1){
36528                 this.selNodes.splice(index, 1);
36529             }
36530             delete this.selMap[node.id];
36531             this.fireEvent("selectionchange", this, this.selNodes);
36532         }
36533     },
36534     
36535     /**
36536      * Clear all selections
36537      */
36538     clearSelections : function(suppressEvent){
36539         var sn = this.selNodes;
36540         if(sn.length > 0){
36541             for(var i = 0, len = sn.length; i < len; i++){
36542                 sn[i].ui.onSelectedChange(false);
36543             }
36544             this.selNodes = [];
36545             this.selMap = {};
36546             if(suppressEvent !== true){
36547                 this.fireEvent("selectionchange", this, this.selNodes);
36548             }
36549         }
36550     },
36551     
36552     /**
36553      * Returns true if the node is selected
36554      * @param {TreeNode} node The node to check
36555      * @return {Boolean}
36556      */
36557     isSelected : function(node){
36558         return this.selMap[node.id] ? true : false;  
36559     },
36560     
36561     /**
36562      * Returns an array of the selected nodes
36563      * @return {Array}
36564      */
36565     getSelectedNodes : function(){
36566         return this.selNodes;    
36567     },
36568
36569     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36570
36571     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36572
36573     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36574 });/*
36575  * Based on:
36576  * Ext JS Library 1.1.1
36577  * Copyright(c) 2006-2007, Ext JS, LLC.
36578  *
36579  * Originally Released Under LGPL - original licence link has changed is not relivant.
36580  *
36581  * Fork - LGPL
36582  * <script type="text/javascript">
36583  */
36584  
36585 /**
36586  * @class Roo.tree.TreeNode
36587  * @extends Roo.data.Node
36588  * @cfg {String} text The text for this node
36589  * @cfg {Boolean} expanded true to start the node expanded
36590  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36591  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36592  * @cfg {Boolean} disabled true to start the node disabled
36593  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36594  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36595  * @cfg {String} cls A css class to be added to the node
36596  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36597  * @cfg {String} href URL of the link used for the node (defaults to #)
36598  * @cfg {String} hrefTarget target frame for the link
36599  * @cfg {String} qtip An Ext QuickTip for the node
36600  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36601  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36602  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36603  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36604  * (defaults to undefined with no checkbox rendered)
36605  * @constructor
36606  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36607  */
36608 Roo.tree.TreeNode = function(attributes){
36609     attributes = attributes || {};
36610     if(typeof attributes == "string"){
36611         attributes = {text: attributes};
36612     }
36613     this.childrenRendered = false;
36614     this.rendered = false;
36615     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36616     this.expanded = attributes.expanded === true;
36617     this.isTarget = attributes.isTarget !== false;
36618     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36619     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36620
36621     /**
36622      * Read-only. The text for this node. To change it use setText().
36623      * @type String
36624      */
36625     this.text = attributes.text;
36626     /**
36627      * True if this node is disabled.
36628      * @type Boolean
36629      */
36630     this.disabled = attributes.disabled === true;
36631
36632     this.addEvents({
36633         /**
36634         * @event textchange
36635         * Fires when the text for this node is changed
36636         * @param {Node} this This node
36637         * @param {String} text The new text
36638         * @param {String} oldText The old text
36639         */
36640         "textchange" : true,
36641         /**
36642         * @event beforeexpand
36643         * Fires before this node is expanded, return false to cancel.
36644         * @param {Node} this This node
36645         * @param {Boolean} deep
36646         * @param {Boolean} anim
36647         */
36648         "beforeexpand" : true,
36649         /**
36650         * @event beforecollapse
36651         * Fires before this node is collapsed, return false to cancel.
36652         * @param {Node} this This node
36653         * @param {Boolean} deep
36654         * @param {Boolean} anim
36655         */
36656         "beforecollapse" : true,
36657         /**
36658         * @event expand
36659         * Fires when this node is expanded
36660         * @param {Node} this This node
36661         */
36662         "expand" : true,
36663         /**
36664         * @event disabledchange
36665         * Fires when the disabled status of this node changes
36666         * @param {Node} this This node
36667         * @param {Boolean} disabled
36668         */
36669         "disabledchange" : true,
36670         /**
36671         * @event collapse
36672         * Fires when this node is collapsed
36673         * @param {Node} this This node
36674         */
36675         "collapse" : true,
36676         /**
36677         * @event beforeclick
36678         * Fires before click processing. Return false to cancel the default action.
36679         * @param {Node} this This node
36680         * @param {Roo.EventObject} e The event object
36681         */
36682         "beforeclick":true,
36683         /**
36684         * @event checkchange
36685         * Fires when a node with a checkbox's checked property changes
36686         * @param {Node} this This node
36687         * @param {Boolean} checked
36688         */
36689         "checkchange":true,
36690         /**
36691         * @event click
36692         * Fires when this node is clicked
36693         * @param {Node} this This node
36694         * @param {Roo.EventObject} e The event object
36695         */
36696         "click":true,
36697         /**
36698         * @event dblclick
36699         * Fires when this node is double clicked
36700         * @param {Node} this This node
36701         * @param {Roo.EventObject} e The event object
36702         */
36703         "dblclick":true,
36704         /**
36705         * @event contextmenu
36706         * Fires when this node is right clicked
36707         * @param {Node} this This node
36708         * @param {Roo.EventObject} e The event object
36709         */
36710         "contextmenu":true,
36711         /**
36712         * @event beforechildrenrendered
36713         * Fires right before the child nodes for this node are rendered
36714         * @param {Node} this This node
36715         */
36716         "beforechildrenrendered":true
36717     });
36718
36719     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36720
36721     /**
36722      * Read-only. The UI for this node
36723      * @type TreeNodeUI
36724      */
36725     this.ui = new uiClass(this);
36726     
36727     // finally support items[]
36728     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36729         return;
36730     }
36731     
36732     
36733     Roo.each(this.attributes.items, function(c) {
36734         this.appendChild(Roo.factory(c,Roo.Tree));
36735     }, this);
36736     delete this.attributes.items;
36737     
36738     
36739     
36740 };
36741 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36742     preventHScroll: true,
36743     /**
36744      * Returns true if this node is expanded
36745      * @return {Boolean}
36746      */
36747     isExpanded : function(){
36748         return this.expanded;
36749     },
36750
36751     /**
36752      * Returns the UI object for this node
36753      * @return {TreeNodeUI}
36754      */
36755     getUI : function(){
36756         return this.ui;
36757     },
36758
36759     // private override
36760     setFirstChild : function(node){
36761         var of = this.firstChild;
36762         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36763         if(this.childrenRendered && of && node != of){
36764             of.renderIndent(true, true);
36765         }
36766         if(this.rendered){
36767             this.renderIndent(true, true);
36768         }
36769     },
36770
36771     // private override
36772     setLastChild : function(node){
36773         var ol = this.lastChild;
36774         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36775         if(this.childrenRendered && ol && node != ol){
36776             ol.renderIndent(true, true);
36777         }
36778         if(this.rendered){
36779             this.renderIndent(true, true);
36780         }
36781     },
36782
36783     // these methods are overridden to provide lazy rendering support
36784     // private override
36785     appendChild : function()
36786     {
36787         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36788         if(node && this.childrenRendered){
36789             node.render();
36790         }
36791         this.ui.updateExpandIcon();
36792         return node;
36793     },
36794
36795     // private override
36796     removeChild : function(node){
36797         this.ownerTree.getSelectionModel().unselect(node);
36798         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36799         // if it's been rendered remove dom node
36800         if(this.childrenRendered){
36801             node.ui.remove();
36802         }
36803         if(this.childNodes.length < 1){
36804             this.collapse(false, false);
36805         }else{
36806             this.ui.updateExpandIcon();
36807         }
36808         if(!this.firstChild) {
36809             this.childrenRendered = false;
36810         }
36811         return node;
36812     },
36813
36814     // private override
36815     insertBefore : function(node, refNode){
36816         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36817         if(newNode && refNode && this.childrenRendered){
36818             node.render();
36819         }
36820         this.ui.updateExpandIcon();
36821         return newNode;
36822     },
36823
36824     /**
36825      * Sets the text for this node
36826      * @param {String} text
36827      */
36828     setText : function(text){
36829         var oldText = this.text;
36830         this.text = text;
36831         this.attributes.text = text;
36832         if(this.rendered){ // event without subscribing
36833             this.ui.onTextChange(this, text, oldText);
36834         }
36835         this.fireEvent("textchange", this, text, oldText);
36836     },
36837
36838     /**
36839      * Triggers selection of this node
36840      */
36841     select : function(){
36842         this.getOwnerTree().getSelectionModel().select(this);
36843     },
36844
36845     /**
36846      * Triggers deselection of this node
36847      */
36848     unselect : function(){
36849         this.getOwnerTree().getSelectionModel().unselect(this);
36850     },
36851
36852     /**
36853      * Returns true if this node is selected
36854      * @return {Boolean}
36855      */
36856     isSelected : function(){
36857         return this.getOwnerTree().getSelectionModel().isSelected(this);
36858     },
36859
36860     /**
36861      * Expand this node.
36862      * @param {Boolean} deep (optional) True to expand all children as well
36863      * @param {Boolean} anim (optional) false to cancel the default animation
36864      * @param {Function} callback (optional) A callback to be called when
36865      * expanding this node completes (does not wait for deep expand to complete).
36866      * Called with 1 parameter, this node.
36867      */
36868     expand : function(deep, anim, callback){
36869         if(!this.expanded){
36870             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36871                 return;
36872             }
36873             if(!this.childrenRendered){
36874                 this.renderChildren();
36875             }
36876             this.expanded = true;
36877             
36878             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36879                 this.ui.animExpand(function(){
36880                     this.fireEvent("expand", this);
36881                     if(typeof callback == "function"){
36882                         callback(this);
36883                     }
36884                     if(deep === true){
36885                         this.expandChildNodes(true);
36886                     }
36887                 }.createDelegate(this));
36888                 return;
36889             }else{
36890                 this.ui.expand();
36891                 this.fireEvent("expand", this);
36892                 if(typeof callback == "function"){
36893                     callback(this);
36894                 }
36895             }
36896         }else{
36897            if(typeof callback == "function"){
36898                callback(this);
36899            }
36900         }
36901         if(deep === true){
36902             this.expandChildNodes(true);
36903         }
36904     },
36905
36906     isHiddenRoot : function(){
36907         return this.isRoot && !this.getOwnerTree().rootVisible;
36908     },
36909
36910     /**
36911      * Collapse this node.
36912      * @param {Boolean} deep (optional) True to collapse all children as well
36913      * @param {Boolean} anim (optional) false to cancel the default animation
36914      */
36915     collapse : function(deep, anim){
36916         if(this.expanded && !this.isHiddenRoot()){
36917             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36918                 return;
36919             }
36920             this.expanded = false;
36921             if((this.getOwnerTree().animate && anim !== false) || anim){
36922                 this.ui.animCollapse(function(){
36923                     this.fireEvent("collapse", this);
36924                     if(deep === true){
36925                         this.collapseChildNodes(true);
36926                     }
36927                 }.createDelegate(this));
36928                 return;
36929             }else{
36930                 this.ui.collapse();
36931                 this.fireEvent("collapse", this);
36932             }
36933         }
36934         if(deep === true){
36935             var cs = this.childNodes;
36936             for(var i = 0, len = cs.length; i < len; i++) {
36937                 cs[i].collapse(true, false);
36938             }
36939         }
36940     },
36941
36942     // private
36943     delayedExpand : function(delay){
36944         if(!this.expandProcId){
36945             this.expandProcId = this.expand.defer(delay, this);
36946         }
36947     },
36948
36949     // private
36950     cancelExpand : function(){
36951         if(this.expandProcId){
36952             clearTimeout(this.expandProcId);
36953         }
36954         this.expandProcId = false;
36955     },
36956
36957     /**
36958      * Toggles expanded/collapsed state of the node
36959      */
36960     toggle : function(){
36961         if(this.expanded){
36962             this.collapse();
36963         }else{
36964             this.expand();
36965         }
36966     },
36967
36968     /**
36969      * Ensures all parent nodes are expanded
36970      */
36971     ensureVisible : function(callback){
36972         var tree = this.getOwnerTree();
36973         tree.expandPath(this.parentNode.getPath(), false, function(){
36974             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36975             Roo.callback(callback);
36976         }.createDelegate(this));
36977     },
36978
36979     /**
36980      * Expand all child nodes
36981      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36982      */
36983     expandChildNodes : function(deep){
36984         var cs = this.childNodes;
36985         for(var i = 0, len = cs.length; i < len; i++) {
36986                 cs[i].expand(deep);
36987         }
36988     },
36989
36990     /**
36991      * Collapse all child nodes
36992      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36993      */
36994     collapseChildNodes : function(deep){
36995         var cs = this.childNodes;
36996         for(var i = 0, len = cs.length; i < len; i++) {
36997                 cs[i].collapse(deep);
36998         }
36999     },
37000
37001     /**
37002      * Disables this node
37003      */
37004     disable : function(){
37005         this.disabled = true;
37006         this.unselect();
37007         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37008             this.ui.onDisableChange(this, true);
37009         }
37010         this.fireEvent("disabledchange", this, true);
37011     },
37012
37013     /**
37014      * Enables this node
37015      */
37016     enable : function(){
37017         this.disabled = false;
37018         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37019             this.ui.onDisableChange(this, false);
37020         }
37021         this.fireEvent("disabledchange", this, false);
37022     },
37023
37024     // private
37025     renderChildren : function(suppressEvent){
37026         if(suppressEvent !== false){
37027             this.fireEvent("beforechildrenrendered", this);
37028         }
37029         var cs = this.childNodes;
37030         for(var i = 0, len = cs.length; i < len; i++){
37031             cs[i].render(true);
37032         }
37033         this.childrenRendered = true;
37034     },
37035
37036     // private
37037     sort : function(fn, scope){
37038         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
37039         if(this.childrenRendered){
37040             var cs = this.childNodes;
37041             for(var i = 0, len = cs.length; i < len; i++){
37042                 cs[i].render(true);
37043             }
37044         }
37045     },
37046
37047     // private
37048     render : function(bulkRender){
37049         this.ui.render(bulkRender);
37050         if(!this.rendered){
37051             this.rendered = true;
37052             if(this.expanded){
37053                 this.expanded = false;
37054                 this.expand(false, false);
37055             }
37056         }
37057     },
37058
37059     // private
37060     renderIndent : function(deep, refresh){
37061         if(refresh){
37062             this.ui.childIndent = null;
37063         }
37064         this.ui.renderIndent();
37065         if(deep === true && this.childrenRendered){
37066             var cs = this.childNodes;
37067             for(var i = 0, len = cs.length; i < len; i++){
37068                 cs[i].renderIndent(true, refresh);
37069             }
37070         }
37071     }
37072 });/*
37073  * Based on:
37074  * Ext JS Library 1.1.1
37075  * Copyright(c) 2006-2007, Ext JS, LLC.
37076  *
37077  * Originally Released Under LGPL - original licence link has changed is not relivant.
37078  *
37079  * Fork - LGPL
37080  * <script type="text/javascript">
37081  */
37082  
37083 /**
37084  * @class Roo.tree.AsyncTreeNode
37085  * @extends Roo.tree.TreeNode
37086  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
37087  * @constructor
37088  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
37089  */
37090  Roo.tree.AsyncTreeNode = function(config){
37091     this.loaded = false;
37092     this.loading = false;
37093     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
37094     /**
37095     * @event beforeload
37096     * Fires before this node is loaded, return false to cancel
37097     * @param {Node} this This node
37098     */
37099     this.addEvents({'beforeload':true, 'load': true});
37100     /**
37101     * @event load
37102     * Fires when this node is loaded
37103     * @param {Node} this This node
37104     */
37105     /**
37106      * The loader used by this node (defaults to using the tree's defined loader)
37107      * @type TreeLoader
37108      * @property loader
37109      */
37110 };
37111 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
37112     expand : function(deep, anim, callback){
37113         if(this.loading){ // if an async load is already running, waiting til it's done
37114             var timer;
37115             var f = function(){
37116                 if(!this.loading){ // done loading
37117                     clearInterval(timer);
37118                     this.expand(deep, anim, callback);
37119                 }
37120             }.createDelegate(this);
37121             timer = setInterval(f, 200);
37122             return;
37123         }
37124         if(!this.loaded){
37125             if(this.fireEvent("beforeload", this) === false){
37126                 return;
37127             }
37128             this.loading = true;
37129             this.ui.beforeLoad(this);
37130             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
37131             if(loader){
37132                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
37133                 return;
37134             }
37135         }
37136         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
37137     },
37138     
37139     /**
37140      * Returns true if this node is currently loading
37141      * @return {Boolean}
37142      */
37143     isLoading : function(){
37144         return this.loading;  
37145     },
37146     
37147     loadComplete : function(deep, anim, callback){
37148         this.loading = false;
37149         this.loaded = true;
37150         this.ui.afterLoad(this);
37151         this.fireEvent("load", this);
37152         this.expand(deep, anim, callback);
37153     },
37154     
37155     /**
37156      * Returns true if this node has been loaded
37157      * @return {Boolean}
37158      */
37159     isLoaded : function(){
37160         return this.loaded;
37161     },
37162     
37163     hasChildNodes : function(){
37164         if(!this.isLeaf() && !this.loaded){
37165             return true;
37166         }else{
37167             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
37168         }
37169     },
37170
37171     /**
37172      * Trigger a reload for this node
37173      * @param {Function} callback
37174      */
37175     reload : function(callback){
37176         this.collapse(false, false);
37177         while(this.firstChild){
37178             this.removeChild(this.firstChild);
37179         }
37180         this.childrenRendered = false;
37181         this.loaded = false;
37182         if(this.isHiddenRoot()){
37183             this.expanded = false;
37184         }
37185         this.expand(false, false, callback);
37186     }
37187 });/*
37188  * Based on:
37189  * Ext JS Library 1.1.1
37190  * Copyright(c) 2006-2007, Ext JS, LLC.
37191  *
37192  * Originally Released Under LGPL - original licence link has changed is not relivant.
37193  *
37194  * Fork - LGPL
37195  * <script type="text/javascript">
37196  */
37197  
37198 /**
37199  * @class Roo.tree.TreeNodeUI
37200  * @constructor
37201  * @param {Object} node The node to render
37202  * The TreeNode UI implementation is separate from the
37203  * tree implementation. Unless you are customizing the tree UI,
37204  * you should never have to use this directly.
37205  */
37206 Roo.tree.TreeNodeUI = function(node){
37207     this.node = node;
37208     this.rendered = false;
37209     this.animating = false;
37210     this.emptyIcon = Roo.BLANK_IMAGE_URL;
37211 };
37212
37213 Roo.tree.TreeNodeUI.prototype = {
37214     removeChild : function(node){
37215         if(this.rendered){
37216             this.ctNode.removeChild(node.ui.getEl());
37217         }
37218     },
37219
37220     beforeLoad : function(){
37221          this.addClass("x-tree-node-loading");
37222     },
37223
37224     afterLoad : function(){
37225          this.removeClass("x-tree-node-loading");
37226     },
37227
37228     onTextChange : function(node, text, oldText){
37229         if(this.rendered){
37230             this.textNode.innerHTML = text;
37231         }
37232     },
37233
37234     onDisableChange : function(node, state){
37235         this.disabled = state;
37236         if(state){
37237             this.addClass("x-tree-node-disabled");
37238         }else{
37239             this.removeClass("x-tree-node-disabled");
37240         }
37241     },
37242
37243     onSelectedChange : function(state){
37244         if(state){
37245             this.focus();
37246             this.addClass("x-tree-selected");
37247         }else{
37248             //this.blur();
37249             this.removeClass("x-tree-selected");
37250         }
37251     },
37252
37253     onMove : function(tree, node, oldParent, newParent, index, refNode){
37254         this.childIndent = null;
37255         if(this.rendered){
37256             var targetNode = newParent.ui.getContainer();
37257             if(!targetNode){//target not rendered
37258                 this.holder = document.createElement("div");
37259                 this.holder.appendChild(this.wrap);
37260                 return;
37261             }
37262             var insertBefore = refNode ? refNode.ui.getEl() : null;
37263             if(insertBefore){
37264                 targetNode.insertBefore(this.wrap, insertBefore);
37265             }else{
37266                 targetNode.appendChild(this.wrap);
37267             }
37268             this.node.renderIndent(true);
37269         }
37270     },
37271
37272     addClass : function(cls){
37273         if(this.elNode){
37274             Roo.fly(this.elNode).addClass(cls);
37275         }
37276     },
37277
37278     removeClass : function(cls){
37279         if(this.elNode){
37280             Roo.fly(this.elNode).removeClass(cls);
37281         }
37282     },
37283
37284     remove : function(){
37285         if(this.rendered){
37286             this.holder = document.createElement("div");
37287             this.holder.appendChild(this.wrap);
37288         }
37289     },
37290
37291     fireEvent : function(){
37292         return this.node.fireEvent.apply(this.node, arguments);
37293     },
37294
37295     initEvents : function(){
37296         this.node.on("move", this.onMove, this);
37297         var E = Roo.EventManager;
37298         var a = this.anchor;
37299
37300         var el = Roo.fly(a, '_treeui');
37301
37302         if(Roo.isOpera){ // opera render bug ignores the CSS
37303             el.setStyle("text-decoration", "none");
37304         }
37305
37306         el.on("click", this.onClick, this);
37307         el.on("dblclick", this.onDblClick, this);
37308
37309         if(this.checkbox){
37310             Roo.EventManager.on(this.checkbox,
37311                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37312         }
37313
37314         el.on("contextmenu", this.onContextMenu, this);
37315
37316         var icon = Roo.fly(this.iconNode);
37317         icon.on("click", this.onClick, this);
37318         icon.on("dblclick", this.onDblClick, this);
37319         icon.on("contextmenu", this.onContextMenu, this);
37320         E.on(this.ecNode, "click", this.ecClick, this, true);
37321
37322         if(this.node.disabled){
37323             this.addClass("x-tree-node-disabled");
37324         }
37325         if(this.node.hidden){
37326             this.addClass("x-tree-node-disabled");
37327         }
37328         var ot = this.node.getOwnerTree();
37329         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37330         if(dd && (!this.node.isRoot || ot.rootVisible)){
37331             Roo.dd.Registry.register(this.elNode, {
37332                 node: this.node,
37333                 handles: this.getDDHandles(),
37334                 isHandle: false
37335             });
37336         }
37337     },
37338
37339     getDDHandles : function(){
37340         return [this.iconNode, this.textNode];
37341     },
37342
37343     hide : function(){
37344         if(this.rendered){
37345             this.wrap.style.display = "none";
37346         }
37347     },
37348
37349     show : function(){
37350         if(this.rendered){
37351             this.wrap.style.display = "";
37352         }
37353     },
37354
37355     onContextMenu : function(e){
37356         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37357             e.preventDefault();
37358             this.focus();
37359             this.fireEvent("contextmenu", this.node, e);
37360         }
37361     },
37362
37363     onClick : function(e){
37364         if(this.dropping){
37365             e.stopEvent();
37366             return;
37367         }
37368         if(this.fireEvent("beforeclick", this.node, e) !== false){
37369             if(!this.disabled && this.node.attributes.href){
37370                 this.fireEvent("click", this.node, e);
37371                 return;
37372             }
37373             e.preventDefault();
37374             if(this.disabled){
37375                 return;
37376             }
37377
37378             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37379                 this.node.toggle();
37380             }
37381
37382             this.fireEvent("click", this.node, e);
37383         }else{
37384             e.stopEvent();
37385         }
37386     },
37387
37388     onDblClick : function(e){
37389         e.preventDefault();
37390         if(this.disabled){
37391             return;
37392         }
37393         if(this.checkbox){
37394             this.toggleCheck();
37395         }
37396         if(!this.animating && this.node.hasChildNodes()){
37397             this.node.toggle();
37398         }
37399         this.fireEvent("dblclick", this.node, e);
37400     },
37401
37402     onCheckChange : function(){
37403         var checked = this.checkbox.checked;
37404         this.node.attributes.checked = checked;
37405         this.fireEvent('checkchange', this.node, checked);
37406     },
37407
37408     ecClick : function(e){
37409         if(!this.animating && this.node.hasChildNodes()){
37410             this.node.toggle();
37411         }
37412     },
37413
37414     startDrop : function(){
37415         this.dropping = true;
37416     },
37417
37418     // delayed drop so the click event doesn't get fired on a drop
37419     endDrop : function(){
37420        setTimeout(function(){
37421            this.dropping = false;
37422        }.createDelegate(this), 50);
37423     },
37424
37425     expand : function(){
37426         this.updateExpandIcon();
37427         this.ctNode.style.display = "";
37428     },
37429
37430     focus : function(){
37431         if(!this.node.preventHScroll){
37432             try{this.anchor.focus();
37433             }catch(e){}
37434         }else if(!Roo.isIE){
37435             try{
37436                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37437                 var l = noscroll.scrollLeft;
37438                 this.anchor.focus();
37439                 noscroll.scrollLeft = l;
37440             }catch(e){}
37441         }
37442     },
37443
37444     toggleCheck : function(value){
37445         var cb = this.checkbox;
37446         if(cb){
37447             cb.checked = (value === undefined ? !cb.checked : value);
37448         }
37449     },
37450
37451     blur : function(){
37452         try{
37453             this.anchor.blur();
37454         }catch(e){}
37455     },
37456
37457     animExpand : function(callback){
37458         var ct = Roo.get(this.ctNode);
37459         ct.stopFx();
37460         if(!this.node.hasChildNodes()){
37461             this.updateExpandIcon();
37462             this.ctNode.style.display = "";
37463             Roo.callback(callback);
37464             return;
37465         }
37466         this.animating = true;
37467         this.updateExpandIcon();
37468
37469         ct.slideIn('t', {
37470            callback : function(){
37471                this.animating = false;
37472                Roo.callback(callback);
37473             },
37474             scope: this,
37475             duration: this.node.ownerTree.duration || .25
37476         });
37477     },
37478
37479     highlight : function(){
37480         var tree = this.node.getOwnerTree();
37481         Roo.fly(this.wrap).highlight(
37482             tree.hlColor || "C3DAF9",
37483             {endColor: tree.hlBaseColor}
37484         );
37485     },
37486
37487     collapse : function(){
37488         this.updateExpandIcon();
37489         this.ctNode.style.display = "none";
37490     },
37491
37492     animCollapse : function(callback){
37493         var ct = Roo.get(this.ctNode);
37494         ct.enableDisplayMode('block');
37495         ct.stopFx();
37496
37497         this.animating = true;
37498         this.updateExpandIcon();
37499
37500         ct.slideOut('t', {
37501             callback : function(){
37502                this.animating = false;
37503                Roo.callback(callback);
37504             },
37505             scope: this,
37506             duration: this.node.ownerTree.duration || .25
37507         });
37508     },
37509
37510     getContainer : function(){
37511         return this.ctNode;
37512     },
37513
37514     getEl : function(){
37515         return this.wrap;
37516     },
37517
37518     appendDDGhost : function(ghostNode){
37519         ghostNode.appendChild(this.elNode.cloneNode(true));
37520     },
37521
37522     getDDRepairXY : function(){
37523         return Roo.lib.Dom.getXY(this.iconNode);
37524     },
37525
37526     onRender : function(){
37527         this.render();
37528     },
37529
37530     render : function(bulkRender){
37531         var n = this.node, a = n.attributes;
37532         var targetNode = n.parentNode ?
37533               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37534
37535         if(!this.rendered){
37536             this.rendered = true;
37537
37538             this.renderElements(n, a, targetNode, bulkRender);
37539
37540             if(a.qtip){
37541                if(this.textNode.setAttributeNS){
37542                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37543                    if(a.qtipTitle){
37544                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37545                    }
37546                }else{
37547                    this.textNode.setAttribute("ext:qtip", a.qtip);
37548                    if(a.qtipTitle){
37549                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37550                    }
37551                }
37552             }else if(a.qtipCfg){
37553                 a.qtipCfg.target = Roo.id(this.textNode);
37554                 Roo.QuickTips.register(a.qtipCfg);
37555             }
37556             this.initEvents();
37557             if(!this.node.expanded){
37558                 this.updateExpandIcon();
37559             }
37560         }else{
37561             if(bulkRender === true) {
37562                 targetNode.appendChild(this.wrap);
37563             }
37564         }
37565     },
37566
37567     renderElements : function(n, a, targetNode, bulkRender)
37568     {
37569         // add some indent caching, this helps performance when rendering a large tree
37570         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37571         var t = n.getOwnerTree();
37572         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37573         if (typeof(n.attributes.html) != 'undefined') {
37574             txt = n.attributes.html;
37575         }
37576         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37577         var cb = typeof a.checked == 'boolean';
37578         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37579         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37580             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37581             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37582             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37583             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37584             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37585              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37586                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37587             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37588             "</li>"];
37589
37590         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37591             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37592                                 n.nextSibling.ui.getEl(), buf.join(""));
37593         }else{
37594             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37595         }
37596
37597         this.elNode = this.wrap.childNodes[0];
37598         this.ctNode = this.wrap.childNodes[1];
37599         var cs = this.elNode.childNodes;
37600         this.indentNode = cs[0];
37601         this.ecNode = cs[1];
37602         this.iconNode = cs[2];
37603         var index = 3;
37604         if(cb){
37605             this.checkbox = cs[3];
37606             index++;
37607         }
37608         this.anchor = cs[index];
37609         this.textNode = cs[index].firstChild;
37610     },
37611
37612     getAnchor : function(){
37613         return this.anchor;
37614     },
37615
37616     getTextEl : function(){
37617         return this.textNode;
37618     },
37619
37620     getIconEl : function(){
37621         return this.iconNode;
37622     },
37623
37624     isChecked : function(){
37625         return this.checkbox ? this.checkbox.checked : false;
37626     },
37627
37628     updateExpandIcon : function(){
37629         if(this.rendered){
37630             var n = this.node, c1, c2;
37631             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37632             var hasChild = n.hasChildNodes();
37633             if(hasChild){
37634                 if(n.expanded){
37635                     cls += "-minus";
37636                     c1 = "x-tree-node-collapsed";
37637                     c2 = "x-tree-node-expanded";
37638                 }else{
37639                     cls += "-plus";
37640                     c1 = "x-tree-node-expanded";
37641                     c2 = "x-tree-node-collapsed";
37642                 }
37643                 if(this.wasLeaf){
37644                     this.removeClass("x-tree-node-leaf");
37645                     this.wasLeaf = false;
37646                 }
37647                 if(this.c1 != c1 || this.c2 != c2){
37648                     Roo.fly(this.elNode).replaceClass(c1, c2);
37649                     this.c1 = c1; this.c2 = c2;
37650                 }
37651             }else{
37652                 // this changes non-leafs into leafs if they have no children.
37653                 // it's not very rational behaviour..
37654                 
37655                 if(!this.wasLeaf && this.node.leaf){
37656                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37657                     delete this.c1;
37658                     delete this.c2;
37659                     this.wasLeaf = true;
37660                 }
37661             }
37662             var ecc = "x-tree-ec-icon "+cls;
37663             if(this.ecc != ecc){
37664                 this.ecNode.className = ecc;
37665                 this.ecc = ecc;
37666             }
37667         }
37668     },
37669
37670     getChildIndent : function(){
37671         if(!this.childIndent){
37672             var buf = [];
37673             var p = this.node;
37674             while(p){
37675                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37676                     if(!p.isLast()) {
37677                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37678                     } else {
37679                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37680                     }
37681                 }
37682                 p = p.parentNode;
37683             }
37684             this.childIndent = buf.join("");
37685         }
37686         return this.childIndent;
37687     },
37688
37689     renderIndent : function(){
37690         if(this.rendered){
37691             var indent = "";
37692             var p = this.node.parentNode;
37693             if(p){
37694                 indent = p.ui.getChildIndent();
37695             }
37696             if(this.indentMarkup != indent){ // don't rerender if not required
37697                 this.indentNode.innerHTML = indent;
37698                 this.indentMarkup = indent;
37699             }
37700             this.updateExpandIcon();
37701         }
37702     }
37703 };
37704
37705 Roo.tree.RootTreeNodeUI = function(){
37706     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37707 };
37708 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37709     render : function(){
37710         if(!this.rendered){
37711             var targetNode = this.node.ownerTree.innerCt.dom;
37712             this.node.expanded = true;
37713             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37714             this.wrap = this.ctNode = targetNode.firstChild;
37715         }
37716     },
37717     collapse : function(){
37718     },
37719     expand : function(){
37720     }
37721 });/*
37722  * Based on:
37723  * Ext JS Library 1.1.1
37724  * Copyright(c) 2006-2007, Ext JS, LLC.
37725  *
37726  * Originally Released Under LGPL - original licence link has changed is not relivant.
37727  *
37728  * Fork - LGPL
37729  * <script type="text/javascript">
37730  */
37731 /**
37732  * @class Roo.tree.TreeLoader
37733  * @extends Roo.util.Observable
37734  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37735  * nodes from a specified URL. The response must be a javascript Array definition
37736  * who's elements are node definition objects. eg:
37737  * <pre><code>
37738 {  success : true,
37739    data :      [
37740    
37741     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37742     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37743     ]
37744 }
37745
37746
37747 </code></pre>
37748  * <br><br>
37749  * The old style respose with just an array is still supported, but not recommended.
37750  * <br><br>
37751  *
37752  * A server request is sent, and child nodes are loaded only when a node is expanded.
37753  * The loading node's id is passed to the server under the parameter name "node" to
37754  * enable the server to produce the correct child nodes.
37755  * <br><br>
37756  * To pass extra parameters, an event handler may be attached to the "beforeload"
37757  * event, and the parameters specified in the TreeLoader's baseParams property:
37758  * <pre><code>
37759     myTreeLoader.on("beforeload", function(treeLoader, node) {
37760         this.baseParams.category = node.attributes.category;
37761     }, this);
37762     
37763 </code></pre>
37764  *
37765  * This would pass an HTTP parameter called "category" to the server containing
37766  * the value of the Node's "category" attribute.
37767  * @constructor
37768  * Creates a new Treeloader.
37769  * @param {Object} config A config object containing config properties.
37770  */
37771 Roo.tree.TreeLoader = function(config){
37772     this.baseParams = {};
37773     this.requestMethod = "POST";
37774     Roo.apply(this, config);
37775
37776     this.addEvents({
37777     
37778         /**
37779          * @event beforeload
37780          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37781          * @param {Object} This TreeLoader object.
37782          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37783          * @param {Object} callback The callback function specified in the {@link #load} call.
37784          */
37785         beforeload : true,
37786         /**
37787          * @event load
37788          * Fires when the node has been successfuly loaded.
37789          * @param {Object} This TreeLoader object.
37790          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37791          * @param {Object} response The response object containing the data from the server.
37792          */
37793         load : true,
37794         /**
37795          * @event loadexception
37796          * Fires if the network request failed.
37797          * @param {Object} This TreeLoader object.
37798          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37799          * @param {Object} response The response object containing the data from the server.
37800          */
37801         loadexception : true,
37802         /**
37803          * @event create
37804          * Fires before a node is created, enabling you to return custom Node types 
37805          * @param {Object} This TreeLoader object.
37806          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37807          */
37808         create : true
37809     });
37810
37811     Roo.tree.TreeLoader.superclass.constructor.call(this);
37812 };
37813
37814 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37815     /**
37816     * @cfg {String} dataUrl The URL from which to request a Json string which
37817     * specifies an array of node definition object representing the child nodes
37818     * to be loaded.
37819     */
37820     /**
37821     * @cfg {String} requestMethod either GET or POST
37822     * defaults to POST (due to BC)
37823     * to be loaded.
37824     */
37825     /**
37826     * @cfg {Object} baseParams (optional) An object containing properties which
37827     * specify HTTP parameters to be passed to each request for child nodes.
37828     */
37829     /**
37830     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37831     * created by this loader. If the attributes sent by the server have an attribute in this object,
37832     * they take priority.
37833     */
37834     /**
37835     * @cfg {Object} uiProviders (optional) An object containing properties which
37836     * 
37837     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37838     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37839     * <i>uiProvider</i> attribute of a returned child node is a string rather
37840     * than a reference to a TreeNodeUI implementation, this that string value
37841     * is used as a property name in the uiProviders object. You can define the provider named
37842     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37843     */
37844     uiProviders : {},
37845
37846     /**
37847     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37848     * child nodes before loading.
37849     */
37850     clearOnLoad : true,
37851
37852     /**
37853     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37854     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37855     * Grid query { data : [ .....] }
37856     */
37857     
37858     root : false,
37859      /**
37860     * @cfg {String} queryParam (optional) 
37861     * Name of the query as it will be passed on the querystring (defaults to 'node')
37862     * eg. the request will be ?node=[id]
37863     */
37864     
37865     
37866     queryParam: false,
37867     
37868     /**
37869      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37870      * This is called automatically when a node is expanded, but may be used to reload
37871      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37872      * @param {Roo.tree.TreeNode} node
37873      * @param {Function} callback
37874      */
37875     load : function(node, callback){
37876         if(this.clearOnLoad){
37877             while(node.firstChild){
37878                 node.removeChild(node.firstChild);
37879             }
37880         }
37881         if(node.attributes.children){ // preloaded json children
37882             var cs = node.attributes.children;
37883             for(var i = 0, len = cs.length; i < len; i++){
37884                 node.appendChild(this.createNode(cs[i]));
37885             }
37886             if(typeof callback == "function"){
37887                 callback();
37888             }
37889         }else if(this.dataUrl){
37890             this.requestData(node, callback);
37891         }
37892     },
37893
37894     getParams: function(node){
37895         var buf = [], bp = this.baseParams;
37896         for(var key in bp){
37897             if(typeof bp[key] != "function"){
37898                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37899             }
37900         }
37901         var n = this.queryParam === false ? 'node' : this.queryParam;
37902         buf.push(n + "=", encodeURIComponent(node.id));
37903         return buf.join("");
37904     },
37905
37906     requestData : function(node, callback){
37907         if(this.fireEvent("beforeload", this, node, callback) !== false){
37908             this.transId = Roo.Ajax.request({
37909                 method:this.requestMethod,
37910                 url: this.dataUrl||this.url,
37911                 success: this.handleResponse,
37912                 failure: this.handleFailure,
37913                 scope: this,
37914                 argument: {callback: callback, node: node},
37915                 params: this.getParams(node)
37916             });
37917         }else{
37918             // if the load is cancelled, make sure we notify
37919             // the node that we are done
37920             if(typeof callback == "function"){
37921                 callback();
37922             }
37923         }
37924     },
37925
37926     isLoading : function(){
37927         return this.transId ? true : false;
37928     },
37929
37930     abort : function(){
37931         if(this.isLoading()){
37932             Roo.Ajax.abort(this.transId);
37933         }
37934     },
37935
37936     // private
37937     createNode : function(attr)
37938     {
37939         // apply baseAttrs, nice idea Corey!
37940         if(this.baseAttrs){
37941             Roo.applyIf(attr, this.baseAttrs);
37942         }
37943         if(this.applyLoader !== false){
37944             attr.loader = this;
37945         }
37946         // uiProvider = depreciated..
37947         
37948         if(typeof(attr.uiProvider) == 'string'){
37949            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37950                 /**  eval:var:attr */ eval(attr.uiProvider);
37951         }
37952         if(typeof(this.uiProviders['default']) != 'undefined') {
37953             attr.uiProvider = this.uiProviders['default'];
37954         }
37955         
37956         this.fireEvent('create', this, attr);
37957         
37958         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37959         return(attr.leaf ?
37960                         new Roo.tree.TreeNode(attr) :
37961                         new Roo.tree.AsyncTreeNode(attr));
37962     },
37963
37964     processResponse : function(response, node, callback)
37965     {
37966         var json = response.responseText;
37967         try {
37968             
37969             var o = Roo.decode(json);
37970             
37971             if (this.root === false && typeof(o.success) != undefined) {
37972                 this.root = 'data'; // the default behaviour for list like data..
37973                 }
37974                 
37975             if (this.root !== false &&  !o.success) {
37976                 // it's a failure condition.
37977                 var a = response.argument;
37978                 this.fireEvent("loadexception", this, a.node, response);
37979                 Roo.log("Load failed - should have a handler really");
37980                 return;
37981             }
37982             
37983             
37984             
37985             if (this.root !== false) {
37986                  o = o[this.root];
37987             }
37988             
37989             for(var i = 0, len = o.length; i < len; i++){
37990                 var n = this.createNode(o[i]);
37991                 if(n){
37992                     node.appendChild(n);
37993                 }
37994             }
37995             if(typeof callback == "function"){
37996                 callback(this, node);
37997             }
37998         }catch(e){
37999             this.handleFailure(response);
38000         }
38001     },
38002
38003     handleResponse : function(response){
38004         this.transId = false;
38005         var a = response.argument;
38006         this.processResponse(response, a.node, a.callback);
38007         this.fireEvent("load", this, a.node, response);
38008     },
38009
38010     handleFailure : function(response)
38011     {
38012         // should handle failure better..
38013         this.transId = false;
38014         var a = response.argument;
38015         this.fireEvent("loadexception", this, a.node, response);
38016         if(typeof a.callback == "function"){
38017             a.callback(this, a.node);
38018         }
38019     }
38020 });/*
38021  * Based on:
38022  * Ext JS Library 1.1.1
38023  * Copyright(c) 2006-2007, Ext JS, LLC.
38024  *
38025  * Originally Released Under LGPL - original licence link has changed is not relivant.
38026  *
38027  * Fork - LGPL
38028  * <script type="text/javascript">
38029  */
38030
38031 /**
38032 * @class Roo.tree.TreeFilter
38033 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
38034 * @param {TreePanel} tree
38035 * @param {Object} config (optional)
38036  */
38037 Roo.tree.TreeFilter = function(tree, config){
38038     this.tree = tree;
38039     this.filtered = {};
38040     Roo.apply(this, config);
38041 };
38042
38043 Roo.tree.TreeFilter.prototype = {
38044     clearBlank:false,
38045     reverse:false,
38046     autoClear:false,
38047     remove:false,
38048
38049      /**
38050      * Filter the data by a specific attribute.
38051      * @param {String/RegExp} value Either string that the attribute value
38052      * should start with or a RegExp to test against the attribute
38053      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
38054      * @param {TreeNode} startNode (optional) The node to start the filter at.
38055      */
38056     filter : function(value, attr, startNode){
38057         attr = attr || "text";
38058         var f;
38059         if(typeof value == "string"){
38060             var vlen = value.length;
38061             // auto clear empty filter
38062             if(vlen == 0 && this.clearBlank){
38063                 this.clear();
38064                 return;
38065             }
38066             value = value.toLowerCase();
38067             f = function(n){
38068                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
38069             };
38070         }else if(value.exec){ // regex?
38071             f = function(n){
38072                 return value.test(n.attributes[attr]);
38073             };
38074         }else{
38075             throw 'Illegal filter type, must be string or regex';
38076         }
38077         this.filterBy(f, null, startNode);
38078         },
38079
38080     /**
38081      * Filter by a function. The passed function will be called with each
38082      * node in the tree (or from the startNode). If the function returns true, the node is kept
38083      * otherwise it is filtered. If a node is filtered, its children are also filtered.
38084      * @param {Function} fn The filter function
38085      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
38086      */
38087     filterBy : function(fn, scope, startNode){
38088         startNode = startNode || this.tree.root;
38089         if(this.autoClear){
38090             this.clear();
38091         }
38092         var af = this.filtered, rv = this.reverse;
38093         var f = function(n){
38094             if(n == startNode){
38095                 return true;
38096             }
38097             if(af[n.id]){
38098                 return false;
38099             }
38100             var m = fn.call(scope || n, n);
38101             if(!m || rv){
38102                 af[n.id] = n;
38103                 n.ui.hide();
38104                 return false;
38105             }
38106             return true;
38107         };
38108         startNode.cascade(f);
38109         if(this.remove){
38110            for(var id in af){
38111                if(typeof id != "function"){
38112                    var n = af[id];
38113                    if(n && n.parentNode){
38114                        n.parentNode.removeChild(n);
38115                    }
38116                }
38117            }
38118         }
38119     },
38120
38121     /**
38122      * Clears the current filter. Note: with the "remove" option
38123      * set a filter cannot be cleared.
38124      */
38125     clear : function(){
38126         var t = this.tree;
38127         var af = this.filtered;
38128         for(var id in af){
38129             if(typeof id != "function"){
38130                 var n = af[id];
38131                 if(n){
38132                     n.ui.show();
38133                 }
38134             }
38135         }
38136         this.filtered = {};
38137     }
38138 };
38139 /*
38140  * Based on:
38141  * Ext JS Library 1.1.1
38142  * Copyright(c) 2006-2007, Ext JS, LLC.
38143  *
38144  * Originally Released Under LGPL - original licence link has changed is not relivant.
38145  *
38146  * Fork - LGPL
38147  * <script type="text/javascript">
38148  */
38149  
38150
38151 /**
38152  * @class Roo.tree.TreeSorter
38153  * Provides sorting of nodes in a TreePanel
38154  * 
38155  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
38156  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
38157  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
38158  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
38159  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
38160  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
38161  * @constructor
38162  * @param {TreePanel} tree
38163  * @param {Object} config
38164  */
38165 Roo.tree.TreeSorter = function(tree, config){
38166     Roo.apply(this, config);
38167     tree.on("beforechildrenrendered", this.doSort, this);
38168     tree.on("append", this.updateSort, this);
38169     tree.on("insert", this.updateSort, this);
38170     
38171     var dsc = this.dir && this.dir.toLowerCase() == "desc";
38172     var p = this.property || "text";
38173     var sortType = this.sortType;
38174     var fs = this.folderSort;
38175     var cs = this.caseSensitive === true;
38176     var leafAttr = this.leafAttr || 'leaf';
38177
38178     this.sortFn = function(n1, n2){
38179         if(fs){
38180             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
38181                 return 1;
38182             }
38183             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
38184                 return -1;
38185             }
38186         }
38187         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
38188         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
38189         if(v1 < v2){
38190                         return dsc ? +1 : -1;
38191                 }else if(v1 > v2){
38192                         return dsc ? -1 : +1;
38193         }else{
38194                 return 0;
38195         }
38196     };
38197 };
38198
38199 Roo.tree.TreeSorter.prototype = {
38200     doSort : function(node){
38201         node.sort(this.sortFn);
38202     },
38203     
38204     compareNodes : function(n1, n2){
38205         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
38206     },
38207     
38208     updateSort : function(tree, node){
38209         if(node.childrenRendered){
38210             this.doSort.defer(1, this, [node]);
38211         }
38212     }
38213 };/*
38214  * Based on:
38215  * Ext JS Library 1.1.1
38216  * Copyright(c) 2006-2007, Ext JS, LLC.
38217  *
38218  * Originally Released Under LGPL - original licence link has changed is not relivant.
38219  *
38220  * Fork - LGPL
38221  * <script type="text/javascript">
38222  */
38223
38224 if(Roo.dd.DropZone){
38225     
38226 Roo.tree.TreeDropZone = function(tree, config){
38227     this.allowParentInsert = false;
38228     this.allowContainerDrop = false;
38229     this.appendOnly = false;
38230     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38231     this.tree = tree;
38232     this.lastInsertClass = "x-tree-no-status";
38233     this.dragOverData = {};
38234 };
38235
38236 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38237     ddGroup : "TreeDD",
38238     scroll:  true,
38239     
38240     expandDelay : 1000,
38241     
38242     expandNode : function(node){
38243         if(node.hasChildNodes() && !node.isExpanded()){
38244             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38245         }
38246     },
38247     
38248     queueExpand : function(node){
38249         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38250     },
38251     
38252     cancelExpand : function(){
38253         if(this.expandProcId){
38254             clearTimeout(this.expandProcId);
38255             this.expandProcId = false;
38256         }
38257     },
38258     
38259     isValidDropPoint : function(n, pt, dd, e, data){
38260         if(!n || !data){ return false; }
38261         var targetNode = n.node;
38262         var dropNode = data.node;
38263         // default drop rules
38264         if(!(targetNode && targetNode.isTarget && pt)){
38265             return false;
38266         }
38267         if(pt == "append" && targetNode.allowChildren === false){
38268             return false;
38269         }
38270         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38271             return false;
38272         }
38273         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38274             return false;
38275         }
38276         // reuse the object
38277         var overEvent = this.dragOverData;
38278         overEvent.tree = this.tree;
38279         overEvent.target = targetNode;
38280         overEvent.data = data;
38281         overEvent.point = pt;
38282         overEvent.source = dd;
38283         overEvent.rawEvent = e;
38284         overEvent.dropNode = dropNode;
38285         overEvent.cancel = false;  
38286         var result = this.tree.fireEvent("nodedragover", overEvent);
38287         return overEvent.cancel === false && result !== false;
38288     },
38289     
38290     getDropPoint : function(e, n, dd)
38291     {
38292         var tn = n.node;
38293         if(tn.isRoot){
38294             return tn.allowChildren !== false ? "append" : false; // always append for root
38295         }
38296         var dragEl = n.ddel;
38297         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38298         var y = Roo.lib.Event.getPageY(e);
38299         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38300         
38301         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38302         var noAppend = tn.allowChildren === false;
38303         if(this.appendOnly || tn.parentNode.allowChildren === false){
38304             return noAppend ? false : "append";
38305         }
38306         var noBelow = false;
38307         if(!this.allowParentInsert){
38308             noBelow = tn.hasChildNodes() && tn.isExpanded();
38309         }
38310         var q = (b - t) / (noAppend ? 2 : 3);
38311         if(y >= t && y < (t + q)){
38312             return "above";
38313         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38314             return "below";
38315         }else{
38316             return "append";
38317         }
38318     },
38319     
38320     onNodeEnter : function(n, dd, e, data)
38321     {
38322         this.cancelExpand();
38323     },
38324     
38325     onNodeOver : function(n, dd, e, data)
38326     {
38327        
38328         var pt = this.getDropPoint(e, n, dd);
38329         var node = n.node;
38330         
38331         // auto node expand check
38332         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38333             this.queueExpand(node);
38334         }else if(pt != "append"){
38335             this.cancelExpand();
38336         }
38337         
38338         // set the insert point style on the target node
38339         var returnCls = this.dropNotAllowed;
38340         if(this.isValidDropPoint(n, pt, dd, e, data)){
38341            if(pt){
38342                var el = n.ddel;
38343                var cls;
38344                if(pt == "above"){
38345                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38346                    cls = "x-tree-drag-insert-above";
38347                }else if(pt == "below"){
38348                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38349                    cls = "x-tree-drag-insert-below";
38350                }else{
38351                    returnCls = "x-tree-drop-ok-append";
38352                    cls = "x-tree-drag-append";
38353                }
38354                if(this.lastInsertClass != cls){
38355                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38356                    this.lastInsertClass = cls;
38357                }
38358            }
38359        }
38360        return returnCls;
38361     },
38362     
38363     onNodeOut : function(n, dd, e, data){
38364         
38365         this.cancelExpand();
38366         this.removeDropIndicators(n);
38367     },
38368     
38369     onNodeDrop : function(n, dd, e, data){
38370         var point = this.getDropPoint(e, n, dd);
38371         var targetNode = n.node;
38372         targetNode.ui.startDrop();
38373         if(!this.isValidDropPoint(n, point, dd, e, data)){
38374             targetNode.ui.endDrop();
38375             return false;
38376         }
38377         // first try to find the drop node
38378         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38379         var dropEvent = {
38380             tree : this.tree,
38381             target: targetNode,
38382             data: data,
38383             point: point,
38384             source: dd,
38385             rawEvent: e,
38386             dropNode: dropNode,
38387             cancel: !dropNode   
38388         };
38389         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38390         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38391             targetNode.ui.endDrop();
38392             return false;
38393         }
38394         // allow target changing
38395         targetNode = dropEvent.target;
38396         if(point == "append" && !targetNode.isExpanded()){
38397             targetNode.expand(false, null, function(){
38398                 this.completeDrop(dropEvent);
38399             }.createDelegate(this));
38400         }else{
38401             this.completeDrop(dropEvent);
38402         }
38403         return true;
38404     },
38405     
38406     completeDrop : function(de){
38407         var ns = de.dropNode, p = de.point, t = de.target;
38408         if(!(ns instanceof Array)){
38409             ns = [ns];
38410         }
38411         var n;
38412         for(var i = 0, len = ns.length; i < len; i++){
38413             n = ns[i];
38414             if(p == "above"){
38415                 t.parentNode.insertBefore(n, t);
38416             }else if(p == "below"){
38417                 t.parentNode.insertBefore(n, t.nextSibling);
38418             }else{
38419                 t.appendChild(n);
38420             }
38421         }
38422         n.ui.focus();
38423         if(this.tree.hlDrop){
38424             n.ui.highlight();
38425         }
38426         t.ui.endDrop();
38427         this.tree.fireEvent("nodedrop", de);
38428     },
38429     
38430     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38431         if(this.tree.hlDrop){
38432             dropNode.ui.focus();
38433             dropNode.ui.highlight();
38434         }
38435         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38436     },
38437     
38438     getTree : function(){
38439         return this.tree;
38440     },
38441     
38442     removeDropIndicators : function(n){
38443         if(n && n.ddel){
38444             var el = n.ddel;
38445             Roo.fly(el).removeClass([
38446                     "x-tree-drag-insert-above",
38447                     "x-tree-drag-insert-below",
38448                     "x-tree-drag-append"]);
38449             this.lastInsertClass = "_noclass";
38450         }
38451     },
38452     
38453     beforeDragDrop : function(target, e, id){
38454         this.cancelExpand();
38455         return true;
38456     },
38457     
38458     afterRepair : function(data){
38459         if(data && Roo.enableFx){
38460             data.node.ui.highlight();
38461         }
38462         this.hideProxy();
38463     } 
38464     
38465 });
38466
38467 }
38468 /*
38469  * Based on:
38470  * Ext JS Library 1.1.1
38471  * Copyright(c) 2006-2007, Ext JS, LLC.
38472  *
38473  * Originally Released Under LGPL - original licence link has changed is not relivant.
38474  *
38475  * Fork - LGPL
38476  * <script type="text/javascript">
38477  */
38478  
38479
38480 if(Roo.dd.DragZone){
38481 Roo.tree.TreeDragZone = function(tree, config){
38482     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38483     this.tree = tree;
38484 };
38485
38486 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38487     ddGroup : "TreeDD",
38488    
38489     onBeforeDrag : function(data, e){
38490         var n = data.node;
38491         return n && n.draggable && !n.disabled;
38492     },
38493      
38494     
38495     onInitDrag : function(e){
38496         var data = this.dragData;
38497         this.tree.getSelectionModel().select(data.node);
38498         this.proxy.update("");
38499         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38500         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38501     },
38502     
38503     getRepairXY : function(e, data){
38504         return data.node.ui.getDDRepairXY();
38505     },
38506     
38507     onEndDrag : function(data, e){
38508         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38509         
38510         
38511     },
38512     
38513     onValidDrop : function(dd, e, id){
38514         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38515         this.hideProxy();
38516     },
38517     
38518     beforeInvalidDrop : function(e, id){
38519         // this scrolls the original position back into view
38520         var sm = this.tree.getSelectionModel();
38521         sm.clearSelections();
38522         sm.select(this.dragData.node);
38523     }
38524 });
38525 }/*
38526  * Based on:
38527  * Ext JS Library 1.1.1
38528  * Copyright(c) 2006-2007, Ext JS, LLC.
38529  *
38530  * Originally Released Under LGPL - original licence link has changed is not relivant.
38531  *
38532  * Fork - LGPL
38533  * <script type="text/javascript">
38534  */
38535 /**
38536  * @class Roo.tree.TreeEditor
38537  * @extends Roo.Editor
38538  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38539  * as the editor field.
38540  * @constructor
38541  * @param {Object} config (used to be the tree panel.)
38542  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38543  * 
38544  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38545  * @cfg {Roo.form.TextField} field [required] The field configuration
38546  *
38547  * 
38548  */
38549 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38550     var tree = config;
38551     var field;
38552     if (oldconfig) { // old style..
38553         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38554     } else {
38555         // new style..
38556         tree = config.tree;
38557         config.field = config.field  || {};
38558         config.field.xtype = 'TextField';
38559         field = Roo.factory(config.field, Roo.form);
38560     }
38561     config = config || {};
38562     
38563     
38564     this.addEvents({
38565         /**
38566          * @event beforenodeedit
38567          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38568          * false from the handler of this event.
38569          * @param {Editor} this
38570          * @param {Roo.tree.Node} node 
38571          */
38572         "beforenodeedit" : true
38573     });
38574     
38575     //Roo.log(config);
38576     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38577
38578     this.tree = tree;
38579
38580     tree.on('beforeclick', this.beforeNodeClick, this);
38581     tree.getTreeEl().on('mousedown', this.hide, this);
38582     this.on('complete', this.updateNode, this);
38583     this.on('beforestartedit', this.fitToTree, this);
38584     this.on('startedit', this.bindScroll, this, {delay:10});
38585     this.on('specialkey', this.onSpecialKey, this);
38586 };
38587
38588 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38589     /**
38590      * @cfg {String} alignment
38591      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38592      */
38593     alignment: "l-l",
38594     // inherit
38595     autoSize: false,
38596     /**
38597      * @cfg {Boolean} hideEl
38598      * True to hide the bound element while the editor is displayed (defaults to false)
38599      */
38600     hideEl : false,
38601     /**
38602      * @cfg {String} cls
38603      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38604      */
38605     cls: "x-small-editor x-tree-editor",
38606     /**
38607      * @cfg {Boolean} shim
38608      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38609      */
38610     shim:false,
38611     // inherit
38612     shadow:"frame",
38613     /**
38614      * @cfg {Number} maxWidth
38615      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38616      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38617      * scroll and client offsets into account prior to each edit.
38618      */
38619     maxWidth: 250,
38620
38621     editDelay : 350,
38622
38623     // private
38624     fitToTree : function(ed, el){
38625         var td = this.tree.getTreeEl().dom, nd = el.dom;
38626         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38627             td.scrollLeft = nd.offsetLeft;
38628         }
38629         var w = Math.min(
38630                 this.maxWidth,
38631                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38632         this.setSize(w, '');
38633         
38634         return this.fireEvent('beforenodeedit', this, this.editNode);
38635         
38636     },
38637
38638     // private
38639     triggerEdit : function(node){
38640         this.completeEdit();
38641         this.editNode = node;
38642         this.startEdit(node.ui.textNode, node.text);
38643     },
38644
38645     // private
38646     bindScroll : function(){
38647         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38648     },
38649
38650     // private
38651     beforeNodeClick : function(node, e){
38652         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38653         this.lastClick = new Date();
38654         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38655             e.stopEvent();
38656             this.triggerEdit(node);
38657             return false;
38658         }
38659         return true;
38660     },
38661
38662     // private
38663     updateNode : function(ed, value){
38664         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38665         this.editNode.setText(value);
38666     },
38667
38668     // private
38669     onHide : function(){
38670         Roo.tree.TreeEditor.superclass.onHide.call(this);
38671         if(this.editNode){
38672             this.editNode.ui.focus();
38673         }
38674     },
38675
38676     // private
38677     onSpecialKey : function(field, e){
38678         var k = e.getKey();
38679         if(k == e.ESC){
38680             e.stopEvent();
38681             this.cancelEdit();
38682         }else if(k == e.ENTER && !e.hasModifier()){
38683             e.stopEvent();
38684             this.completeEdit();
38685         }
38686     }
38687 });//<Script type="text/javascript">
38688 /*
38689  * Based on:
38690  * Ext JS Library 1.1.1
38691  * Copyright(c) 2006-2007, Ext JS, LLC.
38692  *
38693  * Originally Released Under LGPL - original licence link has changed is not relivant.
38694  *
38695  * Fork - LGPL
38696  * <script type="text/javascript">
38697  */
38698  
38699 /**
38700  * Not documented??? - probably should be...
38701  */
38702
38703 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38704     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38705     
38706     renderElements : function(n, a, targetNode, bulkRender){
38707         //consel.log("renderElements?");
38708         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38709
38710         var t = n.getOwnerTree();
38711         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38712         
38713         var cols = t.columns;
38714         var bw = t.borderWidth;
38715         var c = cols[0];
38716         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38717          var cb = typeof a.checked == "boolean";
38718         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38719         var colcls = 'x-t-' + tid + '-c0';
38720         var buf = [
38721             '<li class="x-tree-node">',
38722             
38723                 
38724                 '<div class="x-tree-node-el ', a.cls,'">',
38725                     // extran...
38726                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38727                 
38728                 
38729                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38730                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38731                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38732                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38733                            (a.iconCls ? ' '+a.iconCls : ''),
38734                            '" unselectable="on" />',
38735                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38736                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38737                              
38738                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38739                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38740                             '<span unselectable="on" qtip="' + tx + '">',
38741                              tx,
38742                              '</span></a>' ,
38743                     '</div>',
38744                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38745                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38746                  ];
38747         for(var i = 1, len = cols.length; i < len; i++){
38748             c = cols[i];
38749             colcls = 'x-t-' + tid + '-c' +i;
38750             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38751             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38752                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38753                       "</div>");
38754          }
38755          
38756          buf.push(
38757             '</a>',
38758             '<div class="x-clear"></div></div>',
38759             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38760             "</li>");
38761         
38762         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38763             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38764                                 n.nextSibling.ui.getEl(), buf.join(""));
38765         }else{
38766             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38767         }
38768         var el = this.wrap.firstChild;
38769         this.elRow = el;
38770         this.elNode = el.firstChild;
38771         this.ranchor = el.childNodes[1];
38772         this.ctNode = this.wrap.childNodes[1];
38773         var cs = el.firstChild.childNodes;
38774         this.indentNode = cs[0];
38775         this.ecNode = cs[1];
38776         this.iconNode = cs[2];
38777         var index = 3;
38778         if(cb){
38779             this.checkbox = cs[3];
38780             index++;
38781         }
38782         this.anchor = cs[index];
38783         
38784         this.textNode = cs[index].firstChild;
38785         
38786         //el.on("click", this.onClick, this);
38787         //el.on("dblclick", this.onDblClick, this);
38788         
38789         
38790        // console.log(this);
38791     },
38792     initEvents : function(){
38793         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38794         
38795             
38796         var a = this.ranchor;
38797
38798         var el = Roo.get(a);
38799
38800         if(Roo.isOpera){ // opera render bug ignores the CSS
38801             el.setStyle("text-decoration", "none");
38802         }
38803
38804         el.on("click", this.onClick, this);
38805         el.on("dblclick", this.onDblClick, this);
38806         el.on("contextmenu", this.onContextMenu, this);
38807         
38808     },
38809     
38810     /*onSelectedChange : function(state){
38811         if(state){
38812             this.focus();
38813             this.addClass("x-tree-selected");
38814         }else{
38815             //this.blur();
38816             this.removeClass("x-tree-selected");
38817         }
38818     },*/
38819     addClass : function(cls){
38820         if(this.elRow){
38821             Roo.fly(this.elRow).addClass(cls);
38822         }
38823         
38824     },
38825     
38826     
38827     removeClass : function(cls){
38828         if(this.elRow){
38829             Roo.fly(this.elRow).removeClass(cls);
38830         }
38831     }
38832
38833     
38834     
38835 });//<Script type="text/javascript">
38836
38837 /*
38838  * Based on:
38839  * Ext JS Library 1.1.1
38840  * Copyright(c) 2006-2007, Ext JS, LLC.
38841  *
38842  * Originally Released Under LGPL - original licence link has changed is not relivant.
38843  *
38844  * Fork - LGPL
38845  * <script type="text/javascript">
38846  */
38847  
38848
38849 /**
38850  * @class Roo.tree.ColumnTree
38851  * @extends Roo.tree.TreePanel
38852  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38853  * @cfg {int} borderWidth  compined right/left border allowance
38854  * @constructor
38855  * @param {String/HTMLElement/Element} el The container element
38856  * @param {Object} config
38857  */
38858 Roo.tree.ColumnTree =  function(el, config)
38859 {
38860    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38861    this.addEvents({
38862         /**
38863         * @event resize
38864         * Fire this event on a container when it resizes
38865         * @param {int} w Width
38866         * @param {int} h Height
38867         */
38868        "resize" : true
38869     });
38870     this.on('resize', this.onResize, this);
38871 };
38872
38873 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38874     //lines:false,
38875     
38876     
38877     borderWidth: Roo.isBorderBox ? 0 : 2, 
38878     headEls : false,
38879     
38880     render : function(){
38881         // add the header.....
38882        
38883         Roo.tree.ColumnTree.superclass.render.apply(this);
38884         
38885         this.el.addClass('x-column-tree');
38886         
38887         this.headers = this.el.createChild(
38888             {cls:'x-tree-headers'},this.innerCt.dom);
38889    
38890         var cols = this.columns, c;
38891         var totalWidth = 0;
38892         this.headEls = [];
38893         var  len = cols.length;
38894         for(var i = 0; i < len; i++){
38895              c = cols[i];
38896              totalWidth += c.width;
38897             this.headEls.push(this.headers.createChild({
38898                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38899                  cn: {
38900                      cls:'x-tree-hd-text',
38901                      html: c.header
38902                  },
38903                  style:'width:'+(c.width-this.borderWidth)+'px;'
38904              }));
38905         }
38906         this.headers.createChild({cls:'x-clear'});
38907         // prevent floats from wrapping when clipped
38908         this.headers.setWidth(totalWidth);
38909         //this.innerCt.setWidth(totalWidth);
38910         this.innerCt.setStyle({ overflow: 'auto' });
38911         this.onResize(this.width, this.height);
38912              
38913         
38914     },
38915     onResize : function(w,h)
38916     {
38917         this.height = h;
38918         this.width = w;
38919         // resize cols..
38920         this.innerCt.setWidth(this.width);
38921         this.innerCt.setHeight(this.height-20);
38922         
38923         // headers...
38924         var cols = this.columns, c;
38925         var totalWidth = 0;
38926         var expEl = false;
38927         var len = cols.length;
38928         for(var i = 0; i < len; i++){
38929             c = cols[i];
38930             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38931                 // it's the expander..
38932                 expEl  = this.headEls[i];
38933                 continue;
38934             }
38935             totalWidth += c.width;
38936             
38937         }
38938         if (expEl) {
38939             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38940         }
38941         this.headers.setWidth(w-20);
38942
38943         
38944         
38945         
38946     }
38947 });
38948 /*
38949  * Based on:
38950  * Ext JS Library 1.1.1
38951  * Copyright(c) 2006-2007, Ext JS, LLC.
38952  *
38953  * Originally Released Under LGPL - original licence link has changed is not relivant.
38954  *
38955  * Fork - LGPL
38956  * <script type="text/javascript">
38957  */
38958  
38959 /**
38960  * @class Roo.menu.Menu
38961  * @extends Roo.util.Observable
38962  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38963  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38964  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38965  * @constructor
38966  * Creates a new Menu
38967  * @param {Object} config Configuration options
38968  */
38969 Roo.menu.Menu = function(config){
38970     
38971     Roo.menu.Menu.superclass.constructor.call(this, config);
38972     
38973     this.id = this.id || Roo.id();
38974     this.addEvents({
38975         /**
38976          * @event beforeshow
38977          * Fires before this menu is displayed
38978          * @param {Roo.menu.Menu} this
38979          */
38980         beforeshow : true,
38981         /**
38982          * @event beforehide
38983          * Fires before this menu is hidden
38984          * @param {Roo.menu.Menu} this
38985          */
38986         beforehide : true,
38987         /**
38988          * @event show
38989          * Fires after this menu is displayed
38990          * @param {Roo.menu.Menu} this
38991          */
38992         show : true,
38993         /**
38994          * @event hide
38995          * Fires after this menu is hidden
38996          * @param {Roo.menu.Menu} this
38997          */
38998         hide : true,
38999         /**
39000          * @event click
39001          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
39002          * @param {Roo.menu.Menu} this
39003          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39004          * @param {Roo.EventObject} e
39005          */
39006         click : true,
39007         /**
39008          * @event mouseover
39009          * Fires when the mouse is hovering over this menu
39010          * @param {Roo.menu.Menu} this
39011          * @param {Roo.EventObject} e
39012          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39013          */
39014         mouseover : true,
39015         /**
39016          * @event mouseout
39017          * Fires when the mouse exits this menu
39018          * @param {Roo.menu.Menu} this
39019          * @param {Roo.EventObject} e
39020          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39021          */
39022         mouseout : true,
39023         /**
39024          * @event itemclick
39025          * Fires when a menu item contained in this menu is clicked
39026          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
39027          * @param {Roo.EventObject} e
39028          */
39029         itemclick: true
39030     });
39031     if (this.registerMenu) {
39032         Roo.menu.MenuMgr.register(this);
39033     }
39034     
39035     var mis = this.items;
39036     this.items = new Roo.util.MixedCollection();
39037     if(mis){
39038         this.add.apply(this, mis);
39039     }
39040 };
39041
39042 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
39043     /**
39044      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
39045      */
39046     minWidth : 120,
39047     /**
39048      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
39049      * for bottom-right shadow (defaults to "sides")
39050      */
39051     shadow : "sides",
39052     /**
39053      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
39054      * this menu (defaults to "tl-tr?")
39055      */
39056     subMenuAlign : "tl-tr?",
39057     /**
39058      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
39059      * relative to its element of origin (defaults to "tl-bl?")
39060      */
39061     defaultAlign : "tl-bl?",
39062     /**
39063      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
39064      */
39065     allowOtherMenus : false,
39066     /**
39067      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
39068      */
39069     registerMenu : true,
39070
39071     hidden:true,
39072
39073     // private
39074     render : function(){
39075         if(this.el){
39076             return;
39077         }
39078         var el = this.el = new Roo.Layer({
39079             cls: "x-menu",
39080             shadow:this.shadow,
39081             constrain: false,
39082             parentEl: this.parentEl || document.body,
39083             zindex:15000
39084         });
39085
39086         this.keyNav = new Roo.menu.MenuNav(this);
39087
39088         if(this.plain){
39089             el.addClass("x-menu-plain");
39090         }
39091         if(this.cls){
39092             el.addClass(this.cls);
39093         }
39094         // generic focus element
39095         this.focusEl = el.createChild({
39096             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
39097         });
39098         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
39099         //disabling touch- as it's causing issues ..
39100         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
39101         ul.on('click'   , this.onClick, this);
39102         
39103         
39104         ul.on("mouseover", this.onMouseOver, this);
39105         ul.on("mouseout", this.onMouseOut, this);
39106         this.items.each(function(item){
39107             if (item.hidden) {
39108                 return;
39109             }
39110             
39111             var li = document.createElement("li");
39112             li.className = "x-menu-list-item";
39113             ul.dom.appendChild(li);
39114             item.render(li, this);
39115         }, this);
39116         this.ul = ul;
39117         this.autoWidth();
39118     },
39119
39120     // private
39121     autoWidth : function(){
39122         var el = this.el, ul = this.ul;
39123         if(!el){
39124             return;
39125         }
39126         var w = this.width;
39127         if(w){
39128             el.setWidth(w);
39129         }else if(Roo.isIE){
39130             el.setWidth(this.minWidth);
39131             var t = el.dom.offsetWidth; // force recalc
39132             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
39133         }
39134     },
39135
39136     // private
39137     delayAutoWidth : function(){
39138         if(this.rendered){
39139             if(!this.awTask){
39140                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
39141             }
39142             this.awTask.delay(20);
39143         }
39144     },
39145
39146     // private
39147     findTargetItem : function(e){
39148         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
39149         if(t && t.menuItemId){
39150             return this.items.get(t.menuItemId);
39151         }
39152     },
39153
39154     // private
39155     onClick : function(e){
39156         Roo.log("menu.onClick");
39157         var t = this.findTargetItem(e);
39158         if(!t){
39159             return;
39160         }
39161         Roo.log(e);
39162         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
39163             if(t == this.activeItem && t.shouldDeactivate(e)){
39164                 this.activeItem.deactivate();
39165                 delete this.activeItem;
39166                 return;
39167             }
39168             if(t.canActivate){
39169                 this.setActiveItem(t, true);
39170             }
39171             return;
39172             
39173             
39174         }
39175         
39176         t.onClick(e);
39177         this.fireEvent("click", this, t, e);
39178     },
39179
39180     // private
39181     setActiveItem : function(item, autoExpand){
39182         if(item != this.activeItem){
39183             if(this.activeItem){
39184                 this.activeItem.deactivate();
39185             }
39186             this.activeItem = item;
39187             item.activate(autoExpand);
39188         }else if(autoExpand){
39189             item.expandMenu();
39190         }
39191     },
39192
39193     // private
39194     tryActivate : function(start, step){
39195         var items = this.items;
39196         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
39197             var item = items.get(i);
39198             if(!item.disabled && item.canActivate){
39199                 this.setActiveItem(item, false);
39200                 return item;
39201             }
39202         }
39203         return false;
39204     },
39205
39206     // private
39207     onMouseOver : function(e){
39208         var t;
39209         if(t = this.findTargetItem(e)){
39210             if(t.canActivate && !t.disabled){
39211                 this.setActiveItem(t, true);
39212             }
39213         }
39214         this.fireEvent("mouseover", this, e, t);
39215     },
39216
39217     // private
39218     onMouseOut : function(e){
39219         var t;
39220         if(t = this.findTargetItem(e)){
39221             if(t == this.activeItem && t.shouldDeactivate(e)){
39222                 this.activeItem.deactivate();
39223                 delete this.activeItem;
39224             }
39225         }
39226         this.fireEvent("mouseout", this, e, t);
39227     },
39228
39229     /**
39230      * Read-only.  Returns true if the menu is currently displayed, else false.
39231      * @type Boolean
39232      */
39233     isVisible : function(){
39234         return this.el && !this.hidden;
39235     },
39236
39237     /**
39238      * Displays this menu relative to another element
39239      * @param {String/HTMLElement/Roo.Element} element The element to align to
39240      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39241      * the element (defaults to this.defaultAlign)
39242      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39243      */
39244     show : function(el, pos, parentMenu){
39245         this.parentMenu = parentMenu;
39246         if(!this.el){
39247             this.render();
39248         }
39249         this.fireEvent("beforeshow", this);
39250         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39251     },
39252
39253     /**
39254      * Displays this menu at a specific xy position
39255      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39256      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39257      */
39258     showAt : function(xy, parentMenu, /* private: */_e){
39259         this.parentMenu = parentMenu;
39260         if(!this.el){
39261             this.render();
39262         }
39263         if(_e !== false){
39264             this.fireEvent("beforeshow", this);
39265             xy = this.el.adjustForConstraints(xy);
39266         }
39267         this.el.setXY(xy);
39268         this.el.show();
39269         this.hidden = false;
39270         this.focus();
39271         this.fireEvent("show", this);
39272     },
39273
39274     focus : function(){
39275         if(!this.hidden){
39276             this.doFocus.defer(50, this);
39277         }
39278     },
39279
39280     doFocus : function(){
39281         if(!this.hidden){
39282             this.focusEl.focus();
39283         }
39284     },
39285
39286     /**
39287      * Hides this menu and optionally all parent menus
39288      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39289      */
39290     hide : function(deep){
39291         if(this.el && this.isVisible()){
39292             this.fireEvent("beforehide", this);
39293             if(this.activeItem){
39294                 this.activeItem.deactivate();
39295                 this.activeItem = null;
39296             }
39297             this.el.hide();
39298             this.hidden = true;
39299             this.fireEvent("hide", this);
39300         }
39301         if(deep === true && this.parentMenu){
39302             this.parentMenu.hide(true);
39303         }
39304     },
39305
39306     /**
39307      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39308      * Any of the following are valid:
39309      * <ul>
39310      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39311      * <li>An HTMLElement object which will be converted to a menu item</li>
39312      * <li>A menu item config object that will be created as a new menu item</li>
39313      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39314      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39315      * </ul>
39316      * Usage:
39317      * <pre><code>
39318 // Create the menu
39319 var menu = new Roo.menu.Menu();
39320
39321 // Create a menu item to add by reference
39322 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39323
39324 // Add a bunch of items at once using different methods.
39325 // Only the last item added will be returned.
39326 var item = menu.add(
39327     menuItem,                // add existing item by ref
39328     'Dynamic Item',          // new TextItem
39329     '-',                     // new separator
39330     { text: 'Config Item' }  // new item by config
39331 );
39332 </code></pre>
39333      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39334      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39335      */
39336     add : function(){
39337         var a = arguments, l = a.length, item;
39338         for(var i = 0; i < l; i++){
39339             var el = a[i];
39340             if ((typeof(el) == "object") && el.xtype && el.xns) {
39341                 el = Roo.factory(el, Roo.menu);
39342             }
39343             
39344             if(el.render){ // some kind of Item
39345                 item = this.addItem(el);
39346             }else if(typeof el == "string"){ // string
39347                 if(el == "separator" || el == "-"){
39348                     item = this.addSeparator();
39349                 }else{
39350                     item = this.addText(el);
39351                 }
39352             }else if(el.tagName || el.el){ // element
39353                 item = this.addElement(el);
39354             }else if(typeof el == "object"){ // must be menu item config?
39355                 item = this.addMenuItem(el);
39356             }
39357         }
39358         return item;
39359     },
39360
39361     /**
39362      * Returns this menu's underlying {@link Roo.Element} object
39363      * @return {Roo.Element} The element
39364      */
39365     getEl : function(){
39366         if(!this.el){
39367             this.render();
39368         }
39369         return this.el;
39370     },
39371
39372     /**
39373      * Adds a separator bar to the menu
39374      * @return {Roo.menu.Item} The menu item that was added
39375      */
39376     addSeparator : function(){
39377         return this.addItem(new Roo.menu.Separator());
39378     },
39379
39380     /**
39381      * Adds an {@link Roo.Element} object to the menu
39382      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39383      * @return {Roo.menu.Item} The menu item that was added
39384      */
39385     addElement : function(el){
39386         return this.addItem(new Roo.menu.BaseItem(el));
39387     },
39388
39389     /**
39390      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39391      * @param {Roo.menu.Item} item The menu item to add
39392      * @return {Roo.menu.Item} The menu item that was added
39393      */
39394     addItem : function(item){
39395         this.items.add(item);
39396         if(this.ul){
39397             var li = document.createElement("li");
39398             li.className = "x-menu-list-item";
39399             this.ul.dom.appendChild(li);
39400             item.render(li, this);
39401             this.delayAutoWidth();
39402         }
39403         return item;
39404     },
39405
39406     /**
39407      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39408      * @param {Object} config A MenuItem config object
39409      * @return {Roo.menu.Item} The menu item that was added
39410      */
39411     addMenuItem : function(config){
39412         if(!(config instanceof Roo.menu.Item)){
39413             if(typeof config.checked == "boolean"){ // must be check menu item config?
39414                 config = new Roo.menu.CheckItem(config);
39415             }else{
39416                 config = new Roo.menu.Item(config);
39417             }
39418         }
39419         return this.addItem(config);
39420     },
39421
39422     /**
39423      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39424      * @param {String} text The text to display in the menu item
39425      * @return {Roo.menu.Item} The menu item that was added
39426      */
39427     addText : function(text){
39428         return this.addItem(new Roo.menu.TextItem({ text : text }));
39429     },
39430
39431     /**
39432      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39433      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39434      * @param {Roo.menu.Item} item The menu item to add
39435      * @return {Roo.menu.Item} The menu item that was added
39436      */
39437     insert : function(index, item){
39438         this.items.insert(index, item);
39439         if(this.ul){
39440             var li = document.createElement("li");
39441             li.className = "x-menu-list-item";
39442             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39443             item.render(li, this);
39444             this.delayAutoWidth();
39445         }
39446         return item;
39447     },
39448
39449     /**
39450      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39451      * @param {Roo.menu.Item} item The menu item to remove
39452      */
39453     remove : function(item){
39454         this.items.removeKey(item.id);
39455         item.destroy();
39456     },
39457
39458     /**
39459      * Removes and destroys all items in the menu
39460      */
39461     removeAll : function(){
39462         var f;
39463         while(f = this.items.first()){
39464             this.remove(f);
39465         }
39466     }
39467 });
39468
39469 // MenuNav is a private utility class used internally by the Menu
39470 Roo.menu.MenuNav = function(menu){
39471     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39472     this.scope = this.menu = menu;
39473 };
39474
39475 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39476     doRelay : function(e, h){
39477         var k = e.getKey();
39478         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39479             this.menu.tryActivate(0, 1);
39480             return false;
39481         }
39482         return h.call(this.scope || this, e, this.menu);
39483     },
39484
39485     up : function(e, m){
39486         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39487             m.tryActivate(m.items.length-1, -1);
39488         }
39489     },
39490
39491     down : function(e, m){
39492         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39493             m.tryActivate(0, 1);
39494         }
39495     },
39496
39497     right : function(e, m){
39498         if(m.activeItem){
39499             m.activeItem.expandMenu(true);
39500         }
39501     },
39502
39503     left : function(e, m){
39504         m.hide();
39505         if(m.parentMenu && m.parentMenu.activeItem){
39506             m.parentMenu.activeItem.activate();
39507         }
39508     },
39509
39510     enter : function(e, m){
39511         if(m.activeItem){
39512             e.stopPropagation();
39513             m.activeItem.onClick(e);
39514             m.fireEvent("click", this, m.activeItem);
39515             return true;
39516         }
39517     }
39518 });/*
39519  * Based on:
39520  * Ext JS Library 1.1.1
39521  * Copyright(c) 2006-2007, Ext JS, LLC.
39522  *
39523  * Originally Released Under LGPL - original licence link has changed is not relivant.
39524  *
39525  * Fork - LGPL
39526  * <script type="text/javascript">
39527  */
39528  
39529 /**
39530  * @class Roo.menu.MenuMgr
39531  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39532  * @static
39533  */
39534 Roo.menu.MenuMgr = function(){
39535    var menus, active, groups = {}, attached = false, lastShow = new Date();
39536
39537    // private - called when first menu is created
39538    function init(){
39539        menus = {};
39540        active = new Roo.util.MixedCollection();
39541        Roo.get(document).addKeyListener(27, function(){
39542            if(active.length > 0){
39543                hideAll();
39544            }
39545        });
39546    }
39547
39548    // private
39549    function hideAll(){
39550        if(active && active.length > 0){
39551            var c = active.clone();
39552            c.each(function(m){
39553                m.hide();
39554            });
39555        }
39556    }
39557
39558    // private
39559    function onHide(m){
39560        active.remove(m);
39561        if(active.length < 1){
39562            Roo.get(document).un("mousedown", onMouseDown);
39563            attached = false;
39564        }
39565    }
39566
39567    // private
39568    function onShow(m){
39569        var last = active.last();
39570        lastShow = new Date();
39571        active.add(m);
39572        if(!attached){
39573            Roo.get(document).on("mousedown", onMouseDown);
39574            attached = true;
39575        }
39576        if(m.parentMenu){
39577           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39578           m.parentMenu.activeChild = m;
39579        }else if(last && last.isVisible()){
39580           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39581        }
39582    }
39583
39584    // private
39585    function onBeforeHide(m){
39586        if(m.activeChild){
39587            m.activeChild.hide();
39588        }
39589        if(m.autoHideTimer){
39590            clearTimeout(m.autoHideTimer);
39591            delete m.autoHideTimer;
39592        }
39593    }
39594
39595    // private
39596    function onBeforeShow(m){
39597        var pm = m.parentMenu;
39598        if(!pm && !m.allowOtherMenus){
39599            hideAll();
39600        }else if(pm && pm.activeChild && active != m){
39601            pm.activeChild.hide();
39602        }
39603    }
39604
39605    // private
39606    function onMouseDown(e){
39607        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39608            hideAll();
39609        }
39610    }
39611
39612    // private
39613    function onBeforeCheck(mi, state){
39614        if(state){
39615            var g = groups[mi.group];
39616            for(var i = 0, l = g.length; i < l; i++){
39617                if(g[i] != mi){
39618                    g[i].setChecked(false);
39619                }
39620            }
39621        }
39622    }
39623
39624    return {
39625
39626        /**
39627         * Hides all menus that are currently visible
39628         */
39629        hideAll : function(){
39630             hideAll();  
39631        },
39632
39633        // private
39634        register : function(menu){
39635            if(!menus){
39636                init();
39637            }
39638            menus[menu.id] = menu;
39639            menu.on("beforehide", onBeforeHide);
39640            menu.on("hide", onHide);
39641            menu.on("beforeshow", onBeforeShow);
39642            menu.on("show", onShow);
39643            var g = menu.group;
39644            if(g && menu.events["checkchange"]){
39645                if(!groups[g]){
39646                    groups[g] = [];
39647                }
39648                groups[g].push(menu);
39649                menu.on("checkchange", onCheck);
39650            }
39651        },
39652
39653         /**
39654          * Returns a {@link Roo.menu.Menu} object
39655          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39656          * be used to generate and return a new Menu instance.
39657          */
39658        get : function(menu){
39659            if(typeof menu == "string"){ // menu id
39660                return menus[menu];
39661            }else if(menu.events){  // menu instance
39662                return menu;
39663            }else if(typeof menu.length == 'number'){ // array of menu items?
39664                return new Roo.menu.Menu({items:menu});
39665            }else{ // otherwise, must be a config
39666                return new Roo.menu.Menu(menu);
39667            }
39668        },
39669
39670        // private
39671        unregister : function(menu){
39672            delete menus[menu.id];
39673            menu.un("beforehide", onBeforeHide);
39674            menu.un("hide", onHide);
39675            menu.un("beforeshow", onBeforeShow);
39676            menu.un("show", onShow);
39677            var g = menu.group;
39678            if(g && menu.events["checkchange"]){
39679                groups[g].remove(menu);
39680                menu.un("checkchange", onCheck);
39681            }
39682        },
39683
39684        // private
39685        registerCheckable : function(menuItem){
39686            var g = menuItem.group;
39687            if(g){
39688                if(!groups[g]){
39689                    groups[g] = [];
39690                }
39691                groups[g].push(menuItem);
39692                menuItem.on("beforecheckchange", onBeforeCheck);
39693            }
39694        },
39695
39696        // private
39697        unregisterCheckable : function(menuItem){
39698            var g = menuItem.group;
39699            if(g){
39700                groups[g].remove(menuItem);
39701                menuItem.un("beforecheckchange", onBeforeCheck);
39702            }
39703        }
39704    };
39705 }();/*
39706  * Based on:
39707  * Ext JS Library 1.1.1
39708  * Copyright(c) 2006-2007, Ext JS, LLC.
39709  *
39710  * Originally Released Under LGPL - original licence link has changed is not relivant.
39711  *
39712  * Fork - LGPL
39713  * <script type="text/javascript">
39714  */
39715  
39716
39717 /**
39718  * @class Roo.menu.BaseItem
39719  * @extends Roo.Component
39720  * @abstract
39721  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39722  * management and base configuration options shared by all menu components.
39723  * @constructor
39724  * Creates a new BaseItem
39725  * @param {Object} config Configuration options
39726  */
39727 Roo.menu.BaseItem = function(config){
39728     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39729
39730     this.addEvents({
39731         /**
39732          * @event click
39733          * Fires when this item is clicked
39734          * @param {Roo.menu.BaseItem} this
39735          * @param {Roo.EventObject} e
39736          */
39737         click: true,
39738         /**
39739          * @event activate
39740          * Fires when this item is activated
39741          * @param {Roo.menu.BaseItem} this
39742          */
39743         activate : true,
39744         /**
39745          * @event deactivate
39746          * Fires when this item is deactivated
39747          * @param {Roo.menu.BaseItem} this
39748          */
39749         deactivate : true
39750     });
39751
39752     if(this.handler){
39753         this.on("click", this.handler, this.scope, true);
39754     }
39755 };
39756
39757 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39758     /**
39759      * @cfg {Function} handler
39760      * A function that will handle the click event of this menu item (defaults to undefined)
39761      */
39762     /**
39763      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39764      */
39765     canActivate : false,
39766     
39767      /**
39768      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39769      */
39770     hidden: false,
39771     
39772     /**
39773      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39774      */
39775     activeClass : "x-menu-item-active",
39776     /**
39777      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39778      */
39779     hideOnClick : true,
39780     /**
39781      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39782      */
39783     hideDelay : 100,
39784
39785     // private
39786     ctype: "Roo.menu.BaseItem",
39787
39788     // private
39789     actionMode : "container",
39790
39791     // private
39792     render : function(container, parentMenu){
39793         this.parentMenu = parentMenu;
39794         Roo.menu.BaseItem.superclass.render.call(this, container);
39795         this.container.menuItemId = this.id;
39796     },
39797
39798     // private
39799     onRender : function(container, position){
39800         this.el = Roo.get(this.el);
39801         container.dom.appendChild(this.el.dom);
39802     },
39803
39804     // private
39805     onClick : function(e){
39806         if(!this.disabled && this.fireEvent("click", this, e) !== false
39807                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39808             this.handleClick(e);
39809         }else{
39810             e.stopEvent();
39811         }
39812     },
39813
39814     // private
39815     activate : function(){
39816         if(this.disabled){
39817             return false;
39818         }
39819         var li = this.container;
39820         li.addClass(this.activeClass);
39821         this.region = li.getRegion().adjust(2, 2, -2, -2);
39822         this.fireEvent("activate", this);
39823         return true;
39824     },
39825
39826     // private
39827     deactivate : function(){
39828         this.container.removeClass(this.activeClass);
39829         this.fireEvent("deactivate", this);
39830     },
39831
39832     // private
39833     shouldDeactivate : function(e){
39834         return !this.region || !this.region.contains(e.getPoint());
39835     },
39836
39837     // private
39838     handleClick : function(e){
39839         if(this.hideOnClick){
39840             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39841         }
39842     },
39843
39844     // private
39845     expandMenu : function(autoActivate){
39846         // do nothing
39847     },
39848
39849     // private
39850     hideMenu : function(){
39851         // do nothing
39852     }
39853 });/*
39854  * Based on:
39855  * Ext JS Library 1.1.1
39856  * Copyright(c) 2006-2007, Ext JS, LLC.
39857  *
39858  * Originally Released Under LGPL - original licence link has changed is not relivant.
39859  *
39860  * Fork - LGPL
39861  * <script type="text/javascript">
39862  */
39863  
39864 /**
39865  * @class Roo.menu.Adapter
39866  * @extends Roo.menu.BaseItem
39867  * @abstract
39868  * 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.
39869  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39870  * @constructor
39871  * Creates a new Adapter
39872  * @param {Object} config Configuration options
39873  */
39874 Roo.menu.Adapter = function(component, config){
39875     Roo.menu.Adapter.superclass.constructor.call(this, config);
39876     this.component = component;
39877 };
39878 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39879     // private
39880     canActivate : true,
39881
39882     // private
39883     onRender : function(container, position){
39884         this.component.render(container);
39885         this.el = this.component.getEl();
39886     },
39887
39888     // private
39889     activate : function(){
39890         if(this.disabled){
39891             return false;
39892         }
39893         this.component.focus();
39894         this.fireEvent("activate", this);
39895         return true;
39896     },
39897
39898     // private
39899     deactivate : function(){
39900         this.fireEvent("deactivate", this);
39901     },
39902
39903     // private
39904     disable : function(){
39905         this.component.disable();
39906         Roo.menu.Adapter.superclass.disable.call(this);
39907     },
39908
39909     // private
39910     enable : function(){
39911         this.component.enable();
39912         Roo.menu.Adapter.superclass.enable.call(this);
39913     }
39914 });/*
39915  * Based on:
39916  * Ext JS Library 1.1.1
39917  * Copyright(c) 2006-2007, Ext JS, LLC.
39918  *
39919  * Originally Released Under LGPL - original licence link has changed is not relivant.
39920  *
39921  * Fork - LGPL
39922  * <script type="text/javascript">
39923  */
39924
39925 /**
39926  * @class Roo.menu.TextItem
39927  * @extends Roo.menu.BaseItem
39928  * Adds a static text string to a menu, usually used as either a heading or group separator.
39929  * Note: old style constructor with text is still supported.
39930  * 
39931  * @constructor
39932  * Creates a new TextItem
39933  * @param {Object} cfg Configuration
39934  */
39935 Roo.menu.TextItem = function(cfg){
39936     if (typeof(cfg) == 'string') {
39937         this.text = cfg;
39938     } else {
39939         Roo.apply(this,cfg);
39940     }
39941     
39942     Roo.menu.TextItem.superclass.constructor.call(this);
39943 };
39944
39945 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39946     /**
39947      * @cfg {String} text Text to show on item.
39948      */
39949     text : '',
39950     
39951     /**
39952      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39953      */
39954     hideOnClick : false,
39955     /**
39956      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39957      */
39958     itemCls : "x-menu-text",
39959
39960     // private
39961     onRender : function(){
39962         var s = document.createElement("span");
39963         s.className = this.itemCls;
39964         s.innerHTML = this.text;
39965         this.el = s;
39966         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39967     }
39968 });/*
39969  * Based on:
39970  * Ext JS Library 1.1.1
39971  * Copyright(c) 2006-2007, Ext JS, LLC.
39972  *
39973  * Originally Released Under LGPL - original licence link has changed is not relivant.
39974  *
39975  * Fork - LGPL
39976  * <script type="text/javascript">
39977  */
39978
39979 /**
39980  * @class Roo.menu.Separator
39981  * @extends Roo.menu.BaseItem
39982  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39983  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39984  * @constructor
39985  * @param {Object} config Configuration options
39986  */
39987 Roo.menu.Separator = function(config){
39988     Roo.menu.Separator.superclass.constructor.call(this, config);
39989 };
39990
39991 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39992     /**
39993      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39994      */
39995     itemCls : "x-menu-sep",
39996     /**
39997      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39998      */
39999     hideOnClick : false,
40000
40001     // private
40002     onRender : function(li){
40003         var s = document.createElement("span");
40004         s.className = this.itemCls;
40005         s.innerHTML = "&#160;";
40006         this.el = s;
40007         li.addClass("x-menu-sep-li");
40008         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
40009     }
40010 });/*
40011  * Based on:
40012  * Ext JS Library 1.1.1
40013  * Copyright(c) 2006-2007, Ext JS, LLC.
40014  *
40015  * Originally Released Under LGPL - original licence link has changed is not relivant.
40016  *
40017  * Fork - LGPL
40018  * <script type="text/javascript">
40019  */
40020 /**
40021  * @class Roo.menu.Item
40022  * @extends Roo.menu.BaseItem
40023  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
40024  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
40025  * activation and click handling.
40026  * @constructor
40027  * Creates a new Item
40028  * @param {Object} config Configuration options
40029  */
40030 Roo.menu.Item = function(config){
40031     Roo.menu.Item.superclass.constructor.call(this, config);
40032     if(this.menu){
40033         this.menu = Roo.menu.MenuMgr.get(this.menu);
40034     }
40035 };
40036 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
40037     /**
40038      * @cfg {Roo.menu.Menu} menu
40039      * A Sub menu
40040      */
40041     /**
40042      * @cfg {String} text
40043      * The text to show on the menu item.
40044      */
40045     text: '',
40046      /**
40047      * @cfg {String} html to render in menu
40048      * The text to show on the menu item (HTML version).
40049      */
40050     html: '',
40051     /**
40052      * @cfg {String} icon
40053      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
40054      */
40055     icon: undefined,
40056     /**
40057      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
40058      */
40059     itemCls : "x-menu-item",
40060     /**
40061      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
40062      */
40063     canActivate : true,
40064     /**
40065      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
40066      */
40067     showDelay: 200,
40068     // doc'd in BaseItem
40069     hideDelay: 200,
40070
40071     // private
40072     ctype: "Roo.menu.Item",
40073     
40074     // private
40075     onRender : function(container, position){
40076         var el = document.createElement("a");
40077         el.hideFocus = true;
40078         el.unselectable = "on";
40079         el.href = this.href || "#";
40080         if(this.hrefTarget){
40081             el.target = this.hrefTarget;
40082         }
40083         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
40084         
40085         var html = this.html.length ? this.html  : String.format('{0}',this.text);
40086         
40087         el.innerHTML = String.format(
40088                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
40089                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
40090         this.el = el;
40091         Roo.menu.Item.superclass.onRender.call(this, container, position);
40092     },
40093
40094     /**
40095      * Sets the text to display in this menu item
40096      * @param {String} text The text to display
40097      * @param {Boolean} isHTML true to indicate text is pure html.
40098      */
40099     setText : function(text, isHTML){
40100         if (isHTML) {
40101             this.html = text;
40102         } else {
40103             this.text = text;
40104             this.html = '';
40105         }
40106         if(this.rendered){
40107             var html = this.html.length ? this.html  : String.format('{0}',this.text);
40108      
40109             this.el.update(String.format(
40110                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
40111                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
40112             this.parentMenu.autoWidth();
40113         }
40114     },
40115
40116     // private
40117     handleClick : function(e){
40118         if(!this.href){ // if no link defined, stop the event automatically
40119             e.stopEvent();
40120         }
40121         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
40122     },
40123
40124     // private
40125     activate : function(autoExpand){
40126         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
40127             this.focus();
40128             if(autoExpand){
40129                 this.expandMenu();
40130             }
40131         }
40132         return true;
40133     },
40134
40135     // private
40136     shouldDeactivate : function(e){
40137         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
40138             if(this.menu && this.menu.isVisible()){
40139                 return !this.menu.getEl().getRegion().contains(e.getPoint());
40140             }
40141             return true;
40142         }
40143         return false;
40144     },
40145
40146     // private
40147     deactivate : function(){
40148         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
40149         this.hideMenu();
40150     },
40151
40152     // private
40153     expandMenu : function(autoActivate){
40154         if(!this.disabled && this.menu){
40155             clearTimeout(this.hideTimer);
40156             delete this.hideTimer;
40157             if(!this.menu.isVisible() && !this.showTimer){
40158                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
40159             }else if (this.menu.isVisible() && autoActivate){
40160                 this.menu.tryActivate(0, 1);
40161             }
40162         }
40163     },
40164
40165     // private
40166     deferExpand : function(autoActivate){
40167         delete this.showTimer;
40168         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
40169         if(autoActivate){
40170             this.menu.tryActivate(0, 1);
40171         }
40172     },
40173
40174     // private
40175     hideMenu : function(){
40176         clearTimeout(this.showTimer);
40177         delete this.showTimer;
40178         if(!this.hideTimer && this.menu && this.menu.isVisible()){
40179             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
40180         }
40181     },
40182
40183     // private
40184     deferHide : function(){
40185         delete this.hideTimer;
40186         this.menu.hide();
40187     }
40188 });/*
40189  * Based on:
40190  * Ext JS Library 1.1.1
40191  * Copyright(c) 2006-2007, Ext JS, LLC.
40192  *
40193  * Originally Released Under LGPL - original licence link has changed is not relivant.
40194  *
40195  * Fork - LGPL
40196  * <script type="text/javascript">
40197  */
40198  
40199 /**
40200  * @class Roo.menu.CheckItem
40201  * @extends Roo.menu.Item
40202  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
40203  * @constructor
40204  * Creates a new CheckItem
40205  * @param {Object} config Configuration options
40206  */
40207 Roo.menu.CheckItem = function(config){
40208     Roo.menu.CheckItem.superclass.constructor.call(this, config);
40209     this.addEvents({
40210         /**
40211          * @event beforecheckchange
40212          * Fires before the checked value is set, providing an opportunity to cancel if needed
40213          * @param {Roo.menu.CheckItem} this
40214          * @param {Boolean} checked The new checked value that will be set
40215          */
40216         "beforecheckchange" : true,
40217         /**
40218          * @event checkchange
40219          * Fires after the checked value has been set
40220          * @param {Roo.menu.CheckItem} this
40221          * @param {Boolean} checked The checked value that was set
40222          */
40223         "checkchange" : true
40224     });
40225     if(this.checkHandler){
40226         this.on('checkchange', this.checkHandler, this.scope);
40227     }
40228 };
40229 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40230     /**
40231      * @cfg {String} group
40232      * All check items with the same group name will automatically be grouped into a single-select
40233      * radio button group (defaults to '')
40234      */
40235     /**
40236      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40237      */
40238     itemCls : "x-menu-item x-menu-check-item",
40239     /**
40240      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40241      */
40242     groupClass : "x-menu-group-item",
40243
40244     /**
40245      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
40246      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40247      * initialized with checked = true will be rendered as checked.
40248      */
40249     checked: false,
40250
40251     // private
40252     ctype: "Roo.menu.CheckItem",
40253
40254     // private
40255     onRender : function(c){
40256         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40257         if(this.group){
40258             this.el.addClass(this.groupClass);
40259         }
40260         Roo.menu.MenuMgr.registerCheckable(this);
40261         if(this.checked){
40262             this.checked = false;
40263             this.setChecked(true, true);
40264         }
40265     },
40266
40267     // private
40268     destroy : function(){
40269         if(this.rendered){
40270             Roo.menu.MenuMgr.unregisterCheckable(this);
40271         }
40272         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40273     },
40274
40275     /**
40276      * Set the checked state of this item
40277      * @param {Boolean} checked The new checked value
40278      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40279      */
40280     setChecked : function(state, suppressEvent){
40281         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40282             if(this.container){
40283                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40284             }
40285             this.checked = state;
40286             if(suppressEvent !== true){
40287                 this.fireEvent("checkchange", this, state);
40288             }
40289         }
40290     },
40291
40292     // private
40293     handleClick : function(e){
40294        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40295            this.setChecked(!this.checked);
40296        }
40297        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40298     }
40299 });/*
40300  * Based on:
40301  * Ext JS Library 1.1.1
40302  * Copyright(c) 2006-2007, Ext JS, LLC.
40303  *
40304  * Originally Released Under LGPL - original licence link has changed is not relivant.
40305  *
40306  * Fork - LGPL
40307  * <script type="text/javascript">
40308  */
40309  
40310 /**
40311  * @class Roo.menu.DateItem
40312  * @extends Roo.menu.Adapter
40313  * A menu item that wraps the {@link Roo.DatPicker} component.
40314  * @constructor
40315  * Creates a new DateItem
40316  * @param {Object} config Configuration options
40317  */
40318 Roo.menu.DateItem = function(config){
40319     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40320     /** The Roo.DatePicker object @type Roo.DatePicker */
40321     this.picker = this.component;
40322     this.addEvents({select: true});
40323     
40324     this.picker.on("render", function(picker){
40325         picker.getEl().swallowEvent("click");
40326         picker.container.addClass("x-menu-date-item");
40327     });
40328
40329     this.picker.on("select", this.onSelect, this);
40330 };
40331
40332 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40333     // private
40334     onSelect : function(picker, date){
40335         this.fireEvent("select", this, date, picker);
40336         Roo.menu.DateItem.superclass.handleClick.call(this);
40337     }
40338 });/*
40339  * Based on:
40340  * Ext JS Library 1.1.1
40341  * Copyright(c) 2006-2007, Ext JS, LLC.
40342  *
40343  * Originally Released Under LGPL - original licence link has changed is not relivant.
40344  *
40345  * Fork - LGPL
40346  * <script type="text/javascript">
40347  */
40348  
40349 /**
40350  * @class Roo.menu.ColorItem
40351  * @extends Roo.menu.Adapter
40352  * A menu item that wraps the {@link Roo.ColorPalette} component.
40353  * @constructor
40354  * Creates a new ColorItem
40355  * @param {Object} config Configuration options
40356  */
40357 Roo.menu.ColorItem = function(config){
40358     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40359     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40360     this.palette = this.component;
40361     this.relayEvents(this.palette, ["select"]);
40362     if(this.selectHandler){
40363         this.on('select', this.selectHandler, this.scope);
40364     }
40365 };
40366 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40367  * Based on:
40368  * Ext JS Library 1.1.1
40369  * Copyright(c) 2006-2007, Ext JS, LLC.
40370  *
40371  * Originally Released Under LGPL - original licence link has changed is not relivant.
40372  *
40373  * Fork - LGPL
40374  * <script type="text/javascript">
40375  */
40376  
40377
40378 /**
40379  * @class Roo.menu.DateMenu
40380  * @extends Roo.menu.Menu
40381  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40382  * @constructor
40383  * Creates a new DateMenu
40384  * @param {Object} config Configuration options
40385  */
40386 Roo.menu.DateMenu = function(config){
40387     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40388     this.plain = true;
40389     var di = new Roo.menu.DateItem(config);
40390     this.add(di);
40391     /**
40392      * The {@link Roo.DatePicker} instance for this DateMenu
40393      * @type DatePicker
40394      */
40395     this.picker = di.picker;
40396     /**
40397      * @event select
40398      * @param {DatePicker} picker
40399      * @param {Date} date
40400      */
40401     this.relayEvents(di, ["select"]);
40402     this.on('beforeshow', function(){
40403         if(this.picker){
40404             this.picker.hideMonthPicker(false);
40405         }
40406     }, this);
40407 };
40408 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40409     cls:'x-date-menu'
40410 });/*
40411  * Based on:
40412  * Ext JS Library 1.1.1
40413  * Copyright(c) 2006-2007, Ext JS, LLC.
40414  *
40415  * Originally Released Under LGPL - original licence link has changed is not relivant.
40416  *
40417  * Fork - LGPL
40418  * <script type="text/javascript">
40419  */
40420  
40421
40422 /**
40423  * @class Roo.menu.ColorMenu
40424  * @extends Roo.menu.Menu
40425  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40426  * @constructor
40427  * Creates a new ColorMenu
40428  * @param {Object} config Configuration options
40429  */
40430 Roo.menu.ColorMenu = function(config){
40431     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40432     this.plain = true;
40433     var ci = new Roo.menu.ColorItem(config);
40434     this.add(ci);
40435     /**
40436      * The {@link Roo.ColorPalette} instance for this ColorMenu
40437      * @type ColorPalette
40438      */
40439     this.palette = ci.palette;
40440     /**
40441      * @event select
40442      * @param {ColorPalette} palette
40443      * @param {String} color
40444      */
40445     this.relayEvents(ci, ["select"]);
40446 };
40447 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40448  * Based on:
40449  * Ext JS Library 1.1.1
40450  * Copyright(c) 2006-2007, Ext JS, LLC.
40451  *
40452  * Originally Released Under LGPL - original licence link has changed is not relivant.
40453  *
40454  * Fork - LGPL
40455  * <script type="text/javascript">
40456  */
40457  
40458 /**
40459  * @class Roo.form.TextItem
40460  * @extends Roo.BoxComponent
40461  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40462  * @constructor
40463  * Creates a new TextItem
40464  * @param {Object} config Configuration options
40465  */
40466 Roo.form.TextItem = function(config){
40467     Roo.form.TextItem.superclass.constructor.call(this, config);
40468 };
40469
40470 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40471     
40472     /**
40473      * @cfg {String} tag the tag for this item (default div)
40474      */
40475     tag : 'div',
40476     /**
40477      * @cfg {String} html the content for this item
40478      */
40479     html : '',
40480     
40481     getAutoCreate : function()
40482     {
40483         var cfg = {
40484             id: this.id,
40485             tag: this.tag,
40486             html: this.html,
40487             cls: 'x-form-item'
40488         };
40489         
40490         return cfg;
40491         
40492     },
40493     
40494     onRender : function(ct, position)
40495     {
40496         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40497         
40498         if(!this.el){
40499             var cfg = this.getAutoCreate();
40500             if(!cfg.name){
40501                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40502             }
40503             if (!cfg.name.length) {
40504                 delete cfg.name;
40505             }
40506             this.el = ct.createChild(cfg, position);
40507         }
40508     },
40509     /*
40510      * setHTML
40511      * @param {String} html update the Contents of the element.
40512      */
40513     setHTML : function(html)
40514     {
40515         this.fieldEl.dom.innerHTML = html;
40516     }
40517     
40518 });/*
40519  * Based on:
40520  * Ext JS Library 1.1.1
40521  * Copyright(c) 2006-2007, Ext JS, LLC.
40522  *
40523  * Originally Released Under LGPL - original licence link has changed is not relivant.
40524  *
40525  * Fork - LGPL
40526  * <script type="text/javascript">
40527  */
40528  
40529 /**
40530  * @class Roo.form.Field
40531  * @extends Roo.BoxComponent
40532  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40533  * @constructor
40534  * Creates a new Field
40535  * @param {Object} config Configuration options
40536  */
40537 Roo.form.Field = function(config){
40538     Roo.form.Field.superclass.constructor.call(this, config);
40539 };
40540
40541 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40542     /**
40543      * @cfg {String} fieldLabel Label to use when rendering a form.
40544      */
40545        /**
40546      * @cfg {String} qtip Mouse over tip
40547      */
40548      
40549     /**
40550      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40551      */
40552     invalidClass : "x-form-invalid",
40553     /**
40554      * @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")
40555      */
40556     invalidText : "The value in this field is invalid",
40557     /**
40558      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40559      */
40560     focusClass : "x-form-focus",
40561     /**
40562      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40563       automatic validation (defaults to "keyup").
40564      */
40565     validationEvent : "keyup",
40566     /**
40567      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40568      */
40569     validateOnBlur : true,
40570     /**
40571      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40572      */
40573     validationDelay : 250,
40574     /**
40575      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40576      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40577      */
40578     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40579     /**
40580      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40581      */
40582     fieldClass : "x-form-field",
40583     /**
40584      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40585      *<pre>
40586 Value         Description
40587 -----------   ----------------------------------------------------------------------
40588 qtip          Display a quick tip when the user hovers over the field
40589 title         Display a default browser title attribute popup
40590 under         Add a block div beneath the field containing the error text
40591 side          Add an error icon to the right of the field with a popup on hover
40592 [element id]  Add the error text directly to the innerHTML of the specified element
40593 </pre>
40594      */
40595     msgTarget : 'qtip',
40596     /**
40597      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40598      */
40599     msgFx : 'normal',
40600
40601     /**
40602      * @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.
40603      */
40604     readOnly : false,
40605
40606     /**
40607      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40608      */
40609     disabled : false,
40610
40611     /**
40612      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40613      */
40614     inputType : undefined,
40615     
40616     /**
40617      * @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).
40618          */
40619         tabIndex : undefined,
40620         
40621     // private
40622     isFormField : true,
40623
40624     // private
40625     hasFocus : false,
40626     /**
40627      * @property {Roo.Element} fieldEl
40628      * Element Containing the rendered Field (with label etc.)
40629      */
40630     /**
40631      * @cfg {Mixed} value A value to initialize this field with.
40632      */
40633     value : undefined,
40634
40635     /**
40636      * @cfg {String} name The field's HTML name attribute.
40637      */
40638     /**
40639      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40640      */
40641     // private
40642     loadedValue : false,
40643      
40644      
40645         // private ??
40646         initComponent : function(){
40647         Roo.form.Field.superclass.initComponent.call(this);
40648         this.addEvents({
40649             /**
40650              * @event focus
40651              * Fires when this field receives input focus.
40652              * @param {Roo.form.Field} this
40653              */
40654             focus : true,
40655             /**
40656              * @event blur
40657              * Fires when this field loses input focus.
40658              * @param {Roo.form.Field} this
40659              */
40660             blur : true,
40661             /**
40662              * @event specialkey
40663              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40664              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40665              * @param {Roo.form.Field} this
40666              * @param {Roo.EventObject} e The event object
40667              */
40668             specialkey : true,
40669             /**
40670              * @event change
40671              * Fires just before the field blurs if the field value has changed.
40672              * @param {Roo.form.Field} this
40673              * @param {Mixed} newValue The new value
40674              * @param {Mixed} oldValue The original value
40675              */
40676             change : true,
40677             /**
40678              * @event invalid
40679              * Fires after the field has been marked as invalid.
40680              * @param {Roo.form.Field} this
40681              * @param {String} msg The validation message
40682              */
40683             invalid : true,
40684             /**
40685              * @event valid
40686              * Fires after the field has been validated with no errors.
40687              * @param {Roo.form.Field} this
40688              */
40689             valid : true,
40690              /**
40691              * @event keyup
40692              * Fires after the key up
40693              * @param {Roo.form.Field} this
40694              * @param {Roo.EventObject}  e The event Object
40695              */
40696             keyup : true
40697         });
40698     },
40699
40700     /**
40701      * Returns the name attribute of the field if available
40702      * @return {String} name The field name
40703      */
40704     getName: function(){
40705          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40706     },
40707
40708     // private
40709     onRender : function(ct, position){
40710         Roo.form.Field.superclass.onRender.call(this, ct, position);
40711         if(!this.el){
40712             var cfg = this.getAutoCreate();
40713             if(!cfg.name){
40714                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40715             }
40716             if (!cfg.name.length) {
40717                 delete cfg.name;
40718             }
40719             if(this.inputType){
40720                 cfg.type = this.inputType;
40721             }
40722             this.el = ct.createChild(cfg, position);
40723         }
40724         var type = this.el.dom.type;
40725         if(type){
40726             if(type == 'password'){
40727                 type = 'text';
40728             }
40729             this.el.addClass('x-form-'+type);
40730         }
40731         if(this.readOnly){
40732             this.el.dom.readOnly = true;
40733         }
40734         if(this.tabIndex !== undefined){
40735             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40736         }
40737
40738         this.el.addClass([this.fieldClass, this.cls]);
40739         this.initValue();
40740     },
40741
40742     /**
40743      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40744      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40745      * @return {Roo.form.Field} this
40746      */
40747     applyTo : function(target){
40748         this.allowDomMove = false;
40749         this.el = Roo.get(target);
40750         this.render(this.el.dom.parentNode);
40751         return this;
40752     },
40753
40754     // private
40755     initValue : function(){
40756         if(this.value !== undefined){
40757             this.setValue(this.value);
40758         }else if(this.el.dom.value.length > 0){
40759             this.setValue(this.el.dom.value);
40760         }
40761     },
40762
40763     /**
40764      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40765      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40766      */
40767     isDirty : function() {
40768         if(this.disabled) {
40769             return false;
40770         }
40771         return String(this.getValue()) !== String(this.originalValue);
40772     },
40773
40774     /**
40775      * stores the current value in loadedValue
40776      */
40777     resetHasChanged : function()
40778     {
40779         this.loadedValue = String(this.getValue());
40780     },
40781     /**
40782      * checks the current value against the 'loaded' value.
40783      * Note - will return false if 'resetHasChanged' has not been called first.
40784      */
40785     hasChanged : function()
40786     {
40787         if(this.disabled || this.readOnly) {
40788             return false;
40789         }
40790         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40791     },
40792     
40793     
40794     
40795     // private
40796     afterRender : function(){
40797         Roo.form.Field.superclass.afterRender.call(this);
40798         this.initEvents();
40799     },
40800
40801     // private
40802     fireKey : function(e){
40803         //Roo.log('field ' + e.getKey());
40804         if(e.isNavKeyPress()){
40805             this.fireEvent("specialkey", this, e);
40806         }
40807     },
40808
40809     /**
40810      * Resets the current field value to the originally loaded value and clears any validation messages
40811      */
40812     reset : function(){
40813         this.setValue(this.resetValue);
40814         this.originalValue = this.getValue();
40815         this.clearInvalid();
40816     },
40817
40818     // private
40819     initEvents : function(){
40820         // safari killled keypress - so keydown is now used..
40821         this.el.on("keydown" , this.fireKey,  this);
40822         this.el.on("focus", this.onFocus,  this);
40823         this.el.on("blur", this.onBlur,  this);
40824         this.el.relayEvent('keyup', this);
40825
40826         // reference to original value for reset
40827         this.originalValue = this.getValue();
40828         this.resetValue =  this.getValue();
40829     },
40830
40831     // private
40832     onFocus : function(){
40833         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40834             this.el.addClass(this.focusClass);
40835         }
40836         if(!this.hasFocus){
40837             this.hasFocus = true;
40838             this.startValue = this.getValue();
40839             this.fireEvent("focus", this);
40840         }
40841     },
40842
40843     beforeBlur : Roo.emptyFn,
40844
40845     // private
40846     onBlur : function(){
40847         this.beforeBlur();
40848         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40849             this.el.removeClass(this.focusClass);
40850         }
40851         this.hasFocus = false;
40852         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40853             this.validate();
40854         }
40855         var v = this.getValue();
40856         if(String(v) !== String(this.startValue)){
40857             this.fireEvent('change', this, v, this.startValue);
40858         }
40859         this.fireEvent("blur", this);
40860     },
40861
40862     /**
40863      * Returns whether or not the field value is currently valid
40864      * @param {Boolean} preventMark True to disable marking the field invalid
40865      * @return {Boolean} True if the value is valid, else false
40866      */
40867     isValid : function(preventMark){
40868         if(this.disabled){
40869             return true;
40870         }
40871         var restore = this.preventMark;
40872         this.preventMark = preventMark === true;
40873         var v = this.validateValue(this.processValue(this.getRawValue()));
40874         this.preventMark = restore;
40875         return v;
40876     },
40877
40878     /**
40879      * Validates the field value
40880      * @return {Boolean} True if the value is valid, else false
40881      */
40882     validate : function(){
40883         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40884             this.clearInvalid();
40885             return true;
40886         }
40887         return false;
40888     },
40889
40890     processValue : function(value){
40891         return value;
40892     },
40893
40894     // private
40895     // Subclasses should provide the validation implementation by overriding this
40896     validateValue : function(value){
40897         return true;
40898     },
40899
40900     /**
40901      * Mark this field as invalid
40902      * @param {String} msg The validation message
40903      */
40904     markInvalid : function(msg){
40905         if(!this.rendered || this.preventMark){ // not rendered
40906             return;
40907         }
40908         
40909         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40910         
40911         obj.el.addClass(this.invalidClass);
40912         msg = msg || this.invalidText;
40913         switch(this.msgTarget){
40914             case 'qtip':
40915                 obj.el.dom.qtip = msg;
40916                 obj.el.dom.qclass = 'x-form-invalid-tip';
40917                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40918                     Roo.QuickTips.enable();
40919                 }
40920                 break;
40921             case 'title':
40922                 this.el.dom.title = msg;
40923                 break;
40924             case 'under':
40925                 if(!this.errorEl){
40926                     var elp = this.el.findParent('.x-form-element', 5, true);
40927                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40928                     this.errorEl.setWidth(elp.getWidth(true)-20);
40929                 }
40930                 this.errorEl.update(msg);
40931                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40932                 break;
40933             case 'side':
40934                 if(!this.errorIcon){
40935                     var elp = this.el.findParent('.x-form-element', 5, true);
40936                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40937                 }
40938                 this.alignErrorIcon();
40939                 this.errorIcon.dom.qtip = msg;
40940                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40941                 this.errorIcon.show();
40942                 this.on('resize', this.alignErrorIcon, this);
40943                 break;
40944             default:
40945                 var t = Roo.getDom(this.msgTarget);
40946                 t.innerHTML = msg;
40947                 t.style.display = this.msgDisplay;
40948                 break;
40949         }
40950         this.fireEvent('invalid', this, msg);
40951     },
40952
40953     // private
40954     alignErrorIcon : function(){
40955         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40956     },
40957
40958     /**
40959      * Clear any invalid styles/messages for this field
40960      */
40961     clearInvalid : function(){
40962         if(!this.rendered || this.preventMark){ // not rendered
40963             return;
40964         }
40965         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40966         
40967         obj.el.removeClass(this.invalidClass);
40968         switch(this.msgTarget){
40969             case 'qtip':
40970                 obj.el.dom.qtip = '';
40971                 break;
40972             case 'title':
40973                 this.el.dom.title = '';
40974                 break;
40975             case 'under':
40976                 if(this.errorEl){
40977                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40978                 }
40979                 break;
40980             case 'side':
40981                 if(this.errorIcon){
40982                     this.errorIcon.dom.qtip = '';
40983                     this.errorIcon.hide();
40984                     this.un('resize', this.alignErrorIcon, this);
40985                 }
40986                 break;
40987             default:
40988                 var t = Roo.getDom(this.msgTarget);
40989                 t.innerHTML = '';
40990                 t.style.display = 'none';
40991                 break;
40992         }
40993         this.fireEvent('valid', this);
40994     },
40995
40996     /**
40997      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40998      * @return {Mixed} value The field value
40999      */
41000     getRawValue : function(){
41001         var v = this.el.getValue();
41002         
41003         return v;
41004     },
41005
41006     /**
41007      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41008      * @return {Mixed} value The field value
41009      */
41010     getValue : function(){
41011         var v = this.el.getValue();
41012          
41013         return v;
41014     },
41015
41016     /**
41017      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
41018      * @param {Mixed} value The value to set
41019      */
41020     setRawValue : function(v){
41021         return this.el.dom.value = (v === null || v === undefined ? '' : v);
41022     },
41023
41024     /**
41025      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41026      * @param {Mixed} value The value to set
41027      */
41028     setValue : function(v){
41029         this.value = v;
41030         if(this.rendered){
41031             this.el.dom.value = (v === null || v === undefined ? '' : v);
41032              this.validate();
41033         }
41034     },
41035
41036     adjustSize : function(w, h){
41037         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
41038         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
41039         return s;
41040     },
41041
41042     adjustWidth : function(tag, w){
41043         tag = tag.toLowerCase();
41044         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
41045             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
41046                 if(tag == 'input'){
41047                     return w + 2;
41048                 }
41049                 if(tag == 'textarea'){
41050                     return w-2;
41051                 }
41052             }else if(Roo.isOpera){
41053                 if(tag == 'input'){
41054                     return w + 2;
41055                 }
41056                 if(tag == 'textarea'){
41057                     return w-2;
41058                 }
41059             }
41060         }
41061         return w;
41062     }
41063 });
41064
41065
41066 // anything other than normal should be considered experimental
41067 Roo.form.Field.msgFx = {
41068     normal : {
41069         show: function(msgEl, f){
41070             msgEl.setDisplayed('block');
41071         },
41072
41073         hide : function(msgEl, f){
41074             msgEl.setDisplayed(false).update('');
41075         }
41076     },
41077
41078     slide : {
41079         show: function(msgEl, f){
41080             msgEl.slideIn('t', {stopFx:true});
41081         },
41082
41083         hide : function(msgEl, f){
41084             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
41085         }
41086     },
41087
41088     slideRight : {
41089         show: function(msgEl, f){
41090             msgEl.fixDisplay();
41091             msgEl.alignTo(f.el, 'tl-tr');
41092             msgEl.slideIn('l', {stopFx:true});
41093         },
41094
41095         hide : function(msgEl, f){
41096             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
41097         }
41098     }
41099 };/*
41100  * Based on:
41101  * Ext JS Library 1.1.1
41102  * Copyright(c) 2006-2007, Ext JS, LLC.
41103  *
41104  * Originally Released Under LGPL - original licence link has changed is not relivant.
41105  *
41106  * Fork - LGPL
41107  * <script type="text/javascript">
41108  */
41109  
41110
41111 /**
41112  * @class Roo.form.TextField
41113  * @extends Roo.form.Field
41114  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
41115  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
41116  * @constructor
41117  * Creates a new TextField
41118  * @param {Object} config Configuration options
41119  */
41120 Roo.form.TextField = function(config){
41121     Roo.form.TextField.superclass.constructor.call(this, config);
41122     this.addEvents({
41123         /**
41124          * @event autosize
41125          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
41126          * according to the default logic, but this event provides a hook for the developer to apply additional
41127          * logic at runtime to resize the field if needed.
41128              * @param {Roo.form.Field} this This text field
41129              * @param {Number} width The new field width
41130              */
41131         autosize : true
41132     });
41133 };
41134
41135 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
41136     /**
41137      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
41138      */
41139     grow : false,
41140     /**
41141      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
41142      */
41143     growMin : 30,
41144     /**
41145      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
41146      */
41147     growMax : 800,
41148     /**
41149      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
41150      */
41151     vtype : null,
41152     /**
41153      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
41154      */
41155     maskRe : null,
41156     /**
41157      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
41158      */
41159     disableKeyFilter : false,
41160     /**
41161      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
41162      */
41163     allowBlank : true,
41164     /**
41165      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
41166      */
41167     minLength : 0,
41168     /**
41169      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
41170      */
41171     maxLength : Number.MAX_VALUE,
41172     /**
41173      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
41174      */
41175     minLengthText : "The minimum length for this field is {0}",
41176     /**
41177      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
41178      */
41179     maxLengthText : "The maximum length for this field is {0}",
41180     /**
41181      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
41182      */
41183     selectOnFocus : false,
41184     /**
41185      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
41186      */    
41187     allowLeadingSpace : false,
41188     /**
41189      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
41190      */
41191     blankText : "This field is required",
41192     /**
41193      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
41194      * If available, this function will be called only after the basic validators all return true, and will be passed the
41195      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
41196      */
41197     validator : null,
41198     /**
41199      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
41200      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
41201      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
41202      */
41203     regex : null,
41204     /**
41205      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
41206      */
41207     regexText : "",
41208     /**
41209      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
41210      */
41211     emptyText : null,
41212    
41213
41214     // private
41215     initEvents : function()
41216     {
41217         if (this.emptyText) {
41218             this.el.attr('placeholder', this.emptyText);
41219         }
41220         
41221         Roo.form.TextField.superclass.initEvents.call(this);
41222         if(this.validationEvent == 'keyup'){
41223             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41224             this.el.on('keyup', this.filterValidation, this);
41225         }
41226         else if(this.validationEvent !== false){
41227             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41228         }
41229         
41230         if(this.selectOnFocus){
41231             this.on("focus", this.preFocus, this);
41232         }
41233         if (!this.allowLeadingSpace) {
41234             this.on('blur', this.cleanLeadingSpace, this);
41235         }
41236         
41237         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41238             this.el.on("keypress", this.filterKeys, this);
41239         }
41240         if(this.grow){
41241             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
41242             this.el.on("click", this.autoSize,  this);
41243         }
41244         if(this.el.is('input[type=password]') && Roo.isSafari){
41245             this.el.on('keydown', this.SafariOnKeyDown, this);
41246         }
41247     },
41248
41249     processValue : function(value){
41250         if(this.stripCharsRe){
41251             var newValue = value.replace(this.stripCharsRe, '');
41252             if(newValue !== value){
41253                 this.setRawValue(newValue);
41254                 return newValue;
41255             }
41256         }
41257         return value;
41258     },
41259
41260     filterValidation : function(e){
41261         if(!e.isNavKeyPress()){
41262             this.validationTask.delay(this.validationDelay);
41263         }
41264     },
41265
41266     // private
41267     onKeyUp : function(e){
41268         if(!e.isNavKeyPress()){
41269             this.autoSize();
41270         }
41271     },
41272     // private - clean the leading white space
41273     cleanLeadingSpace : function(e)
41274     {
41275         if ( this.inputType == 'file') {
41276             return;
41277         }
41278         
41279         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41280     },
41281     /**
41282      * Resets the current field value to the originally-loaded value and clears any validation messages.
41283      *  
41284      */
41285     reset : function(){
41286         Roo.form.TextField.superclass.reset.call(this);
41287        
41288     }, 
41289     // private
41290     preFocus : function(){
41291         
41292         if(this.selectOnFocus){
41293             this.el.dom.select();
41294         }
41295     },
41296
41297     
41298     // private
41299     filterKeys : function(e){
41300         var k = e.getKey();
41301         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41302             return;
41303         }
41304         var c = e.getCharCode(), cc = String.fromCharCode(c);
41305         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41306             return;
41307         }
41308         if(!this.maskRe.test(cc)){
41309             e.stopEvent();
41310         }
41311     },
41312
41313     setValue : function(v){
41314         
41315         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41316         
41317         this.autoSize();
41318     },
41319
41320     /**
41321      * Validates a value according to the field's validation rules and marks the field as invalid
41322      * if the validation fails
41323      * @param {Mixed} value The value to validate
41324      * @return {Boolean} True if the value is valid, else false
41325      */
41326     validateValue : function(value){
41327         if(value.length < 1)  { // if it's blank
41328              if(this.allowBlank){
41329                 this.clearInvalid();
41330                 return true;
41331              }else{
41332                 this.markInvalid(this.blankText);
41333                 return false;
41334              }
41335         }
41336         if(value.length < this.minLength){
41337             this.markInvalid(String.format(this.minLengthText, this.minLength));
41338             return false;
41339         }
41340         if(value.length > this.maxLength){
41341             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41342             return false;
41343         }
41344         if(this.vtype){
41345             var vt = Roo.form.VTypes;
41346             if(!vt[this.vtype](value, this)){
41347                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41348                 return false;
41349             }
41350         }
41351         if(typeof this.validator == "function"){
41352             var msg = this.validator(value);
41353             if(msg !== true){
41354                 this.markInvalid(msg);
41355                 return false;
41356             }
41357         }
41358         if(this.regex && !this.regex.test(value)){
41359             this.markInvalid(this.regexText);
41360             return false;
41361         }
41362         return true;
41363     },
41364
41365     /**
41366      * Selects text in this field
41367      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41368      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41369      */
41370     selectText : function(start, end){
41371         var v = this.getRawValue();
41372         if(v.length > 0){
41373             start = start === undefined ? 0 : start;
41374             end = end === undefined ? v.length : end;
41375             var d = this.el.dom;
41376             if(d.setSelectionRange){
41377                 d.setSelectionRange(start, end);
41378             }else if(d.createTextRange){
41379                 var range = d.createTextRange();
41380                 range.moveStart("character", start);
41381                 range.moveEnd("character", v.length-end);
41382                 range.select();
41383             }
41384         }
41385     },
41386
41387     /**
41388      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41389      * This only takes effect if grow = true, and fires the autosize event.
41390      */
41391     autoSize : function(){
41392         if(!this.grow || !this.rendered){
41393             return;
41394         }
41395         if(!this.metrics){
41396             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41397         }
41398         var el = this.el;
41399         var v = el.dom.value;
41400         var d = document.createElement('div');
41401         d.appendChild(document.createTextNode(v));
41402         v = d.innerHTML;
41403         d = null;
41404         v += "&#160;";
41405         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41406         this.el.setWidth(w);
41407         this.fireEvent("autosize", this, w);
41408     },
41409     
41410     // private
41411     SafariOnKeyDown : function(event)
41412     {
41413         // this is a workaround for a password hang bug on chrome/ webkit.
41414         
41415         var isSelectAll = false;
41416         
41417         if(this.el.dom.selectionEnd > 0){
41418             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41419         }
41420         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41421             event.preventDefault();
41422             this.setValue('');
41423             return;
41424         }
41425         
41426         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41427             
41428             event.preventDefault();
41429             // this is very hacky as keydown always get's upper case.
41430             
41431             var cc = String.fromCharCode(event.getCharCode());
41432             
41433             
41434             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41435             
41436         }
41437         
41438         
41439     }
41440 });/*
41441  * Based on:
41442  * Ext JS Library 1.1.1
41443  * Copyright(c) 2006-2007, Ext JS, LLC.
41444  *
41445  * Originally Released Under LGPL - original licence link has changed is not relivant.
41446  *
41447  * Fork - LGPL
41448  * <script type="text/javascript">
41449  */
41450  
41451 /**
41452  * @class Roo.form.Hidden
41453  * @extends Roo.form.TextField
41454  * Simple Hidden element used on forms 
41455  * 
41456  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41457  * 
41458  * @constructor
41459  * Creates a new Hidden form element.
41460  * @param {Object} config Configuration options
41461  */
41462
41463
41464
41465 // easy hidden field...
41466 Roo.form.Hidden = function(config){
41467     Roo.form.Hidden.superclass.constructor.call(this, config);
41468 };
41469   
41470 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41471     fieldLabel:      '',
41472     inputType:      'hidden',
41473     width:          50,
41474     allowBlank:     true,
41475     labelSeparator: '',
41476     hidden:         true,
41477     itemCls :       'x-form-item-display-none'
41478
41479
41480 });
41481
41482
41483 /*
41484  * Based on:
41485  * Ext JS Library 1.1.1
41486  * Copyright(c) 2006-2007, Ext JS, LLC.
41487  *
41488  * Originally Released Under LGPL - original licence link has changed is not relivant.
41489  *
41490  * Fork - LGPL
41491  * <script type="text/javascript">
41492  */
41493  
41494 /**
41495  * @class Roo.form.TriggerField
41496  * @extends Roo.form.TextField
41497  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41498  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41499  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41500  * for which you can provide a custom implementation.  For example:
41501  * <pre><code>
41502 var trigger = new Roo.form.TriggerField();
41503 trigger.onTriggerClick = myTriggerFn;
41504 trigger.applyTo('my-field');
41505 </code></pre>
41506  *
41507  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41508  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41509  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41510  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41511  * @constructor
41512  * Create a new TriggerField.
41513  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41514  * to the base TextField)
41515  */
41516 Roo.form.TriggerField = function(config){
41517     this.mimicing = false;
41518     Roo.form.TriggerField.superclass.constructor.call(this, config);
41519 };
41520
41521 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41522     /**
41523      * @cfg {String} triggerClass A CSS class to apply to the trigger
41524      */
41525     /**
41526      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41527      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41528      */
41529     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41530     /**
41531      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41532      */
41533     hideTrigger:false,
41534
41535     /** @cfg {Boolean} grow @hide */
41536     /** @cfg {Number} growMin @hide */
41537     /** @cfg {Number} growMax @hide */
41538
41539     /**
41540      * @hide 
41541      * @method
41542      */
41543     autoSize: Roo.emptyFn,
41544     // private
41545     monitorTab : true,
41546     // private
41547     deferHeight : true,
41548
41549     
41550     actionMode : 'wrap',
41551     // private
41552     onResize : function(w, h){
41553         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41554         if(typeof w == 'number'){
41555             var x = w - this.trigger.getWidth();
41556             this.el.setWidth(this.adjustWidth('input', x));
41557             this.trigger.setStyle('left', x+'px');
41558         }
41559     },
41560
41561     // private
41562     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41563
41564     // private
41565     getResizeEl : function(){
41566         return this.wrap;
41567     },
41568
41569     // private
41570     getPositionEl : function(){
41571         return this.wrap;
41572     },
41573
41574     // private
41575     alignErrorIcon : function(){
41576         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41577     },
41578
41579     // private
41580     onRender : function(ct, position){
41581         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41582         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41583         this.trigger = this.wrap.createChild(this.triggerConfig ||
41584                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41585         if(this.hideTrigger){
41586             this.trigger.setDisplayed(false);
41587         }
41588         this.initTrigger();
41589         if(!this.width){
41590             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41591         }
41592     },
41593
41594     // private
41595     initTrigger : function(){
41596         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41597         this.trigger.addClassOnOver('x-form-trigger-over');
41598         this.trigger.addClassOnClick('x-form-trigger-click');
41599     },
41600
41601     // private
41602     onDestroy : function(){
41603         if(this.trigger){
41604             this.trigger.removeAllListeners();
41605             this.trigger.remove();
41606         }
41607         if(this.wrap){
41608             this.wrap.remove();
41609         }
41610         Roo.form.TriggerField.superclass.onDestroy.call(this);
41611     },
41612
41613     // private
41614     onFocus : function(){
41615         Roo.form.TriggerField.superclass.onFocus.call(this);
41616         if(!this.mimicing){
41617             this.wrap.addClass('x-trigger-wrap-focus');
41618             this.mimicing = true;
41619             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41620             if(this.monitorTab){
41621                 this.el.on("keydown", this.checkTab, this);
41622             }
41623         }
41624     },
41625
41626     // private
41627     checkTab : function(e){
41628         if(e.getKey() == e.TAB){
41629             this.triggerBlur();
41630         }
41631     },
41632
41633     // private
41634     onBlur : function(){
41635         // do nothing
41636     },
41637
41638     // private
41639     mimicBlur : function(e, t){
41640         if(!this.wrap.contains(t) && this.validateBlur()){
41641             this.triggerBlur();
41642         }
41643     },
41644
41645     // private
41646     triggerBlur : function(){
41647         this.mimicing = false;
41648         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41649         if(this.monitorTab){
41650             this.el.un("keydown", this.checkTab, this);
41651         }
41652         this.wrap.removeClass('x-trigger-wrap-focus');
41653         Roo.form.TriggerField.superclass.onBlur.call(this);
41654     },
41655
41656     // private
41657     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41658     validateBlur : function(e, t){
41659         return true;
41660     },
41661
41662     // private
41663     onDisable : function(){
41664         Roo.form.TriggerField.superclass.onDisable.call(this);
41665         if(this.wrap){
41666             this.wrap.addClass('x-item-disabled');
41667         }
41668     },
41669
41670     // private
41671     onEnable : function(){
41672         Roo.form.TriggerField.superclass.onEnable.call(this);
41673         if(this.wrap){
41674             this.wrap.removeClass('x-item-disabled');
41675         }
41676     },
41677
41678     // private
41679     onShow : function(){
41680         var ae = this.getActionEl();
41681         
41682         if(ae){
41683             ae.dom.style.display = '';
41684             ae.dom.style.visibility = 'visible';
41685         }
41686     },
41687
41688     // private
41689     
41690     onHide : function(){
41691         var ae = this.getActionEl();
41692         ae.dom.style.display = 'none';
41693     },
41694
41695     /**
41696      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41697      * by an implementing function.
41698      * @method
41699      * @param {EventObject} e
41700      */
41701     onTriggerClick : Roo.emptyFn
41702 });
41703
41704 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41705 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41706 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41707 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41708     initComponent : function(){
41709         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41710
41711         this.triggerConfig = {
41712             tag:'span', cls:'x-form-twin-triggers', cn:[
41713             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41714             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41715         ]};
41716     },
41717
41718     getTrigger : function(index){
41719         return this.triggers[index];
41720     },
41721
41722     initTrigger : function(){
41723         var ts = this.trigger.select('.x-form-trigger', true);
41724         this.wrap.setStyle('overflow', 'hidden');
41725         var triggerField = this;
41726         ts.each(function(t, all, index){
41727             t.hide = function(){
41728                 var w = triggerField.wrap.getWidth();
41729                 this.dom.style.display = 'none';
41730                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41731             };
41732             t.show = function(){
41733                 var w = triggerField.wrap.getWidth();
41734                 this.dom.style.display = '';
41735                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41736             };
41737             var triggerIndex = 'Trigger'+(index+1);
41738
41739             if(this['hide'+triggerIndex]){
41740                 t.dom.style.display = 'none';
41741             }
41742             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41743             t.addClassOnOver('x-form-trigger-over');
41744             t.addClassOnClick('x-form-trigger-click');
41745         }, this);
41746         this.triggers = ts.elements;
41747     },
41748
41749     onTrigger1Click : Roo.emptyFn,
41750     onTrigger2Click : Roo.emptyFn
41751 });/*
41752  * Based on:
41753  * Ext JS Library 1.1.1
41754  * Copyright(c) 2006-2007, Ext JS, LLC.
41755  *
41756  * Originally Released Under LGPL - original licence link has changed is not relivant.
41757  *
41758  * Fork - LGPL
41759  * <script type="text/javascript">
41760  */
41761  
41762 /**
41763  * @class Roo.form.TextArea
41764  * @extends Roo.form.TextField
41765  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41766  * support for auto-sizing.
41767  * @constructor
41768  * Creates a new TextArea
41769  * @param {Object} config Configuration options
41770  */
41771 Roo.form.TextArea = function(config){
41772     Roo.form.TextArea.superclass.constructor.call(this, config);
41773     // these are provided exchanges for backwards compat
41774     // minHeight/maxHeight were replaced by growMin/growMax to be
41775     // compatible with TextField growing config values
41776     if(this.minHeight !== undefined){
41777         this.growMin = this.minHeight;
41778     }
41779     if(this.maxHeight !== undefined){
41780         this.growMax = this.maxHeight;
41781     }
41782 };
41783
41784 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41785     /**
41786      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41787      */
41788     growMin : 60,
41789     /**
41790      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41791      */
41792     growMax: 1000,
41793     /**
41794      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41795      * in the field (equivalent to setting overflow: hidden, defaults to false)
41796      */
41797     preventScrollbars: false,
41798     /**
41799      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41800      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41801      */
41802
41803     // private
41804     onRender : function(ct, position){
41805         if(!this.el){
41806             this.defaultAutoCreate = {
41807                 tag: "textarea",
41808                 style:"width:300px;height:60px;",
41809                 autocomplete: "new-password"
41810             };
41811         }
41812         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41813         if(this.grow){
41814             this.textSizeEl = Roo.DomHelper.append(document.body, {
41815                 tag: "pre", cls: "x-form-grow-sizer"
41816             });
41817             if(this.preventScrollbars){
41818                 this.el.setStyle("overflow", "hidden");
41819             }
41820             this.el.setHeight(this.growMin);
41821         }
41822     },
41823
41824     onDestroy : function(){
41825         if(this.textSizeEl){
41826             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41827         }
41828         Roo.form.TextArea.superclass.onDestroy.call(this);
41829     },
41830
41831     // private
41832     onKeyUp : function(e){
41833         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41834             this.autoSize();
41835         }
41836     },
41837
41838     /**
41839      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41840      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41841      */
41842     autoSize : function(){
41843         if(!this.grow || !this.textSizeEl){
41844             return;
41845         }
41846         var el = this.el;
41847         var v = el.dom.value;
41848         var ts = this.textSizeEl;
41849
41850         ts.innerHTML = '';
41851         ts.appendChild(document.createTextNode(v));
41852         v = ts.innerHTML;
41853
41854         Roo.fly(ts).setWidth(this.el.getWidth());
41855         if(v.length < 1){
41856             v = "&#160;&#160;";
41857         }else{
41858             if(Roo.isIE){
41859                 v = v.replace(/\n/g, '<p>&#160;</p>');
41860             }
41861             v += "&#160;\n&#160;";
41862         }
41863         ts.innerHTML = v;
41864         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41865         if(h != this.lastHeight){
41866             this.lastHeight = h;
41867             this.el.setHeight(h);
41868             this.fireEvent("autosize", this, h);
41869         }
41870     }
41871 });/*
41872  * Based on:
41873  * Ext JS Library 1.1.1
41874  * Copyright(c) 2006-2007, Ext JS, LLC.
41875  *
41876  * Originally Released Under LGPL - original licence link has changed is not relivant.
41877  *
41878  * Fork - LGPL
41879  * <script type="text/javascript">
41880  */
41881  
41882
41883 /**
41884  * @class Roo.form.NumberField
41885  * @extends Roo.form.TextField
41886  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41887  * @constructor
41888  * Creates a new NumberField
41889  * @param {Object} config Configuration options
41890  */
41891 Roo.form.NumberField = function(config){
41892     Roo.form.NumberField.superclass.constructor.call(this, config);
41893 };
41894
41895 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41896     /**
41897      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41898      */
41899     fieldClass: "x-form-field x-form-num-field",
41900     /**
41901      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41902      */
41903     allowDecimals : true,
41904     /**
41905      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41906      */
41907     decimalSeparator : ".",
41908     /**
41909      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41910      */
41911     decimalPrecision : 2,
41912     /**
41913      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41914      */
41915     allowNegative : true,
41916     /**
41917      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41918      */
41919     minValue : Number.NEGATIVE_INFINITY,
41920     /**
41921      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41922      */
41923     maxValue : Number.MAX_VALUE,
41924     /**
41925      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41926      */
41927     minText : "The minimum value for this field is {0}",
41928     /**
41929      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41930      */
41931     maxText : "The maximum value for this field is {0}",
41932     /**
41933      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41934      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41935      */
41936     nanText : "{0} is not a valid number",
41937
41938     // private
41939     initEvents : function(){
41940         Roo.form.NumberField.superclass.initEvents.call(this);
41941         var allowed = "0123456789";
41942         if(this.allowDecimals){
41943             allowed += this.decimalSeparator;
41944         }
41945         if(this.allowNegative){
41946             allowed += "-";
41947         }
41948         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41949         var keyPress = function(e){
41950             var k = e.getKey();
41951             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41952                 return;
41953             }
41954             var c = e.getCharCode();
41955             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41956                 e.stopEvent();
41957             }
41958         };
41959         this.el.on("keypress", keyPress, this);
41960     },
41961
41962     // private
41963     validateValue : function(value){
41964         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41965             return false;
41966         }
41967         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41968              return true;
41969         }
41970         var num = this.parseValue(value);
41971         if(isNaN(num)){
41972             this.markInvalid(String.format(this.nanText, value));
41973             return false;
41974         }
41975         if(num < this.minValue){
41976             this.markInvalid(String.format(this.minText, this.minValue));
41977             return false;
41978         }
41979         if(num > this.maxValue){
41980             this.markInvalid(String.format(this.maxText, this.maxValue));
41981             return false;
41982         }
41983         return true;
41984     },
41985
41986     getValue : function(){
41987         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41988     },
41989
41990     // private
41991     parseValue : function(value){
41992         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41993         return isNaN(value) ? '' : value;
41994     },
41995
41996     // private
41997     fixPrecision : function(value){
41998         var nan = isNaN(value);
41999         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42000             return nan ? '' : value;
42001         }
42002         return parseFloat(value).toFixed(this.decimalPrecision);
42003     },
42004
42005     setValue : function(v){
42006         v = this.fixPrecision(v);
42007         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
42008     },
42009
42010     // private
42011     decimalPrecisionFcn : function(v){
42012         return Math.floor(v);
42013     },
42014
42015     beforeBlur : function(){
42016         var v = this.parseValue(this.getRawValue());
42017         if(v){
42018             this.setValue(v);
42019         }
42020     }
42021 });/*
42022  * Based on:
42023  * Ext JS Library 1.1.1
42024  * Copyright(c) 2006-2007, Ext JS, LLC.
42025  *
42026  * Originally Released Under LGPL - original licence link has changed is not relivant.
42027  *
42028  * Fork - LGPL
42029  * <script type="text/javascript">
42030  */
42031  
42032 /**
42033  * @class Roo.form.DateField
42034  * @extends Roo.form.TriggerField
42035  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42036 * @constructor
42037 * Create a new DateField
42038 * @param {Object} config
42039  */
42040 Roo.form.DateField = function(config)
42041 {
42042     Roo.form.DateField.superclass.constructor.call(this, config);
42043     
42044       this.addEvents({
42045          
42046         /**
42047          * @event select
42048          * Fires when a date is selected
42049              * @param {Roo.form.DateField} combo This combo box
42050              * @param {Date} date The date selected
42051              */
42052         'select' : true
42053          
42054     });
42055     
42056     
42057     if(typeof this.minValue == "string") {
42058         this.minValue = this.parseDate(this.minValue);
42059     }
42060     if(typeof this.maxValue == "string") {
42061         this.maxValue = this.parseDate(this.maxValue);
42062     }
42063     this.ddMatch = null;
42064     if(this.disabledDates){
42065         var dd = this.disabledDates;
42066         var re = "(?:";
42067         for(var i = 0; i < dd.length; i++){
42068             re += dd[i];
42069             if(i != dd.length-1) {
42070                 re += "|";
42071             }
42072         }
42073         this.ddMatch = new RegExp(re + ")");
42074     }
42075 };
42076
42077 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
42078     /**
42079      * @cfg {String} format
42080      * The default date format string which can be overriden for localization support.  The format must be
42081      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42082      */
42083     format : "m/d/y",
42084     /**
42085      * @cfg {String} altFormats
42086      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42087      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42088      */
42089     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
42090     /**
42091      * @cfg {Array} disabledDays
42092      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42093      */
42094     disabledDays : null,
42095     /**
42096      * @cfg {String} disabledDaysText
42097      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42098      */
42099     disabledDaysText : "Disabled",
42100     /**
42101      * @cfg {Array} disabledDates
42102      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42103      * expression so they are very powerful. Some examples:
42104      * <ul>
42105      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42106      * <li>["03/08", "09/16"] would disable those days for every year</li>
42107      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42108      * <li>["03/../2006"] would disable every day in March 2006</li>
42109      * <li>["^03"] would disable every day in every March</li>
42110      * </ul>
42111      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42112      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42113      */
42114     disabledDates : null,
42115     /**
42116      * @cfg {String} disabledDatesText
42117      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42118      */
42119     disabledDatesText : "Disabled",
42120         
42121         
42122         /**
42123      * @cfg {Date/String} zeroValue
42124      * if the date is less that this number, then the field is rendered as empty
42125      * default is 1800
42126      */
42127         zeroValue : '1800-01-01',
42128         
42129         
42130     /**
42131      * @cfg {Date/String} minValue
42132      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42133      * valid format (defaults to null).
42134      */
42135     minValue : null,
42136     /**
42137      * @cfg {Date/String} maxValue
42138      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42139      * valid format (defaults to null).
42140      */
42141     maxValue : null,
42142     /**
42143      * @cfg {String} minText
42144      * The error text to display when the date in the cell is before minValue (defaults to
42145      * 'The date in this field must be after {minValue}').
42146      */
42147     minText : "The date in this field must be equal to or after {0}",
42148     /**
42149      * @cfg {String} maxText
42150      * The error text to display when the date in the cell is after maxValue (defaults to
42151      * 'The date in this field must be before {maxValue}').
42152      */
42153     maxText : "The date in this field must be equal to or before {0}",
42154     /**
42155      * @cfg {String} invalidText
42156      * The error text to display when the date in the field is invalid (defaults to
42157      * '{value} is not a valid date - it must be in the format {format}').
42158      */
42159     invalidText : "{0} is not a valid date - it must be in the format {1}",
42160     /**
42161      * @cfg {String} triggerClass
42162      * An additional CSS class used to style the trigger button.  The trigger will always get the
42163      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42164      * which displays a calendar icon).
42165      */
42166     triggerClass : 'x-form-date-trigger',
42167     
42168
42169     /**
42170      * @cfg {Boolean} useIso
42171      * if enabled, then the date field will use a hidden field to store the 
42172      * real value as iso formated date. default (false)
42173      */ 
42174     useIso : false,
42175     /**
42176      * @cfg {String/Object} autoCreate
42177      * A DomHelper element spec, or true for a default element spec (defaults to
42178      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42179      */ 
42180     // private
42181     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
42182     
42183     // private
42184     hiddenField: false,
42185     
42186     onRender : function(ct, position)
42187     {
42188         Roo.form.DateField.superclass.onRender.call(this, ct, position);
42189         if (this.useIso) {
42190             //this.el.dom.removeAttribute('name'); 
42191             Roo.log("Changing name?");
42192             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
42193             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42194                     'before', true);
42195             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42196             // prevent input submission
42197             this.hiddenName = this.name;
42198         }
42199             
42200             
42201     },
42202     
42203     // private
42204     validateValue : function(value)
42205     {
42206         value = this.formatDate(value);
42207         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
42208             Roo.log('super failed');
42209             return false;
42210         }
42211         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42212              return true;
42213         }
42214         var svalue = value;
42215         value = this.parseDate(value);
42216         if(!value){
42217             Roo.log('parse date failed' + svalue);
42218             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42219             return false;
42220         }
42221         var time = value.getTime();
42222         if(this.minValue && time < this.minValue.getTime()){
42223             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42224             return false;
42225         }
42226         if(this.maxValue && time > this.maxValue.getTime()){
42227             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42228             return false;
42229         }
42230         if(this.disabledDays){
42231             var day = value.getDay();
42232             for(var i = 0; i < this.disabledDays.length; i++) {
42233                 if(day === this.disabledDays[i]){
42234                     this.markInvalid(this.disabledDaysText);
42235                     return false;
42236                 }
42237             }
42238         }
42239         var fvalue = this.formatDate(value);
42240         if(this.ddMatch && this.ddMatch.test(fvalue)){
42241             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42242             return false;
42243         }
42244         return true;
42245     },
42246
42247     // private
42248     // Provides logic to override the default TriggerField.validateBlur which just returns true
42249     validateBlur : function(){
42250         return !this.menu || !this.menu.isVisible();
42251     },
42252     
42253     getName: function()
42254     {
42255         // returns hidden if it's set..
42256         if (!this.rendered) {return ''};
42257         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42258         
42259     },
42260
42261     /**
42262      * Returns the current date value of the date field.
42263      * @return {Date} The date value
42264      */
42265     getValue : function(){
42266         
42267         return  this.hiddenField ?
42268                 this.hiddenField.value :
42269                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42270     },
42271
42272     /**
42273      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42274      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42275      * (the default format used is "m/d/y").
42276      * <br />Usage:
42277      * <pre><code>
42278 //All of these calls set the same date value (May 4, 2006)
42279
42280 //Pass a date object:
42281 var dt = new Date('5/4/06');
42282 dateField.setValue(dt);
42283
42284 //Pass a date string (default format):
42285 dateField.setValue('5/4/06');
42286
42287 //Pass a date string (custom format):
42288 dateField.format = 'Y-m-d';
42289 dateField.setValue('2006-5-4');
42290 </code></pre>
42291      * @param {String/Date} date The date or valid date string
42292      */
42293     setValue : function(date){
42294         if (this.hiddenField) {
42295             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42296         }
42297         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42298         // make sure the value field is always stored as a date..
42299         this.value = this.parseDate(date);
42300         
42301         
42302     },
42303
42304     // private
42305     parseDate : function(value){
42306                 
42307                 if (value instanceof Date) {
42308                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42309                                 return  '';
42310                         }
42311                         return value;
42312                 }
42313                 
42314                 
42315         if(!value || value instanceof Date){
42316             return value;
42317         }
42318         var v = Date.parseDate(value, this.format);
42319          if (!v && this.useIso) {
42320             v = Date.parseDate(value, 'Y-m-d');
42321         }
42322         if(!v && this.altFormats){
42323             if(!this.altFormatsArray){
42324                 this.altFormatsArray = this.altFormats.split("|");
42325             }
42326             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42327                 v = Date.parseDate(value, this.altFormatsArray[i]);
42328             }
42329         }
42330                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42331                         v = '';
42332                 }
42333         return v;
42334     },
42335
42336     // private
42337     formatDate : function(date, fmt){
42338         return (!date || !(date instanceof Date)) ?
42339                date : date.dateFormat(fmt || this.format);
42340     },
42341
42342     // private
42343     menuListeners : {
42344         select: function(m, d){
42345             
42346             this.setValue(d);
42347             this.fireEvent('select', this, d);
42348         },
42349         show : function(){ // retain focus styling
42350             this.onFocus();
42351         },
42352         hide : function(){
42353             this.focus.defer(10, this);
42354             var ml = this.menuListeners;
42355             this.menu.un("select", ml.select,  this);
42356             this.menu.un("show", ml.show,  this);
42357             this.menu.un("hide", ml.hide,  this);
42358         }
42359     },
42360
42361     // private
42362     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42363     onTriggerClick : function(){
42364         if(this.disabled){
42365             return;
42366         }
42367         if(this.menu == null){
42368             this.menu = new Roo.menu.DateMenu();
42369         }
42370         Roo.apply(this.menu.picker,  {
42371             showClear: this.allowBlank,
42372             minDate : this.minValue,
42373             maxDate : this.maxValue,
42374             disabledDatesRE : this.ddMatch,
42375             disabledDatesText : this.disabledDatesText,
42376             disabledDays : this.disabledDays,
42377             disabledDaysText : this.disabledDaysText,
42378             format : this.useIso ? 'Y-m-d' : this.format,
42379             minText : String.format(this.minText, this.formatDate(this.minValue)),
42380             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42381         });
42382         this.menu.on(Roo.apply({}, this.menuListeners, {
42383             scope:this
42384         }));
42385         this.menu.picker.setValue(this.getValue() || new Date());
42386         this.menu.show(this.el, "tl-bl?");
42387     },
42388
42389     beforeBlur : function(){
42390         var v = this.parseDate(this.getRawValue());
42391         if(v){
42392             this.setValue(v);
42393         }
42394     },
42395
42396     /*@
42397      * overide
42398      * 
42399      */
42400     isDirty : function() {
42401         if(this.disabled) {
42402             return false;
42403         }
42404         
42405         if(typeof(this.startValue) === 'undefined'){
42406             return false;
42407         }
42408         
42409         return String(this.getValue()) !== String(this.startValue);
42410         
42411     },
42412     // @overide
42413     cleanLeadingSpace : function(e)
42414     {
42415        return;
42416     }
42417     
42418 });/*
42419  * Based on:
42420  * Ext JS Library 1.1.1
42421  * Copyright(c) 2006-2007, Ext JS, LLC.
42422  *
42423  * Originally Released Under LGPL - original licence link has changed is not relivant.
42424  *
42425  * Fork - LGPL
42426  * <script type="text/javascript">
42427  */
42428  
42429 /**
42430  * @class Roo.form.MonthField
42431  * @extends Roo.form.TriggerField
42432  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42433 * @constructor
42434 * Create a new MonthField
42435 * @param {Object} config
42436  */
42437 Roo.form.MonthField = function(config){
42438     
42439     Roo.form.MonthField.superclass.constructor.call(this, config);
42440     
42441       this.addEvents({
42442          
42443         /**
42444          * @event select
42445          * Fires when a date is selected
42446              * @param {Roo.form.MonthFieeld} combo This combo box
42447              * @param {Date} date The date selected
42448              */
42449         'select' : true
42450          
42451     });
42452     
42453     
42454     if(typeof this.minValue == "string") {
42455         this.minValue = this.parseDate(this.minValue);
42456     }
42457     if(typeof this.maxValue == "string") {
42458         this.maxValue = this.parseDate(this.maxValue);
42459     }
42460     this.ddMatch = null;
42461     if(this.disabledDates){
42462         var dd = this.disabledDates;
42463         var re = "(?:";
42464         for(var i = 0; i < dd.length; i++){
42465             re += dd[i];
42466             if(i != dd.length-1) {
42467                 re += "|";
42468             }
42469         }
42470         this.ddMatch = new RegExp(re + ")");
42471     }
42472 };
42473
42474 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42475     /**
42476      * @cfg {String} format
42477      * The default date format string which can be overriden for localization support.  The format must be
42478      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42479      */
42480     format : "M Y",
42481     /**
42482      * @cfg {String} altFormats
42483      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42484      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42485      */
42486     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42487     /**
42488      * @cfg {Array} disabledDays
42489      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42490      */
42491     disabledDays : [0,1,2,3,4,5,6],
42492     /**
42493      * @cfg {String} disabledDaysText
42494      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42495      */
42496     disabledDaysText : "Disabled",
42497     /**
42498      * @cfg {Array} disabledDates
42499      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42500      * expression so they are very powerful. Some examples:
42501      * <ul>
42502      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42503      * <li>["03/08", "09/16"] would disable those days for every year</li>
42504      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42505      * <li>["03/../2006"] would disable every day in March 2006</li>
42506      * <li>["^03"] would disable every day in every March</li>
42507      * </ul>
42508      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42509      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42510      */
42511     disabledDates : null,
42512     /**
42513      * @cfg {String} disabledDatesText
42514      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42515      */
42516     disabledDatesText : "Disabled",
42517     /**
42518      * @cfg {Date/String} minValue
42519      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42520      * valid format (defaults to null).
42521      */
42522     minValue : null,
42523     /**
42524      * @cfg {Date/String} maxValue
42525      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42526      * valid format (defaults to null).
42527      */
42528     maxValue : null,
42529     /**
42530      * @cfg {String} minText
42531      * The error text to display when the date in the cell is before minValue (defaults to
42532      * 'The date in this field must be after {minValue}').
42533      */
42534     minText : "The date in this field must be equal to or after {0}",
42535     /**
42536      * @cfg {String} maxTextf
42537      * The error text to display when the date in the cell is after maxValue (defaults to
42538      * 'The date in this field must be before {maxValue}').
42539      */
42540     maxText : "The date in this field must be equal to or before {0}",
42541     /**
42542      * @cfg {String} invalidText
42543      * The error text to display when the date in the field is invalid (defaults to
42544      * '{value} is not a valid date - it must be in the format {format}').
42545      */
42546     invalidText : "{0} is not a valid date - it must be in the format {1}",
42547     /**
42548      * @cfg {String} triggerClass
42549      * An additional CSS class used to style the trigger button.  The trigger will always get the
42550      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42551      * which displays a calendar icon).
42552      */
42553     triggerClass : 'x-form-date-trigger',
42554     
42555
42556     /**
42557      * @cfg {Boolean} useIso
42558      * if enabled, then the date field will use a hidden field to store the 
42559      * real value as iso formated date. default (true)
42560      */ 
42561     useIso : true,
42562     /**
42563      * @cfg {String/Object} autoCreate
42564      * A DomHelper element spec, or true for a default element spec (defaults to
42565      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42566      */ 
42567     // private
42568     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42569     
42570     // private
42571     hiddenField: false,
42572     
42573     hideMonthPicker : false,
42574     
42575     onRender : function(ct, position)
42576     {
42577         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42578         if (this.useIso) {
42579             this.el.dom.removeAttribute('name'); 
42580             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42581                     'before', true);
42582             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42583             // prevent input submission
42584             this.hiddenName = this.name;
42585         }
42586             
42587             
42588     },
42589     
42590     // private
42591     validateValue : function(value)
42592     {
42593         value = this.formatDate(value);
42594         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42595             return false;
42596         }
42597         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42598              return true;
42599         }
42600         var svalue = value;
42601         value = this.parseDate(value);
42602         if(!value){
42603             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42604             return false;
42605         }
42606         var time = value.getTime();
42607         if(this.minValue && time < this.minValue.getTime()){
42608             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42609             return false;
42610         }
42611         if(this.maxValue && time > this.maxValue.getTime()){
42612             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42613             return false;
42614         }
42615         /*if(this.disabledDays){
42616             var day = value.getDay();
42617             for(var i = 0; i < this.disabledDays.length; i++) {
42618                 if(day === this.disabledDays[i]){
42619                     this.markInvalid(this.disabledDaysText);
42620                     return false;
42621                 }
42622             }
42623         }
42624         */
42625         var fvalue = this.formatDate(value);
42626         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42627             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42628             return false;
42629         }
42630         */
42631         return true;
42632     },
42633
42634     // private
42635     // Provides logic to override the default TriggerField.validateBlur which just returns true
42636     validateBlur : function(){
42637         return !this.menu || !this.menu.isVisible();
42638     },
42639
42640     /**
42641      * Returns the current date value of the date field.
42642      * @return {Date} The date value
42643      */
42644     getValue : function(){
42645         
42646         
42647         
42648         return  this.hiddenField ?
42649                 this.hiddenField.value :
42650                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42651     },
42652
42653     /**
42654      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42655      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42656      * (the default format used is "m/d/y").
42657      * <br />Usage:
42658      * <pre><code>
42659 //All of these calls set the same date value (May 4, 2006)
42660
42661 //Pass a date object:
42662 var dt = new Date('5/4/06');
42663 monthField.setValue(dt);
42664
42665 //Pass a date string (default format):
42666 monthField.setValue('5/4/06');
42667
42668 //Pass a date string (custom format):
42669 monthField.format = 'Y-m-d';
42670 monthField.setValue('2006-5-4');
42671 </code></pre>
42672      * @param {String/Date} date The date or valid date string
42673      */
42674     setValue : function(date){
42675         Roo.log('month setValue' + date);
42676         // can only be first of month..
42677         
42678         var val = this.parseDate(date);
42679         
42680         if (this.hiddenField) {
42681             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42682         }
42683         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42684         this.value = this.parseDate(date);
42685     },
42686
42687     // private
42688     parseDate : function(value){
42689         if(!value || value instanceof Date){
42690             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42691             return value;
42692         }
42693         var v = Date.parseDate(value, this.format);
42694         if (!v && this.useIso) {
42695             v = Date.parseDate(value, 'Y-m-d');
42696         }
42697         if (v) {
42698             // 
42699             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42700         }
42701         
42702         
42703         if(!v && this.altFormats){
42704             if(!this.altFormatsArray){
42705                 this.altFormatsArray = this.altFormats.split("|");
42706             }
42707             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42708                 v = Date.parseDate(value, this.altFormatsArray[i]);
42709             }
42710         }
42711         return v;
42712     },
42713
42714     // private
42715     formatDate : function(date, fmt){
42716         return (!date || !(date instanceof Date)) ?
42717                date : date.dateFormat(fmt || this.format);
42718     },
42719
42720     // private
42721     menuListeners : {
42722         select: function(m, d){
42723             this.setValue(d);
42724             this.fireEvent('select', this, d);
42725         },
42726         show : function(){ // retain focus styling
42727             this.onFocus();
42728         },
42729         hide : function(){
42730             this.focus.defer(10, this);
42731             var ml = this.menuListeners;
42732             this.menu.un("select", ml.select,  this);
42733             this.menu.un("show", ml.show,  this);
42734             this.menu.un("hide", ml.hide,  this);
42735         }
42736     },
42737     // private
42738     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42739     onTriggerClick : function(){
42740         if(this.disabled){
42741             return;
42742         }
42743         if(this.menu == null){
42744             this.menu = new Roo.menu.DateMenu();
42745            
42746         }
42747         
42748         Roo.apply(this.menu.picker,  {
42749             
42750             showClear: this.allowBlank,
42751             minDate : this.minValue,
42752             maxDate : this.maxValue,
42753             disabledDatesRE : this.ddMatch,
42754             disabledDatesText : this.disabledDatesText,
42755             
42756             format : this.useIso ? 'Y-m-d' : this.format,
42757             minText : String.format(this.minText, this.formatDate(this.minValue)),
42758             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42759             
42760         });
42761          this.menu.on(Roo.apply({}, this.menuListeners, {
42762             scope:this
42763         }));
42764        
42765         
42766         var m = this.menu;
42767         var p = m.picker;
42768         
42769         // hide month picker get's called when we called by 'before hide';
42770         
42771         var ignorehide = true;
42772         p.hideMonthPicker  = function(disableAnim){
42773             if (ignorehide) {
42774                 return;
42775             }
42776              if(this.monthPicker){
42777                 Roo.log("hideMonthPicker called");
42778                 if(disableAnim === true){
42779                     this.monthPicker.hide();
42780                 }else{
42781                     this.monthPicker.slideOut('t', {duration:.2});
42782                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42783                     p.fireEvent("select", this, this.value);
42784                     m.hide();
42785                 }
42786             }
42787         }
42788         
42789         Roo.log('picker set value');
42790         Roo.log(this.getValue());
42791         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42792         m.show(this.el, 'tl-bl?');
42793         ignorehide  = false;
42794         // this will trigger hideMonthPicker..
42795         
42796         
42797         // hidden the day picker
42798         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42799         
42800         
42801         
42802       
42803         
42804         p.showMonthPicker.defer(100, p);
42805     
42806         
42807        
42808     },
42809
42810     beforeBlur : function(){
42811         var v = this.parseDate(this.getRawValue());
42812         if(v){
42813             this.setValue(v);
42814         }
42815     }
42816
42817     /** @cfg {Boolean} grow @hide */
42818     /** @cfg {Number} growMin @hide */
42819     /** @cfg {Number} growMax @hide */
42820     /**
42821      * @hide
42822      * @method autoSize
42823      */
42824 });/*
42825  * Based on:
42826  * Ext JS Library 1.1.1
42827  * Copyright(c) 2006-2007, Ext JS, LLC.
42828  *
42829  * Originally Released Under LGPL - original licence link has changed is not relivant.
42830  *
42831  * Fork - LGPL
42832  * <script type="text/javascript">
42833  */
42834  
42835
42836 /**
42837  * @class Roo.form.ComboBox
42838  * @extends Roo.form.TriggerField
42839  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42840  * @constructor
42841  * Create a new ComboBox.
42842  * @param {Object} config Configuration options
42843  */
42844 Roo.form.ComboBox = function(config){
42845     Roo.form.ComboBox.superclass.constructor.call(this, config);
42846     this.addEvents({
42847         /**
42848          * @event expand
42849          * Fires when the dropdown list is expanded
42850              * @param {Roo.form.ComboBox} combo This combo box
42851              */
42852         'expand' : true,
42853         /**
42854          * @event collapse
42855          * Fires when the dropdown list is collapsed
42856              * @param {Roo.form.ComboBox} combo This combo box
42857              */
42858         'collapse' : true,
42859         /**
42860          * @event beforeselect
42861          * Fires before a list item is selected. Return false to cancel the selection.
42862              * @param {Roo.form.ComboBox} combo This combo box
42863              * @param {Roo.data.Record} record The data record returned from the underlying store
42864              * @param {Number} index The index of the selected item in the dropdown list
42865              */
42866         'beforeselect' : true,
42867         /**
42868          * @event select
42869          * Fires when a list item is selected
42870              * @param {Roo.form.ComboBox} combo This combo box
42871              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42872              * @param {Number} index The index of the selected item in the dropdown list
42873              */
42874         'select' : true,
42875         /**
42876          * @event beforequery
42877          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42878          * The event object passed has these properties:
42879              * @param {Roo.form.ComboBox} combo This combo box
42880              * @param {String} query The query
42881              * @param {Boolean} forceAll true to force "all" query
42882              * @param {Boolean} cancel true to cancel the query
42883              * @param {Object} e The query event object
42884              */
42885         'beforequery': true,
42886          /**
42887          * @event add
42888          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42889              * @param {Roo.form.ComboBox} combo This combo box
42890              */
42891         'add' : true,
42892         /**
42893          * @event edit
42894          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42895              * @param {Roo.form.ComboBox} combo This combo box
42896              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42897              */
42898         'edit' : true
42899         
42900         
42901     });
42902     if(this.transform){
42903         this.allowDomMove = false;
42904         var s = Roo.getDom(this.transform);
42905         if(!this.hiddenName){
42906             this.hiddenName = s.name;
42907         }
42908         if(!this.store){
42909             this.mode = 'local';
42910             var d = [], opts = s.options;
42911             for(var i = 0, len = opts.length;i < len; i++){
42912                 var o = opts[i];
42913                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42914                 if(o.selected) {
42915                     this.value = value;
42916                 }
42917                 d.push([value, o.text]);
42918             }
42919             this.store = new Roo.data.SimpleStore({
42920                 'id': 0,
42921                 fields: ['value', 'text'],
42922                 data : d
42923             });
42924             this.valueField = 'value';
42925             this.displayField = 'text';
42926         }
42927         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42928         if(!this.lazyRender){
42929             this.target = true;
42930             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42931             s.parentNode.removeChild(s); // remove it
42932             this.render(this.el.parentNode);
42933         }else{
42934             s.parentNode.removeChild(s); // remove it
42935         }
42936
42937     }
42938     if (this.store) {
42939         this.store = Roo.factory(this.store, Roo.data);
42940     }
42941     
42942     this.selectedIndex = -1;
42943     if(this.mode == 'local'){
42944         if(config.queryDelay === undefined){
42945             this.queryDelay = 10;
42946         }
42947         if(config.minChars === undefined){
42948             this.minChars = 0;
42949         }
42950     }
42951 };
42952
42953 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42954     /**
42955      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42956      */
42957     /**
42958      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42959      * rendering into an Roo.Editor, defaults to false)
42960      */
42961     /**
42962      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42963      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42964      */
42965     /**
42966      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42967      */
42968     /**
42969      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42970      * the dropdown list (defaults to undefined, with no header element)
42971      */
42972
42973      /**
42974      * @cfg {String/Roo.Template} tpl The template to use to render the output
42975      */
42976      
42977     // private
42978     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42979     /**
42980      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42981      */
42982     listWidth: undefined,
42983     /**
42984      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42985      * mode = 'remote' or 'text' if mode = 'local')
42986      */
42987     displayField: undefined,
42988     /**
42989      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42990      * mode = 'remote' or 'value' if mode = 'local'). 
42991      * Note: use of a valueField requires the user make a selection
42992      * in order for a value to be mapped.
42993      */
42994     valueField: undefined,
42995     
42996     
42997     /**
42998      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42999      * field's data value (defaults to the underlying DOM element's name)
43000      */
43001     hiddenName: undefined,
43002     /**
43003      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
43004      */
43005     listClass: '',
43006     /**
43007      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
43008      */
43009     selectedClass: 'x-combo-selected',
43010     /**
43011      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
43012      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
43013      * which displays a downward arrow icon).
43014      */
43015     triggerClass : 'x-form-arrow-trigger',
43016     /**
43017      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
43018      */
43019     shadow:'sides',
43020     /**
43021      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
43022      * anchor positions (defaults to 'tl-bl')
43023      */
43024     listAlign: 'tl-bl?',
43025     /**
43026      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
43027      */
43028     maxHeight: 300,
43029     /**
43030      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
43031      * query specified by the allQuery config option (defaults to 'query')
43032      */
43033     triggerAction: 'query',
43034     /**
43035      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
43036      * (defaults to 4, does not apply if editable = false)
43037      */
43038     minChars : 4,
43039     /**
43040      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
43041      * delay (typeAheadDelay) if it matches a known value (defaults to false)
43042      */
43043     typeAhead: false,
43044     /**
43045      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
43046      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
43047      */
43048     queryDelay: 500,
43049     /**
43050      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
43051      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
43052      */
43053     pageSize: 0,
43054     /**
43055      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
43056      * when editable = true (defaults to false)
43057      */
43058     selectOnFocus:false,
43059     /**
43060      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
43061      */
43062     queryParam: 'query',
43063     /**
43064      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
43065      * when mode = 'remote' (defaults to 'Loading...')
43066      */
43067     loadingText: 'Loading...',
43068     /**
43069      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
43070      */
43071     resizable: false,
43072     /**
43073      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
43074      */
43075     handleHeight : 8,
43076     /**
43077      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
43078      * traditional select (defaults to true)
43079      */
43080     editable: true,
43081     /**
43082      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
43083      */
43084     allQuery: '',
43085     /**
43086      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
43087      */
43088     mode: 'remote',
43089     /**
43090      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
43091      * listWidth has a higher value)
43092      */
43093     minListWidth : 70,
43094     /**
43095      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
43096      * allow the user to set arbitrary text into the field (defaults to false)
43097      */
43098     forceSelection:false,
43099     /**
43100      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
43101      * if typeAhead = true (defaults to 250)
43102      */
43103     typeAheadDelay : 250,
43104     /**
43105      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
43106      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
43107      */
43108     valueNotFoundText : undefined,
43109     /**
43110      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
43111      */
43112     blockFocus : false,
43113     
43114     /**
43115      * @cfg {Boolean} disableClear Disable showing of clear button.
43116      */
43117     disableClear : false,
43118     /**
43119      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
43120      */
43121     alwaysQuery : false,
43122     
43123     //private
43124     addicon : false,
43125     editicon: false,
43126     
43127     // element that contains real text value.. (when hidden is used..)
43128      
43129     // private
43130     onRender : function(ct, position)
43131     {
43132         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
43133         
43134         if(this.hiddenName){
43135             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43136                     'before', true);
43137             this.hiddenField.value =
43138                 this.hiddenValue !== undefined ? this.hiddenValue :
43139                 this.value !== undefined ? this.value : '';
43140
43141             // prevent input submission
43142             this.el.dom.removeAttribute('name');
43143              
43144              
43145         }
43146         
43147         if(Roo.isGecko){
43148             this.el.dom.setAttribute('autocomplete', 'off');
43149         }
43150
43151         var cls = 'x-combo-list';
43152
43153         this.list = new Roo.Layer({
43154             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43155         });
43156
43157         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43158         this.list.setWidth(lw);
43159         this.list.swallowEvent('mousewheel');
43160         this.assetHeight = 0;
43161
43162         if(this.title){
43163             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43164             this.assetHeight += this.header.getHeight();
43165         }
43166
43167         this.innerList = this.list.createChild({cls:cls+'-inner'});
43168         this.innerList.on('mouseover', this.onViewOver, this);
43169         this.innerList.on('mousemove', this.onViewMove, this);
43170         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43171         
43172         if(this.allowBlank && !this.pageSize && !this.disableClear){
43173             this.footer = this.list.createChild({cls:cls+'-ft'});
43174             this.pageTb = new Roo.Toolbar(this.footer);
43175            
43176         }
43177         if(this.pageSize){
43178             this.footer = this.list.createChild({cls:cls+'-ft'});
43179             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
43180                     {pageSize: this.pageSize});
43181             
43182         }
43183         
43184         if (this.pageTb && this.allowBlank && !this.disableClear) {
43185             var _this = this;
43186             this.pageTb.add(new Roo.Toolbar.Fill(), {
43187                 cls: 'x-btn-icon x-btn-clear',
43188                 text: '&#160;',
43189                 handler: function()
43190                 {
43191                     _this.collapse();
43192                     _this.clearValue();
43193                     _this.onSelect(false, -1);
43194                 }
43195             });
43196         }
43197         if (this.footer) {
43198             this.assetHeight += this.footer.getHeight();
43199         }
43200         
43201
43202         if(!this.tpl){
43203             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
43204         }
43205
43206         this.view = new Roo.View(this.innerList, this.tpl, {
43207             singleSelect:true,
43208             store: this.store,
43209             selectedClass: this.selectedClass
43210         });
43211
43212         this.view.on('click', this.onViewClick, this);
43213
43214         this.store.on('beforeload', this.onBeforeLoad, this);
43215         this.store.on('load', this.onLoad, this);
43216         this.store.on('loadexception', this.onLoadException, this);
43217
43218         if(this.resizable){
43219             this.resizer = new Roo.Resizable(this.list,  {
43220                pinned:true, handles:'se'
43221             });
43222             this.resizer.on('resize', function(r, w, h){
43223                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
43224                 this.listWidth = w;
43225                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
43226                 this.restrictHeight();
43227             }, this);
43228             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
43229         }
43230         if(!this.editable){
43231             this.editable = true;
43232             this.setEditable(false);
43233         }  
43234         
43235         
43236         if (typeof(this.events.add.listeners) != 'undefined') {
43237             
43238             this.addicon = this.wrap.createChild(
43239                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
43240        
43241             this.addicon.on('click', function(e) {
43242                 this.fireEvent('add', this);
43243             }, this);
43244         }
43245         if (typeof(this.events.edit.listeners) != 'undefined') {
43246             
43247             this.editicon = this.wrap.createChild(
43248                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
43249             if (this.addicon) {
43250                 this.editicon.setStyle('margin-left', '40px');
43251             }
43252             this.editicon.on('click', function(e) {
43253                 
43254                 // we fire even  if inothing is selected..
43255                 this.fireEvent('edit', this, this.lastData );
43256                 
43257             }, this);
43258         }
43259         
43260         
43261         
43262     },
43263
43264     // private
43265     initEvents : function(){
43266         Roo.form.ComboBox.superclass.initEvents.call(this);
43267
43268         this.keyNav = new Roo.KeyNav(this.el, {
43269             "up" : function(e){
43270                 this.inKeyMode = true;
43271                 this.selectPrev();
43272             },
43273
43274             "down" : function(e){
43275                 if(!this.isExpanded()){
43276                     this.onTriggerClick();
43277                 }else{
43278                     this.inKeyMode = true;
43279                     this.selectNext();
43280                 }
43281             },
43282
43283             "enter" : function(e){
43284                 this.onViewClick();
43285                 //return true;
43286             },
43287
43288             "esc" : function(e){
43289                 this.collapse();
43290             },
43291
43292             "tab" : function(e){
43293                 this.onViewClick(false);
43294                 this.fireEvent("specialkey", this, e);
43295                 return true;
43296             },
43297
43298             scope : this,
43299
43300             doRelay : function(foo, bar, hname){
43301                 if(hname == 'down' || this.scope.isExpanded()){
43302                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43303                 }
43304                 return true;
43305             },
43306
43307             forceKeyDown: true
43308         });
43309         this.queryDelay = Math.max(this.queryDelay || 10,
43310                 this.mode == 'local' ? 10 : 250);
43311         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43312         if(this.typeAhead){
43313             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43314         }
43315         if(this.editable !== false){
43316             this.el.on("keyup", this.onKeyUp, this);
43317         }
43318         if(this.forceSelection){
43319             this.on('blur', this.doForce, this);
43320         }
43321     },
43322
43323     onDestroy : function(){
43324         if(this.view){
43325             this.view.setStore(null);
43326             this.view.el.removeAllListeners();
43327             this.view.el.remove();
43328             this.view.purgeListeners();
43329         }
43330         if(this.list){
43331             this.list.destroy();
43332         }
43333         if(this.store){
43334             this.store.un('beforeload', this.onBeforeLoad, this);
43335             this.store.un('load', this.onLoad, this);
43336             this.store.un('loadexception', this.onLoadException, this);
43337         }
43338         Roo.form.ComboBox.superclass.onDestroy.call(this);
43339     },
43340
43341     // private
43342     fireKey : function(e){
43343         if(e.isNavKeyPress() && !this.list.isVisible()){
43344             this.fireEvent("specialkey", this, e);
43345         }
43346     },
43347
43348     // private
43349     onResize: function(w, h){
43350         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43351         
43352         if(typeof w != 'number'){
43353             // we do not handle it!?!?
43354             return;
43355         }
43356         var tw = this.trigger.getWidth();
43357         tw += this.addicon ? this.addicon.getWidth() : 0;
43358         tw += this.editicon ? this.editicon.getWidth() : 0;
43359         var x = w - tw;
43360         this.el.setWidth( this.adjustWidth('input', x));
43361             
43362         this.trigger.setStyle('left', x+'px');
43363         
43364         if(this.list && this.listWidth === undefined){
43365             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43366             this.list.setWidth(lw);
43367             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43368         }
43369         
43370     
43371         
43372     },
43373
43374     /**
43375      * Allow or prevent the user from directly editing the field text.  If false is passed,
43376      * the user will only be able to select from the items defined in the dropdown list.  This method
43377      * is the runtime equivalent of setting the 'editable' config option at config time.
43378      * @param {Boolean} value True to allow the user to directly edit the field text
43379      */
43380     setEditable : function(value){
43381         if(value == this.editable){
43382             return;
43383         }
43384         this.editable = value;
43385         if(!value){
43386             this.el.dom.setAttribute('readOnly', true);
43387             this.el.on('mousedown', this.onTriggerClick,  this);
43388             this.el.addClass('x-combo-noedit');
43389         }else{
43390             this.el.dom.setAttribute('readOnly', false);
43391             this.el.un('mousedown', this.onTriggerClick,  this);
43392             this.el.removeClass('x-combo-noedit');
43393         }
43394     },
43395
43396     // private
43397     onBeforeLoad : function(){
43398         if(!this.hasFocus){
43399             return;
43400         }
43401         this.innerList.update(this.loadingText ?
43402                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43403         this.restrictHeight();
43404         this.selectedIndex = -1;
43405     },
43406
43407     // private
43408     onLoad : function(){
43409         if(!this.hasFocus){
43410             return;
43411         }
43412         if(this.store.getCount() > 0){
43413             this.expand();
43414             this.restrictHeight();
43415             if(this.lastQuery == this.allQuery){
43416                 if(this.editable){
43417                     this.el.dom.select();
43418                 }
43419                 if(!this.selectByValue(this.value, true)){
43420                     this.select(0, true);
43421                 }
43422             }else{
43423                 this.selectNext();
43424                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43425                     this.taTask.delay(this.typeAheadDelay);
43426                 }
43427             }
43428         }else{
43429             this.onEmptyResults();
43430         }
43431         //this.el.focus();
43432     },
43433     // private
43434     onLoadException : function()
43435     {
43436         this.collapse();
43437         Roo.log(this.store.reader.jsonData);
43438         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43439             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43440         }
43441         
43442         
43443     },
43444     // private
43445     onTypeAhead : function(){
43446         if(this.store.getCount() > 0){
43447             var r = this.store.getAt(0);
43448             var newValue = r.data[this.displayField];
43449             var len = newValue.length;
43450             var selStart = this.getRawValue().length;
43451             if(selStart != len){
43452                 this.setRawValue(newValue);
43453                 this.selectText(selStart, newValue.length);
43454             }
43455         }
43456     },
43457
43458     // private
43459     onSelect : function(record, index){
43460         if(this.fireEvent('beforeselect', this, record, index) !== false){
43461             this.setFromData(index > -1 ? record.data : false);
43462             this.collapse();
43463             this.fireEvent('select', this, record, index);
43464         }
43465     },
43466
43467     /**
43468      * Returns the currently selected field value or empty string if no value is set.
43469      * @return {String} value The selected value
43470      */
43471     getValue : function(){
43472         if(this.valueField){
43473             return typeof this.value != 'undefined' ? this.value : '';
43474         }
43475         return Roo.form.ComboBox.superclass.getValue.call(this);
43476     },
43477
43478     /**
43479      * Clears any text/value currently set in the field
43480      */
43481     clearValue : function(){
43482         if(this.hiddenField){
43483             this.hiddenField.value = '';
43484         }
43485         this.value = '';
43486         this.setRawValue('');
43487         this.lastSelectionText = '';
43488         
43489     },
43490
43491     /**
43492      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43493      * will be displayed in the field.  If the value does not match the data value of an existing item,
43494      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43495      * Otherwise the field will be blank (although the value will still be set).
43496      * @param {String} value The value to match
43497      */
43498     setValue : function(v){
43499         var text = v;
43500         if(this.valueField){
43501             var r = this.findRecord(this.valueField, v);
43502             if(r){
43503                 text = r.data[this.displayField];
43504             }else if(this.valueNotFoundText !== undefined){
43505                 text = this.valueNotFoundText;
43506             }
43507         }
43508         this.lastSelectionText = text;
43509         if(this.hiddenField){
43510             this.hiddenField.value = v;
43511         }
43512         Roo.form.ComboBox.superclass.setValue.call(this, text);
43513         this.value = v;
43514     },
43515     /**
43516      * @property {Object} the last set data for the element
43517      */
43518     
43519     lastData : false,
43520     /**
43521      * Sets the value of the field based on a object which is related to the record format for the store.
43522      * @param {Object} value the value to set as. or false on reset?
43523      */
43524     setFromData : function(o){
43525         var dv = ''; // display value
43526         var vv = ''; // value value..
43527         this.lastData = o;
43528         if (this.displayField) {
43529             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43530         } else {
43531             // this is an error condition!!!
43532             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43533         }
43534         
43535         if(this.valueField){
43536             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43537         }
43538         if(this.hiddenField){
43539             this.hiddenField.value = vv;
43540             
43541             this.lastSelectionText = dv;
43542             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43543             this.value = vv;
43544             return;
43545         }
43546         // no hidden field.. - we store the value in 'value', but still display
43547         // display field!!!!
43548         this.lastSelectionText = dv;
43549         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43550         this.value = vv;
43551         
43552         
43553     },
43554     // private
43555     reset : function(){
43556         // overridden so that last data is reset..
43557         this.setValue(this.resetValue);
43558         this.originalValue = this.getValue();
43559         this.clearInvalid();
43560         this.lastData = false;
43561         if (this.view) {
43562             this.view.clearSelections();
43563         }
43564     },
43565     // private
43566     findRecord : function(prop, value){
43567         var record;
43568         if(this.store.getCount() > 0){
43569             this.store.each(function(r){
43570                 if(r.data[prop] == value){
43571                     record = r;
43572                     return false;
43573                 }
43574                 return true;
43575             });
43576         }
43577         return record;
43578     },
43579     
43580     getName: function()
43581     {
43582         // returns hidden if it's set..
43583         if (!this.rendered) {return ''};
43584         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43585         
43586     },
43587     // private
43588     onViewMove : function(e, t){
43589         this.inKeyMode = false;
43590     },
43591
43592     // private
43593     onViewOver : function(e, t){
43594         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43595             return;
43596         }
43597         var item = this.view.findItemFromChild(t);
43598         if(item){
43599             var index = this.view.indexOf(item);
43600             this.select(index, false);
43601         }
43602     },
43603
43604     // private
43605     onViewClick : function(doFocus)
43606     {
43607         var index = this.view.getSelectedIndexes()[0];
43608         var r = this.store.getAt(index);
43609         if(r){
43610             this.onSelect(r, index);
43611         }
43612         if(doFocus !== false && !this.blockFocus){
43613             this.el.focus();
43614         }
43615     },
43616
43617     // private
43618     restrictHeight : function(){
43619         this.innerList.dom.style.height = '';
43620         var inner = this.innerList.dom;
43621         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43622         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43623         this.list.beginUpdate();
43624         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43625         this.list.alignTo(this.el, this.listAlign);
43626         this.list.endUpdate();
43627     },
43628
43629     // private
43630     onEmptyResults : function(){
43631         this.collapse();
43632     },
43633
43634     /**
43635      * Returns true if the dropdown list is expanded, else false.
43636      */
43637     isExpanded : function(){
43638         return this.list.isVisible();
43639     },
43640
43641     /**
43642      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43643      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43644      * @param {String} value The data value of the item to select
43645      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43646      * selected item if it is not currently in view (defaults to true)
43647      * @return {Boolean} True if the value matched an item in the list, else false
43648      */
43649     selectByValue : function(v, scrollIntoView){
43650         if(v !== undefined && v !== null){
43651             var r = this.findRecord(this.valueField || this.displayField, v);
43652             if(r){
43653                 this.select(this.store.indexOf(r), scrollIntoView);
43654                 return true;
43655             }
43656         }
43657         return false;
43658     },
43659
43660     /**
43661      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43662      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43663      * @param {Number} index The zero-based index of the list item to select
43664      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43665      * selected item if it is not currently in view (defaults to true)
43666      */
43667     select : function(index, scrollIntoView){
43668         this.selectedIndex = index;
43669         this.view.select(index);
43670         if(scrollIntoView !== false){
43671             var el = this.view.getNode(index);
43672             if(el){
43673                 this.innerList.scrollChildIntoView(el, false);
43674             }
43675         }
43676     },
43677
43678     // private
43679     selectNext : function(){
43680         var ct = this.store.getCount();
43681         if(ct > 0){
43682             if(this.selectedIndex == -1){
43683                 this.select(0);
43684             }else if(this.selectedIndex < ct-1){
43685                 this.select(this.selectedIndex+1);
43686             }
43687         }
43688     },
43689
43690     // private
43691     selectPrev : function(){
43692         var ct = this.store.getCount();
43693         if(ct > 0){
43694             if(this.selectedIndex == -1){
43695                 this.select(0);
43696             }else if(this.selectedIndex != 0){
43697                 this.select(this.selectedIndex-1);
43698             }
43699         }
43700     },
43701
43702     // private
43703     onKeyUp : function(e){
43704         if(this.editable !== false && !e.isSpecialKey()){
43705             this.lastKey = e.getKey();
43706             this.dqTask.delay(this.queryDelay);
43707         }
43708     },
43709
43710     // private
43711     validateBlur : function(){
43712         return !this.list || !this.list.isVisible();   
43713     },
43714
43715     // private
43716     initQuery : function(){
43717         this.doQuery(this.getRawValue());
43718     },
43719
43720     // private
43721     doForce : function(){
43722         if(this.el.dom.value.length > 0){
43723             this.el.dom.value =
43724                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43725              
43726         }
43727     },
43728
43729     /**
43730      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43731      * query allowing the query action to be canceled if needed.
43732      * @param {String} query The SQL query to execute
43733      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43734      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43735      * saved in the current store (defaults to false)
43736      */
43737     doQuery : function(q, forceAll){
43738         if(q === undefined || q === null){
43739             q = '';
43740         }
43741         var qe = {
43742             query: q,
43743             forceAll: forceAll,
43744             combo: this,
43745             cancel:false
43746         };
43747         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43748             return false;
43749         }
43750         q = qe.query;
43751         forceAll = qe.forceAll;
43752         if(forceAll === true || (q.length >= this.minChars)){
43753             if(this.lastQuery != q || this.alwaysQuery){
43754                 this.lastQuery = q;
43755                 if(this.mode == 'local'){
43756                     this.selectedIndex = -1;
43757                     if(forceAll){
43758                         this.store.clearFilter();
43759                     }else{
43760                         this.store.filter(this.displayField, q);
43761                     }
43762                     this.onLoad();
43763                 }else{
43764                     this.store.baseParams[this.queryParam] = q;
43765                     this.store.load({
43766                         params: this.getParams(q)
43767                     });
43768                     this.expand();
43769                 }
43770             }else{
43771                 this.selectedIndex = -1;
43772                 this.onLoad();   
43773             }
43774         }
43775     },
43776
43777     // private
43778     getParams : function(q){
43779         var p = {};
43780         //p[this.queryParam] = q;
43781         if(this.pageSize){
43782             p.start = 0;
43783             p.limit = this.pageSize;
43784         }
43785         return p;
43786     },
43787
43788     /**
43789      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43790      */
43791     collapse : function(){
43792         if(!this.isExpanded()){
43793             return;
43794         }
43795         this.list.hide();
43796         Roo.get(document).un('mousedown', this.collapseIf, this);
43797         Roo.get(document).un('mousewheel', this.collapseIf, this);
43798         if (!this.editable) {
43799             Roo.get(document).un('keydown', this.listKeyPress, this);
43800         }
43801         this.fireEvent('collapse', this);
43802     },
43803
43804     // private
43805     collapseIf : function(e){
43806         if(!e.within(this.wrap) && !e.within(this.list)){
43807             this.collapse();
43808         }
43809     },
43810
43811     /**
43812      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43813      */
43814     expand : function(){
43815         if(this.isExpanded() || !this.hasFocus){
43816             return;
43817         }
43818         this.list.alignTo(this.el, this.listAlign);
43819         this.list.show();
43820         Roo.get(document).on('mousedown', this.collapseIf, this);
43821         Roo.get(document).on('mousewheel', this.collapseIf, this);
43822         if (!this.editable) {
43823             Roo.get(document).on('keydown', this.listKeyPress, this);
43824         }
43825         
43826         this.fireEvent('expand', this);
43827     },
43828
43829     // private
43830     // Implements the default empty TriggerField.onTriggerClick function
43831     onTriggerClick : function(){
43832         if(this.disabled){
43833             return;
43834         }
43835         if(this.isExpanded()){
43836             this.collapse();
43837             if (!this.blockFocus) {
43838                 this.el.focus();
43839             }
43840             
43841         }else {
43842             this.hasFocus = true;
43843             if(this.triggerAction == 'all') {
43844                 this.doQuery(this.allQuery, true);
43845             } else {
43846                 this.doQuery(this.getRawValue());
43847             }
43848             if (!this.blockFocus) {
43849                 this.el.focus();
43850             }
43851         }
43852     },
43853     listKeyPress : function(e)
43854     {
43855         //Roo.log('listkeypress');
43856         // scroll to first matching element based on key pres..
43857         if (e.isSpecialKey()) {
43858             return false;
43859         }
43860         var k = String.fromCharCode(e.getKey()).toUpperCase();
43861         //Roo.log(k);
43862         var match  = false;
43863         var csel = this.view.getSelectedNodes();
43864         var cselitem = false;
43865         if (csel.length) {
43866             var ix = this.view.indexOf(csel[0]);
43867             cselitem  = this.store.getAt(ix);
43868             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43869                 cselitem = false;
43870             }
43871             
43872         }
43873         
43874         this.store.each(function(v) { 
43875             if (cselitem) {
43876                 // start at existing selection.
43877                 if (cselitem.id == v.id) {
43878                     cselitem = false;
43879                 }
43880                 return;
43881             }
43882                 
43883             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43884                 match = this.store.indexOf(v);
43885                 return false;
43886             }
43887         }, this);
43888         
43889         if (match === false) {
43890             return true; // no more action?
43891         }
43892         // scroll to?
43893         this.view.select(match);
43894         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43895         sn.scrollIntoView(sn.dom.parentNode, false);
43896     } 
43897
43898     /** 
43899     * @cfg {Boolean} grow 
43900     * @hide 
43901     */
43902     /** 
43903     * @cfg {Number} growMin 
43904     * @hide 
43905     */
43906     /** 
43907     * @cfg {Number} growMax 
43908     * @hide 
43909     */
43910     /**
43911      * @hide
43912      * @method autoSize
43913      */
43914 });/*
43915  * Copyright(c) 2010-2012, Roo J Solutions Limited
43916  *
43917  * Licence LGPL
43918  *
43919  */
43920
43921 /**
43922  * @class Roo.form.ComboBoxArray
43923  * @extends Roo.form.TextField
43924  * A facebook style adder... for lists of email / people / countries  etc...
43925  * pick multiple items from a combo box, and shows each one.
43926  *
43927  *  Fred [x]  Brian [x]  [Pick another |v]
43928  *
43929  *
43930  *  For this to work: it needs various extra information
43931  *    - normal combo problay has
43932  *      name, hiddenName
43933  *    + displayField, valueField
43934  *
43935  *    For our purpose...
43936  *
43937  *
43938  *   If we change from 'extends' to wrapping...
43939  *   
43940  *  
43941  *
43942  
43943  
43944  * @constructor
43945  * Create a new ComboBoxArray.
43946  * @param {Object} config Configuration options
43947  */
43948  
43949
43950 Roo.form.ComboBoxArray = function(config)
43951 {
43952     this.addEvents({
43953         /**
43954          * @event beforeremove
43955          * Fires before remove the value from the list
43956              * @param {Roo.form.ComboBoxArray} _self This combo box array
43957              * @param {Roo.form.ComboBoxArray.Item} item removed item
43958              */
43959         'beforeremove' : true,
43960         /**
43961          * @event remove
43962          * Fires when remove the value from the list
43963              * @param {Roo.form.ComboBoxArray} _self This combo box array
43964              * @param {Roo.form.ComboBoxArray.Item} item removed item
43965              */
43966         'remove' : true
43967         
43968         
43969     });
43970     
43971     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43972     
43973     this.items = new Roo.util.MixedCollection(false);
43974     
43975     // construct the child combo...
43976     
43977     
43978     
43979     
43980    
43981     
43982 }
43983
43984  
43985 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43986
43987     /**
43988      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43989      */
43990     
43991     lastData : false,
43992     
43993     // behavies liek a hiddne field
43994     inputType:      'hidden',
43995     /**
43996      * @cfg {Number} width The width of the box that displays the selected element
43997      */ 
43998     width:          300,
43999
44000     
44001     
44002     /**
44003      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
44004      */
44005     name : false,
44006     /**
44007      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
44008      */
44009     hiddenName : false,
44010       /**
44011      * @cfg {String} seperator    The value seperator normally ',' 
44012      */
44013     seperator : ',',
44014     
44015     // private the array of items that are displayed..
44016     items  : false,
44017     // private - the hidden field el.
44018     hiddenEl : false,
44019     // private - the filed el..
44020     el : false,
44021     
44022     //validateValue : function() { return true; }, // all values are ok!
44023     //onAddClick: function() { },
44024     
44025     onRender : function(ct, position) 
44026     {
44027         
44028         // create the standard hidden element
44029         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
44030         
44031         
44032         // give fake names to child combo;
44033         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
44034         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
44035         
44036         this.combo = Roo.factory(this.combo, Roo.form);
44037         this.combo.onRender(ct, position);
44038         if (typeof(this.combo.width) != 'undefined') {
44039             this.combo.onResize(this.combo.width,0);
44040         }
44041         
44042         this.combo.initEvents();
44043         
44044         // assigned so form know we need to do this..
44045         this.store          = this.combo.store;
44046         this.valueField     = this.combo.valueField;
44047         this.displayField   = this.combo.displayField ;
44048         
44049         
44050         this.combo.wrap.addClass('x-cbarray-grp');
44051         
44052         var cbwrap = this.combo.wrap.createChild(
44053             {tag: 'div', cls: 'x-cbarray-cb'},
44054             this.combo.el.dom
44055         );
44056         
44057              
44058         this.hiddenEl = this.combo.wrap.createChild({
44059             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
44060         });
44061         this.el = this.combo.wrap.createChild({
44062             tag: 'input',  type:'hidden' , name: this.name, value : ''
44063         });
44064          //   this.el.dom.removeAttribute("name");
44065         
44066         
44067         this.outerWrap = this.combo.wrap;
44068         this.wrap = cbwrap;
44069         
44070         this.outerWrap.setWidth(this.width);
44071         this.outerWrap.dom.removeChild(this.el.dom);
44072         
44073         this.wrap.dom.appendChild(this.el.dom);
44074         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
44075         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
44076         
44077         this.combo.trigger.setStyle('position','relative');
44078         this.combo.trigger.setStyle('left', '0px');
44079         this.combo.trigger.setStyle('top', '2px');
44080         
44081         this.combo.el.setStyle('vertical-align', 'text-bottom');
44082         
44083         //this.trigger.setStyle('vertical-align', 'top');
44084         
44085         // this should use the code from combo really... on('add' ....)
44086         if (this.adder) {
44087             
44088         
44089             this.adder = this.outerWrap.createChild(
44090                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
44091             var _t = this;
44092             this.adder.on('click', function(e) {
44093                 _t.fireEvent('adderclick', this, e);
44094             }, _t);
44095         }
44096         //var _t = this;
44097         //this.adder.on('click', this.onAddClick, _t);
44098         
44099         
44100         this.combo.on('select', function(cb, rec, ix) {
44101             this.addItem(rec.data);
44102             
44103             cb.setValue('');
44104             cb.el.dom.value = '';
44105             //cb.lastData = rec.data;
44106             // add to list
44107             
44108         }, this);
44109         
44110         
44111     },
44112     
44113     
44114     getName: function()
44115     {
44116         // returns hidden if it's set..
44117         if (!this.rendered) {return ''};
44118         return  this.hiddenName ? this.hiddenName : this.name;
44119         
44120     },
44121     
44122     
44123     onResize: function(w, h){
44124         
44125         return;
44126         // not sure if this is needed..
44127         //this.combo.onResize(w,h);
44128         
44129         if(typeof w != 'number'){
44130             // we do not handle it!?!?
44131             return;
44132         }
44133         var tw = this.combo.trigger.getWidth();
44134         tw += this.addicon ? this.addicon.getWidth() : 0;
44135         tw += this.editicon ? this.editicon.getWidth() : 0;
44136         var x = w - tw;
44137         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
44138             
44139         this.combo.trigger.setStyle('left', '0px');
44140         
44141         if(this.list && this.listWidth === undefined){
44142             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
44143             this.list.setWidth(lw);
44144             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
44145         }
44146         
44147     
44148         
44149     },
44150     
44151     addItem: function(rec)
44152     {
44153         var valueField = this.combo.valueField;
44154         var displayField = this.combo.displayField;
44155         
44156         if (this.items.indexOfKey(rec[valueField]) > -1) {
44157             //console.log("GOT " + rec.data.id);
44158             return;
44159         }
44160         
44161         var x = new Roo.form.ComboBoxArray.Item({
44162             //id : rec[this.idField],
44163             data : rec,
44164             displayField : displayField ,
44165             tipField : displayField ,
44166             cb : this
44167         });
44168         // use the 
44169         this.items.add(rec[valueField],x);
44170         // add it before the element..
44171         this.updateHiddenEl();
44172         x.render(this.outerWrap, this.wrap.dom);
44173         // add the image handler..
44174     },
44175     
44176     updateHiddenEl : function()
44177     {
44178         this.validate();
44179         if (!this.hiddenEl) {
44180             return;
44181         }
44182         var ar = [];
44183         var idField = this.combo.valueField;
44184         
44185         this.items.each(function(f) {
44186             ar.push(f.data[idField]);
44187         });
44188         this.hiddenEl.dom.value = ar.join(this.seperator);
44189         this.validate();
44190     },
44191     
44192     reset : function()
44193     {
44194         this.items.clear();
44195         
44196         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
44197            el.remove();
44198         });
44199         
44200         this.el.dom.value = '';
44201         if (this.hiddenEl) {
44202             this.hiddenEl.dom.value = '';
44203         }
44204         
44205     },
44206     getValue: function()
44207     {
44208         return this.hiddenEl ? this.hiddenEl.dom.value : '';
44209     },
44210     setValue: function(v) // not a valid action - must use addItems..
44211     {
44212         
44213         this.reset();
44214          
44215         if (this.store.isLocal && (typeof(v) == 'string')) {
44216             // then we can use the store to find the values..
44217             // comma seperated at present.. this needs to allow JSON based encoding..
44218             this.hiddenEl.value  = v;
44219             var v_ar = [];
44220             Roo.each(v.split(this.seperator), function(k) {
44221                 Roo.log("CHECK " + this.valueField + ',' + k);
44222                 var li = this.store.query(this.valueField, k);
44223                 if (!li.length) {
44224                     return;
44225                 }
44226                 var add = {};
44227                 add[this.valueField] = k;
44228                 add[this.displayField] = li.item(0).data[this.displayField];
44229                 
44230                 this.addItem(add);
44231             }, this) 
44232              
44233         }
44234         if (typeof(v) == 'object' ) {
44235             // then let's assume it's an array of objects..
44236             Roo.each(v, function(l) {
44237                 var add = l;
44238                 if (typeof(l) == 'string') {
44239                     add = {};
44240                     add[this.valueField] = l;
44241                     add[this.displayField] = l
44242                 }
44243                 this.addItem(add);
44244             }, this);
44245              
44246         }
44247         
44248         
44249     },
44250     setFromData: function(v)
44251     {
44252         // this recieves an object, if setValues is called.
44253         this.reset();
44254         this.el.dom.value = v[this.displayField];
44255         this.hiddenEl.dom.value = v[this.valueField];
44256         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44257             return;
44258         }
44259         var kv = v[this.valueField];
44260         var dv = v[this.displayField];
44261         kv = typeof(kv) != 'string' ? '' : kv;
44262         dv = typeof(dv) != 'string' ? '' : dv;
44263         
44264         
44265         var keys = kv.split(this.seperator);
44266         var display = dv.split(this.seperator);
44267         for (var i = 0 ; i < keys.length; i++) {
44268             add = {};
44269             add[this.valueField] = keys[i];
44270             add[this.displayField] = display[i];
44271             this.addItem(add);
44272         }
44273       
44274         
44275     },
44276     
44277     /**
44278      * Validates the combox array value
44279      * @return {Boolean} True if the value is valid, else false
44280      */
44281     validate : function(){
44282         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44283             this.clearInvalid();
44284             return true;
44285         }
44286         return false;
44287     },
44288     
44289     validateValue : function(value){
44290         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44291         
44292     },
44293     
44294     /*@
44295      * overide
44296      * 
44297      */
44298     isDirty : function() {
44299         if(this.disabled) {
44300             return false;
44301         }
44302         
44303         try {
44304             var d = Roo.decode(String(this.originalValue));
44305         } catch (e) {
44306             return String(this.getValue()) !== String(this.originalValue);
44307         }
44308         
44309         var originalValue = [];
44310         
44311         for (var i = 0; i < d.length; i++){
44312             originalValue.push(d[i][this.valueField]);
44313         }
44314         
44315         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44316         
44317     }
44318     
44319 });
44320
44321
44322
44323 /**
44324  * @class Roo.form.ComboBoxArray.Item
44325  * @extends Roo.BoxComponent
44326  * A selected item in the list
44327  *  Fred [x]  Brian [x]  [Pick another |v]
44328  * 
44329  * @constructor
44330  * Create a new item.
44331  * @param {Object} config Configuration options
44332  */
44333  
44334 Roo.form.ComboBoxArray.Item = function(config) {
44335     config.id = Roo.id();
44336     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44337 }
44338
44339 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44340     data : {},
44341     cb: false,
44342     displayField : false,
44343     tipField : false,
44344     
44345     
44346     defaultAutoCreate : {
44347         tag: 'div',
44348         cls: 'x-cbarray-item',
44349         cn : [ 
44350             { tag: 'div' },
44351             {
44352                 tag: 'img',
44353                 width:16,
44354                 height : 16,
44355                 src : Roo.BLANK_IMAGE_URL ,
44356                 align: 'center'
44357             }
44358         ]
44359         
44360     },
44361     
44362  
44363     onRender : function(ct, position)
44364     {
44365         Roo.form.Field.superclass.onRender.call(this, ct, position);
44366         
44367         if(!this.el){
44368             var cfg = this.getAutoCreate();
44369             this.el = ct.createChild(cfg, position);
44370         }
44371         
44372         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44373         
44374         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44375             this.cb.renderer(this.data) :
44376             String.format('{0}',this.data[this.displayField]);
44377         
44378             
44379         this.el.child('div').dom.setAttribute('qtip',
44380                         String.format('{0}',this.data[this.tipField])
44381         );
44382         
44383         this.el.child('img').on('click', this.remove, this);
44384         
44385     },
44386    
44387     remove : function()
44388     {
44389         if(this.cb.disabled){
44390             return;
44391         }
44392         
44393         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44394             this.cb.items.remove(this);
44395             this.el.child('img').un('click', this.remove, this);
44396             this.el.remove();
44397             this.cb.updateHiddenEl();
44398
44399             this.cb.fireEvent('remove', this.cb, this);
44400         }
44401         
44402     }
44403 });/*
44404  * RooJS Library 1.1.1
44405  * Copyright(c) 2008-2011  Alan Knowles
44406  *
44407  * License - LGPL
44408  */
44409  
44410
44411 /**
44412  * @class Roo.form.ComboNested
44413  * @extends Roo.form.ComboBox
44414  * A combobox for that allows selection of nested items in a list,
44415  * eg.
44416  *
44417  *  Book
44418  *    -> red
44419  *    -> green
44420  *  Table
44421  *    -> square
44422  *      ->red
44423  *      ->green
44424  *    -> rectangle
44425  *      ->green
44426  *      
44427  * 
44428  * @constructor
44429  * Create a new ComboNested
44430  * @param {Object} config Configuration options
44431  */
44432 Roo.form.ComboNested = function(config){
44433     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44434     // should verify some data...
44435     // like
44436     // hiddenName = required..
44437     // displayField = required
44438     // valudField == required
44439     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44440     var _t = this;
44441     Roo.each(req, function(e) {
44442         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44443             throw "Roo.form.ComboNested : missing value for: " + e;
44444         }
44445     });
44446      
44447     
44448 };
44449
44450 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44451    
44452     /*
44453      * @config {Number} max Number of columns to show
44454      */
44455     
44456     maxColumns : 3,
44457    
44458     list : null, // the outermost div..
44459     innerLists : null, // the
44460     views : null,
44461     stores : null,
44462     // private
44463     loadingChildren : false,
44464     
44465     onRender : function(ct, position)
44466     {
44467         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44468         
44469         if(this.hiddenName){
44470             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44471                     'before', true);
44472             this.hiddenField.value =
44473                 this.hiddenValue !== undefined ? this.hiddenValue :
44474                 this.value !== undefined ? this.value : '';
44475
44476             // prevent input submission
44477             this.el.dom.removeAttribute('name');
44478              
44479              
44480         }
44481         
44482         if(Roo.isGecko){
44483             this.el.dom.setAttribute('autocomplete', 'off');
44484         }
44485
44486         var cls = 'x-combo-list';
44487
44488         this.list = new Roo.Layer({
44489             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44490         });
44491
44492         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44493         this.list.setWidth(lw);
44494         this.list.swallowEvent('mousewheel');
44495         this.assetHeight = 0;
44496
44497         if(this.title){
44498             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44499             this.assetHeight += this.header.getHeight();
44500         }
44501         this.innerLists = [];
44502         this.views = [];
44503         this.stores = [];
44504         for (var i =0 ; i < this.maxColumns; i++) {
44505             this.onRenderList( cls, i);
44506         }
44507         
44508         // always needs footer, as we are going to have an 'OK' button.
44509         this.footer = this.list.createChild({cls:cls+'-ft'});
44510         this.pageTb = new Roo.Toolbar(this.footer);  
44511         var _this = this;
44512         this.pageTb.add(  {
44513             
44514             text: 'Done',
44515             handler: function()
44516             {
44517                 _this.collapse();
44518             }
44519         });
44520         
44521         if ( this.allowBlank && !this.disableClear) {
44522             
44523             this.pageTb.add(new Roo.Toolbar.Fill(), {
44524                 cls: 'x-btn-icon x-btn-clear',
44525                 text: '&#160;',
44526                 handler: function()
44527                 {
44528                     _this.collapse();
44529                     _this.clearValue();
44530                     _this.onSelect(false, -1);
44531                 }
44532             });
44533         }
44534         if (this.footer) {
44535             this.assetHeight += this.footer.getHeight();
44536         }
44537         
44538     },
44539     onRenderList : function (  cls, i)
44540     {
44541         
44542         var lw = Math.floor(
44543                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44544         );
44545         
44546         this.list.setWidth(lw); // default to '1'
44547
44548         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44549         //il.on('mouseover', this.onViewOver, this, { list:  i });
44550         //il.on('mousemove', this.onViewMove, this, { list:  i });
44551         il.setWidth(lw);
44552         il.setStyle({ 'overflow-x' : 'hidden'});
44553
44554         if(!this.tpl){
44555             this.tpl = new Roo.Template({
44556                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44557                 isEmpty: function (value, allValues) {
44558                     //Roo.log(value);
44559                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44560                     return dl ? 'has-children' : 'no-children'
44561                 }
44562             });
44563         }
44564         
44565         var store  = this.store;
44566         if (i > 0) {
44567             store  = new Roo.data.SimpleStore({
44568                 //fields : this.store.reader.meta.fields,
44569                 reader : this.store.reader,
44570                 data : [ ]
44571             });
44572         }
44573         this.stores[i]  = store;
44574                   
44575         var view = this.views[i] = new Roo.View(
44576             il,
44577             this.tpl,
44578             {
44579                 singleSelect:true,
44580                 store: store,
44581                 selectedClass: this.selectedClass
44582             }
44583         );
44584         view.getEl().setWidth(lw);
44585         view.getEl().setStyle({
44586             position: i < 1 ? 'relative' : 'absolute',
44587             top: 0,
44588             left: (i * lw ) + 'px',
44589             display : i > 0 ? 'none' : 'block'
44590         });
44591         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44592         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44593         //view.on('click', this.onViewClick, this, { list : i });
44594
44595         store.on('beforeload', this.onBeforeLoad, this);
44596         store.on('load',  this.onLoad, this, { list  : i});
44597         store.on('loadexception', this.onLoadException, this);
44598
44599         // hide the other vies..
44600         
44601         
44602         
44603     },
44604       
44605     restrictHeight : function()
44606     {
44607         var mh = 0;
44608         Roo.each(this.innerLists, function(il,i) {
44609             var el = this.views[i].getEl();
44610             el.dom.style.height = '';
44611             var inner = el.dom;
44612             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44613             // only adjust heights on other ones..
44614             mh = Math.max(h, mh);
44615             if (i < 1) {
44616                 
44617                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44618                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44619                
44620             }
44621             
44622             
44623         }, this);
44624         
44625         this.list.beginUpdate();
44626         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44627         this.list.alignTo(this.el, this.listAlign);
44628         this.list.endUpdate();
44629         
44630     },
44631      
44632     
44633     // -- store handlers..
44634     // private
44635     onBeforeLoad : function()
44636     {
44637         if(!this.hasFocus){
44638             return;
44639         }
44640         this.innerLists[0].update(this.loadingText ?
44641                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44642         this.restrictHeight();
44643         this.selectedIndex = -1;
44644     },
44645     // private
44646     onLoad : function(a,b,c,d)
44647     {
44648         if (!this.loadingChildren) {
44649             // then we are loading the top level. - hide the children
44650             for (var i = 1;i < this.views.length; i++) {
44651                 this.views[i].getEl().setStyle({ display : 'none' });
44652             }
44653             var lw = Math.floor(
44654                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44655             );
44656         
44657              this.list.setWidth(lw); // default to '1'
44658
44659             
44660         }
44661         if(!this.hasFocus){
44662             return;
44663         }
44664         
44665         if(this.store.getCount() > 0) {
44666             this.expand();
44667             this.restrictHeight();   
44668         } else {
44669             this.onEmptyResults();
44670         }
44671         
44672         if (!this.loadingChildren) {
44673             this.selectActive();
44674         }
44675         /*
44676         this.stores[1].loadData([]);
44677         this.stores[2].loadData([]);
44678         this.views
44679         */    
44680     
44681         //this.el.focus();
44682     },
44683     
44684     
44685     // private
44686     onLoadException : function()
44687     {
44688         this.collapse();
44689         Roo.log(this.store.reader.jsonData);
44690         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44691             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44692         }
44693         
44694         
44695     },
44696     // no cleaning of leading spaces on blur here.
44697     cleanLeadingSpace : function(e) { },
44698     
44699
44700     onSelectChange : function (view, sels, opts )
44701     {
44702         var ix = view.getSelectedIndexes();
44703          
44704         if (opts.list > this.maxColumns - 2) {
44705             if (view.store.getCount()<  1) {
44706                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44707
44708             } else  {
44709                 if (ix.length) {
44710                     // used to clear ?? but if we are loading unselected 
44711                     this.setFromData(view.store.getAt(ix[0]).data);
44712                 }
44713                 
44714             }
44715             
44716             return;
44717         }
44718         
44719         if (!ix.length) {
44720             // this get's fired when trigger opens..
44721            // this.setFromData({});
44722             var str = this.stores[opts.list+1];
44723             str.data.clear(); // removeall wihtout the fire events..
44724             return;
44725         }
44726         
44727         var rec = view.store.getAt(ix[0]);
44728          
44729         this.setFromData(rec.data);
44730         this.fireEvent('select', this, rec, ix[0]);
44731         
44732         var lw = Math.floor(
44733              (
44734                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44735              ) / this.maxColumns
44736         );
44737         this.loadingChildren = true;
44738         this.stores[opts.list+1].loadDataFromChildren( rec );
44739         this.loadingChildren = false;
44740         var dl = this.stores[opts.list+1]. getTotalCount();
44741         
44742         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44743         
44744         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44745         for (var i = opts.list+2; i < this.views.length;i++) {
44746             this.views[i].getEl().setStyle({ display : 'none' });
44747         }
44748         
44749         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44750         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44751         
44752         if (this.isLoading) {
44753            // this.selectActive(opts.list);
44754         }
44755          
44756     },
44757     
44758     
44759     
44760     
44761     onDoubleClick : function()
44762     {
44763         this.collapse(); //??
44764     },
44765     
44766      
44767     
44768     
44769     
44770     // private
44771     recordToStack : function(store, prop, value, stack)
44772     {
44773         var cstore = new Roo.data.SimpleStore({
44774             //fields : this.store.reader.meta.fields, // we need array reader.. for
44775             reader : this.store.reader,
44776             data : [ ]
44777         });
44778         var _this = this;
44779         var record  = false;
44780         var srec = false;
44781         if(store.getCount() < 1){
44782             return false;
44783         }
44784         store.each(function(r){
44785             if(r.data[prop] == value){
44786                 record = r;
44787             srec = r;
44788                 return false;
44789             }
44790             if (r.data.cn && r.data.cn.length) {
44791                 cstore.loadDataFromChildren( r);
44792                 var cret = _this.recordToStack(cstore, prop, value, stack);
44793                 if (cret !== false) {
44794                     record = cret;
44795                     srec = r;
44796                     return false;
44797                 }
44798             }
44799              
44800             return true;
44801         });
44802         if (record == false) {
44803             return false
44804         }
44805         stack.unshift(srec);
44806         return record;
44807     },
44808     
44809     /*
44810      * find the stack of stores that match our value.
44811      *
44812      * 
44813      */
44814     
44815     selectActive : function ()
44816     {
44817         // if store is not loaded, then we will need to wait for that to happen first.
44818         var stack = [];
44819         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44820         for (var i = 0; i < stack.length; i++ ) {
44821             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44822         }
44823         
44824     }
44825         
44826          
44827     
44828     
44829     
44830     
44831 });/*
44832  * Based on:
44833  * Ext JS Library 1.1.1
44834  * Copyright(c) 2006-2007, Ext JS, LLC.
44835  *
44836  * Originally Released Under LGPL - original licence link has changed is not relivant.
44837  *
44838  * Fork - LGPL
44839  * <script type="text/javascript">
44840  */
44841 /**
44842  * @class Roo.form.Checkbox
44843  * @extends Roo.form.Field
44844  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44845  * @constructor
44846  * Creates a new Checkbox
44847  * @param {Object} config Configuration options
44848  */
44849 Roo.form.Checkbox = function(config){
44850     Roo.form.Checkbox.superclass.constructor.call(this, config);
44851     this.addEvents({
44852         /**
44853          * @event check
44854          * Fires when the checkbox is checked or unchecked.
44855              * @param {Roo.form.Checkbox} this This checkbox
44856              * @param {Boolean} checked The new checked value
44857              */
44858         check : true
44859     });
44860 };
44861
44862 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44863     /**
44864      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44865      */
44866     focusClass : undefined,
44867     /**
44868      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44869      */
44870     fieldClass: "x-form-field",
44871     /**
44872      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44873      */
44874     checked: false,
44875     /**
44876      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44877      * {tag: "input", type: "checkbox", autocomplete: "off"})
44878      */
44879     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44880     /**
44881      * @cfg {String} boxLabel The text that appears beside the checkbox
44882      */
44883     boxLabel : "",
44884     /**
44885      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44886      */  
44887     inputValue : '1',
44888     /**
44889      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44890      */
44891      valueOff: '0', // value when not checked..
44892
44893     actionMode : 'viewEl', 
44894     //
44895     // private
44896     itemCls : 'x-menu-check-item x-form-item',
44897     groupClass : 'x-menu-group-item',
44898     inputType : 'hidden',
44899     
44900     
44901     inSetChecked: false, // check that we are not calling self...
44902     
44903     inputElement: false, // real input element?
44904     basedOn: false, // ????
44905     
44906     isFormField: true, // not sure where this is needed!!!!
44907
44908     onResize : function(){
44909         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44910         if(!this.boxLabel){
44911             this.el.alignTo(this.wrap, 'c-c');
44912         }
44913     },
44914
44915     initEvents : function(){
44916         Roo.form.Checkbox.superclass.initEvents.call(this);
44917         this.el.on("click", this.onClick,  this);
44918         this.el.on("change", this.onClick,  this);
44919     },
44920
44921
44922     getResizeEl : function(){
44923         return this.wrap;
44924     },
44925
44926     getPositionEl : function(){
44927         return this.wrap;
44928     },
44929
44930     // private
44931     onRender : function(ct, position){
44932         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44933         /*
44934         if(this.inputValue !== undefined){
44935             this.el.dom.value = this.inputValue;
44936         }
44937         */
44938         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44939         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44940         var viewEl = this.wrap.createChild({ 
44941             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44942         this.viewEl = viewEl;   
44943         this.wrap.on('click', this.onClick,  this); 
44944         
44945         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44946         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44947         
44948         
44949         
44950         if(this.boxLabel){
44951             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44952         //    viewEl.on('click', this.onClick,  this); 
44953         }
44954         //if(this.checked){
44955             this.setChecked(this.checked);
44956         //}else{
44957             //this.checked = this.el.dom;
44958         //}
44959
44960     },
44961
44962     // private
44963     initValue : Roo.emptyFn,
44964
44965     /**
44966      * Returns the checked state of the checkbox.
44967      * @return {Boolean} True if checked, else false
44968      */
44969     getValue : function(){
44970         if(this.el){
44971             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44972         }
44973         return this.valueOff;
44974         
44975     },
44976
44977         // private
44978     onClick : function(){ 
44979         if (this.disabled) {
44980             return;
44981         }
44982         this.setChecked(!this.checked);
44983
44984         //if(this.el.dom.checked != this.checked){
44985         //    this.setValue(this.el.dom.checked);
44986        // }
44987     },
44988
44989     /**
44990      * Sets the checked state of the checkbox.
44991      * On is always based on a string comparison between inputValue and the param.
44992      * @param {Boolean/String} value - the value to set 
44993      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44994      */
44995     setValue : function(v,suppressEvent){
44996         
44997         
44998         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44999         //if(this.el && this.el.dom){
45000         //    this.el.dom.checked = this.checked;
45001         //    this.el.dom.defaultChecked = this.checked;
45002         //}
45003         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
45004         //this.fireEvent("check", this, this.checked);
45005     },
45006     // private..
45007     setChecked : function(state,suppressEvent)
45008     {
45009         if (this.inSetChecked) {
45010             this.checked = state;
45011             return;
45012         }
45013         
45014     
45015         if(this.wrap){
45016             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
45017         }
45018         this.checked = state;
45019         if(suppressEvent !== true){
45020             this.fireEvent('check', this, state);
45021         }
45022         this.inSetChecked = true;
45023         this.el.dom.value = state ? this.inputValue : this.valueOff;
45024         this.inSetChecked = false;
45025         
45026     },
45027     // handle setting of hidden value by some other method!!?!?
45028     setFromHidden: function()
45029     {
45030         if(!this.el){
45031             return;
45032         }
45033         //console.log("SET FROM HIDDEN");
45034         //alert('setFrom hidden');
45035         this.setValue(this.el.dom.value);
45036     },
45037     
45038     onDestroy : function()
45039     {
45040         if(this.viewEl){
45041             Roo.get(this.viewEl).remove();
45042         }
45043          
45044         Roo.form.Checkbox.superclass.onDestroy.call(this);
45045     },
45046     
45047     setBoxLabel : function(str)
45048     {
45049         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
45050     }
45051
45052 });/*
45053  * Based on:
45054  * Ext JS Library 1.1.1
45055  * Copyright(c) 2006-2007, Ext JS, LLC.
45056  *
45057  * Originally Released Under LGPL - original licence link has changed is not relivant.
45058  *
45059  * Fork - LGPL
45060  * <script type="text/javascript">
45061  */
45062  
45063 /**
45064  * @class Roo.form.Radio
45065  * @extends Roo.form.Checkbox
45066  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
45067  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
45068  * @constructor
45069  * Creates a new Radio
45070  * @param {Object} config Configuration options
45071  */
45072 Roo.form.Radio = function(){
45073     Roo.form.Radio.superclass.constructor.apply(this, arguments);
45074 };
45075 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
45076     inputType: 'radio',
45077
45078     /**
45079      * If this radio is part of a group, it will return the selected value
45080      * @return {String}
45081      */
45082     getGroupValue : function(){
45083         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
45084     },
45085     
45086     
45087     onRender : function(ct, position){
45088         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45089         
45090         if(this.inputValue !== undefined){
45091             this.el.dom.value = this.inputValue;
45092         }
45093          
45094         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
45095         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
45096         //var viewEl = this.wrap.createChild({ 
45097         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
45098         //this.viewEl = viewEl;   
45099         //this.wrap.on('click', this.onClick,  this); 
45100         
45101         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45102         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
45103         
45104         
45105         
45106         if(this.boxLabel){
45107             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
45108         //    viewEl.on('click', this.onClick,  this); 
45109         }
45110          if(this.checked){
45111             this.el.dom.checked =   'checked' ;
45112         }
45113          
45114     } 
45115     
45116     
45117 });Roo.rtf = {}; // namespace
45118 Roo.rtf.Hex = function(hex)
45119 {
45120     this.hexstr = hex;
45121 };
45122 Roo.rtf.Paragraph = function(opts)
45123 {
45124     this.content = []; ///??? is that used?
45125 };Roo.rtf.Span = function(opts)
45126 {
45127     this.value = opts.value;
45128 };
45129
45130 Roo.rtf.Group = function(parent)
45131 {
45132     // we dont want to acutally store parent - it will make debug a nightmare..
45133     this.content = [];
45134     this.cn  = [];
45135      
45136        
45137     
45138 };
45139
45140 Roo.rtf.Group.prototype = {
45141     ignorable : false,
45142     content: false,
45143     cn: false,
45144     addContent : function(node) {
45145         // could set styles...
45146         this.content.push(node);
45147     },
45148     addChild : function(cn)
45149     {
45150         this.cn.push(cn);
45151     },
45152     // only for images really...
45153     toDataURL : function()
45154     {
45155         var mimetype = false;
45156         switch(true) {
45157             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
45158                 mimetype = "image/png";
45159                 break;
45160              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
45161                 mimetype = "image/jpeg";
45162                 break;
45163             default :
45164                 return 'about:blank'; // ?? error?
45165         }
45166         
45167         
45168         var hexstring = this.content[this.content.length-1].value;
45169         
45170         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
45171             return String.fromCharCode(parseInt(a, 16));
45172         }).join(""));
45173     }
45174     
45175 };
45176 // this looks like it's normally the {rtf{ .... }}
45177 Roo.rtf.Document = function()
45178 {
45179     // we dont want to acutally store parent - it will make debug a nightmare..
45180     this.rtlch  = [];
45181     this.content = [];
45182     this.cn = [];
45183     
45184 };
45185 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
45186     addChild : function(cn)
45187     {
45188         this.cn.push(cn);
45189         switch(cn.type) {
45190             case 'rtlch': // most content seems to be inside this??
45191             case 'listtext':
45192             case 'shpinst':
45193                 this.rtlch.push(cn);
45194                 return;
45195             default:
45196                 this[cn.type] = cn;
45197         }
45198         
45199     },
45200     
45201     getElementsByType : function(type)
45202     {
45203         var ret =  [];
45204         this._getElementsByType(type, ret, this.cn, 'rtf');
45205         return ret;
45206     },
45207     _getElementsByType : function (type, ret, search_array, path)
45208     {
45209         search_array.forEach(function(n,i) {
45210             if (n.type == type) {
45211                 n.path = path + '/' + n.type + ':' + i;
45212                 ret.push(n);
45213             }
45214             if (n.cn.length > 0) {
45215                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
45216             }
45217         },this);
45218     }
45219     
45220 });
45221  
45222 Roo.rtf.Ctrl = function(opts)
45223 {
45224     this.value = opts.value;
45225     this.param = opts.param;
45226 };
45227 /**
45228  *
45229  *
45230  * based on this https://github.com/iarna/rtf-parser
45231  * it's really only designed to extract pict from pasted RTF 
45232  *
45233  * usage:
45234  *
45235  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45236  *  
45237  *
45238  */
45239
45240  
45241
45242
45243
45244 Roo.rtf.Parser = function(text) {
45245     //super({objectMode: true})
45246     this.text = '';
45247     this.parserState = this.parseText;
45248     
45249     // these are for interpeter...
45250     this.doc = {};
45251     ///this.parserState = this.parseTop
45252     this.groupStack = [];
45253     this.hexStore = [];
45254     this.doc = false;
45255     
45256     this.groups = []; // where we put the return.
45257     
45258     for (var ii = 0; ii < text.length; ++ii) {
45259         ++this.cpos;
45260         
45261         if (text[ii] === '\n') {
45262             ++this.row;
45263             this.col = 1;
45264         } else {
45265             ++this.col;
45266         }
45267         this.parserState(text[ii]);
45268     }
45269     
45270     
45271     
45272 };
45273 Roo.rtf.Parser.prototype = {
45274     text : '', // string being parsed..
45275     controlWord : '',
45276     controlWordParam :  '',
45277     hexChar : '',
45278     doc : false,
45279     group: false,
45280     groupStack : false,
45281     hexStore : false,
45282     
45283     
45284     cpos : 0, 
45285     row : 1, // reportin?
45286     col : 1, //
45287
45288      
45289     push : function (el)
45290     {
45291         var m = 'cmd'+ el.type;
45292         if (typeof(this[m]) == 'undefined') {
45293             Roo.log('invalid cmd:' + el.type);
45294             return;
45295         }
45296         this[m](el);
45297         //Roo.log(el);
45298     },
45299     flushHexStore : function()
45300     {
45301         if (this.hexStore.length < 1) {
45302             return;
45303         }
45304         var hexstr = this.hexStore.map(
45305             function(cmd) {
45306                 return cmd.value;
45307         }).join('');
45308         
45309         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45310               
45311             
45312         this.hexStore.splice(0)
45313         
45314     },
45315     
45316     cmdgroupstart : function()
45317     {
45318         this.flushHexStore();
45319         if (this.group) {
45320             this.groupStack.push(this.group);
45321         }
45322          // parent..
45323         if (this.doc === false) {
45324             this.group = this.doc = new Roo.rtf.Document();
45325             return;
45326             
45327         }
45328         this.group = new Roo.rtf.Group(this.group);
45329     },
45330     cmdignorable : function()
45331     {
45332         this.flushHexStore();
45333         this.group.ignorable = true;
45334     },
45335     cmdendparagraph : function()
45336     {
45337         this.flushHexStore();
45338         this.group.addContent(new Roo.rtf.Paragraph());
45339     },
45340     cmdgroupend : function ()
45341     {
45342         this.flushHexStore();
45343         var endingGroup = this.group;
45344         
45345         
45346         this.group = this.groupStack.pop();
45347         if (this.group) {
45348             this.group.addChild(endingGroup);
45349         }
45350         
45351         
45352         
45353         var doc = this.group || this.doc;
45354         //if (endingGroup instanceof FontTable) {
45355         //  doc.fonts = endingGroup.table
45356         //} else if (endingGroup instanceof ColorTable) {
45357         //  doc.colors = endingGroup.table
45358         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45359         if (endingGroup.ignorable === false) {
45360             //code
45361             this.groups.push(endingGroup);
45362            // Roo.log( endingGroup );
45363         }
45364             //Roo.each(endingGroup.content, function(item)) {
45365             //    doc.addContent(item);
45366             //}
45367             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45368         //}
45369     },
45370     cmdtext : function (cmd)
45371     {
45372         this.flushHexStore();
45373         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45374             //this.group = this.doc
45375             return;  // we really don't care about stray text...
45376         }
45377         this.group.addContent(new Roo.rtf.Span(cmd));
45378     },
45379     cmdcontrolword : function (cmd)
45380     {
45381         this.flushHexStore();
45382         if (!this.group.type) {
45383             this.group.type = cmd.value;
45384             return;
45385         }
45386         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45387         // we actually don't care about ctrl words...
45388         return ;
45389         /*
45390         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45391         if (this[method]) {
45392             this[method](cmd.param)
45393         } else {
45394             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45395         }
45396         */
45397     },
45398     cmdhexchar : function(cmd) {
45399         this.hexStore.push(cmd);
45400     },
45401     cmderror : function(cmd) {
45402         throw cmd.value;
45403     },
45404     
45405     /*
45406       _flush (done) {
45407         if (this.text !== '\u0000') this.emitText()
45408         done()
45409       }
45410       */
45411       
45412       
45413     parseText : function(c)
45414     {
45415         if (c === '\\') {
45416             this.parserState = this.parseEscapes;
45417         } else if (c === '{') {
45418             this.emitStartGroup();
45419         } else if (c === '}') {
45420             this.emitEndGroup();
45421         } else if (c === '\x0A' || c === '\x0D') {
45422             // cr/lf are noise chars
45423         } else {
45424             this.text += c;
45425         }
45426     },
45427     
45428     parseEscapes: function (c)
45429     {
45430         if (c === '\\' || c === '{' || c === '}') {
45431             this.text += c;
45432             this.parserState = this.parseText;
45433         } else {
45434             this.parserState = this.parseControlSymbol;
45435             this.parseControlSymbol(c);
45436         }
45437     },
45438     parseControlSymbol: function(c)
45439     {
45440         if (c === '~') {
45441             this.text += '\u00a0'; // nbsp
45442             this.parserState = this.parseText
45443         } else if (c === '-') {
45444              this.text += '\u00ad'; // soft hyphen
45445         } else if (c === '_') {
45446             this.text += '\u2011'; // non-breaking hyphen
45447         } else if (c === '*') {
45448             this.emitIgnorable();
45449             this.parserState = this.parseText;
45450         } else if (c === "'") {
45451             this.parserState = this.parseHexChar;
45452         } else if (c === '|') { // formula cacter
45453             this.emitFormula();
45454             this.parserState = this.parseText;
45455         } else if (c === ':') { // subentry in an index entry
45456             this.emitIndexSubEntry();
45457             this.parserState = this.parseText;
45458         } else if (c === '\x0a') {
45459             this.emitEndParagraph();
45460             this.parserState = this.parseText;
45461         } else if (c === '\x0d') {
45462             this.emitEndParagraph();
45463             this.parserState = this.parseText;
45464         } else {
45465             this.parserState = this.parseControlWord;
45466             this.parseControlWord(c);
45467         }
45468     },
45469     parseHexChar: function (c)
45470     {
45471         if (/^[A-Fa-f0-9]$/.test(c)) {
45472             this.hexChar += c;
45473             if (this.hexChar.length >= 2) {
45474               this.emitHexChar();
45475               this.parserState = this.parseText;
45476             }
45477             return;
45478         }
45479         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45480         this.parserState = this.parseText;
45481         
45482     },
45483     parseControlWord : function(c)
45484     {
45485         if (c === ' ') {
45486             this.emitControlWord();
45487             this.parserState = this.parseText;
45488         } else if (/^[-\d]$/.test(c)) {
45489             this.parserState = this.parseControlWordParam;
45490             this.controlWordParam += c;
45491         } else if (/^[A-Za-z]$/.test(c)) {
45492           this.controlWord += c;
45493         } else {
45494           this.emitControlWord();
45495           this.parserState = this.parseText;
45496           this.parseText(c);
45497         }
45498     },
45499     parseControlWordParam : function (c) {
45500         if (/^\d$/.test(c)) {
45501           this.controlWordParam += c;
45502         } else if (c === ' ') {
45503           this.emitControlWord();
45504           this.parserState = this.parseText;
45505         } else {
45506           this.emitControlWord();
45507           this.parserState = this.parseText;
45508           this.parseText(c);
45509         }
45510     },
45511     
45512     
45513     
45514     
45515     emitText : function () {
45516         if (this.text === '') {
45517             return;
45518         }
45519         this.push({
45520             type: 'text',
45521             value: this.text,
45522             pos: this.cpos,
45523             row: this.row,
45524             col: this.col
45525         });
45526         this.text = ''
45527     },
45528     emitControlWord : function ()
45529     {
45530         this.emitText();
45531         if (this.controlWord === '') {
45532             // do we want to track this - it seems just to cause problems.
45533             //this.emitError('empty control word');
45534         } else {
45535             this.push({
45536                   type: 'controlword',
45537                   value: this.controlWord,
45538                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45539                   pos: this.cpos,
45540                   row: this.row,
45541                   col: this.col
45542             });
45543         }
45544         this.controlWord = '';
45545         this.controlWordParam = '';
45546     },
45547     emitStartGroup : function ()
45548     {
45549         this.emitText();
45550         this.push({
45551             type: 'groupstart',
45552             pos: this.cpos,
45553             row: this.row,
45554             col: this.col
45555         });
45556     },
45557     emitEndGroup : function ()
45558     {
45559         this.emitText();
45560         this.push({
45561             type: 'groupend',
45562             pos: this.cpos,
45563             row: this.row,
45564             col: this.col
45565         });
45566     },
45567     emitIgnorable : function ()
45568     {
45569         this.emitText();
45570         this.push({
45571             type: 'ignorable',
45572             pos: this.cpos,
45573             row: this.row,
45574             col: this.col
45575         });
45576     },
45577     emitHexChar : function ()
45578     {
45579         this.emitText();
45580         this.push({
45581             type: 'hexchar',
45582             value: this.hexChar,
45583             pos: this.cpos,
45584             row: this.row,
45585             col: this.col
45586         });
45587         this.hexChar = ''
45588     },
45589     emitError : function (message)
45590     {
45591       this.emitText();
45592       this.push({
45593             type: 'error',
45594             value: message,
45595             row: this.row,
45596             col: this.col,
45597             char: this.cpos //,
45598             //stack: new Error().stack
45599         });
45600     },
45601     emitEndParagraph : function () {
45602         this.emitText();
45603         this.push({
45604             type: 'endparagraph',
45605             pos: this.cpos,
45606             row: this.row,
45607             col: this.col
45608         });
45609     }
45610      
45611 } ;
45612 Roo.htmleditor = {};
45613  
45614 /**
45615  * @class Roo.htmleditor.Filter
45616  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45617  * @cfg {DomElement} node The node to iterate and filter
45618  * @cfg {boolean|String|Array} tag Tags to replace 
45619  * @constructor
45620  * Create a new Filter.
45621  * @param {Object} config Configuration options
45622  */
45623
45624
45625
45626 Roo.htmleditor.Filter = function(cfg) {
45627     Roo.apply(this.cfg);
45628     // this does not actually call walk as it's really just a abstract class
45629 }
45630
45631
45632 Roo.htmleditor.Filter.prototype = {
45633     
45634     node: false,
45635     
45636     tag: false,
45637
45638     // overrride to do replace comments.
45639     replaceComment : false,
45640     
45641     // overrride to do replace or do stuff with tags..
45642     replaceTag : false,
45643     
45644     walk : function(dom)
45645     {
45646         Roo.each( Array.from(dom.childNodes), function( e ) {
45647             switch(true) {
45648                 
45649                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
45650                     this.replaceComment(e);
45651                     return;
45652                 
45653                 case e.nodeType != 1: //not a node.
45654                     return;
45655                 
45656                 case this.tag === true: // everything
45657                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45658                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45659                     if (this.replaceTag && false === this.replaceTag(e)) {
45660                         return;
45661                     }
45662                     if (e.hasChildNodes()) {
45663                         this.walk(e);
45664                     }
45665                     return;
45666                 
45667                 default:    // tags .. that do not match.
45668                     if (e.hasChildNodes()) {
45669                         this.walk(e);
45670                     }
45671             }
45672             
45673         }, this);
45674         
45675     }
45676 }; 
45677
45678 /**
45679  * @class Roo.htmleditor.FilterAttributes
45680  * clean attributes and  styles including http:// etc.. in attribute
45681  * @constructor
45682 * Run a new Attribute Filter
45683 * @param {Object} config Configuration options
45684  */
45685 Roo.htmleditor.FilterAttributes = function(cfg)
45686 {
45687     Roo.apply(this, cfg);
45688     this.attrib_black = this.attrib_black || [];
45689     this.attrib_white = this.attrib_white || [];
45690
45691     this.attrib_clean = this.attrib_clean || [];
45692     this.style_white = this.style_white || [];
45693     this.style_black = this.style_black || [];
45694     this.walk(cfg.node);
45695 }
45696
45697 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45698 {
45699     tag: true, // all tags
45700     
45701     attrib_black : false, // array
45702     attrib_clean : false,
45703     attrib_white : false,
45704
45705     style_white : false,
45706     style_black : false,
45707      
45708      
45709     replaceTag : function(node)
45710     {
45711         if (!node.attributes || !node.attributes.length) {
45712             return true;
45713         }
45714         
45715         for (var i = node.attributes.length-1; i > -1 ; i--) {
45716             var a = node.attributes[i];
45717             //console.log(a);
45718             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45719                 node.removeAttribute(a.name);
45720                 continue;
45721             }
45722             
45723             
45724             
45725             if (a.name.toLowerCase().substr(0,2)=='on')  {
45726                 node.removeAttribute(a.name);
45727                 continue;
45728             }
45729             
45730             
45731             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45732                 node.removeAttribute(a.name);
45733                 continue;
45734             }
45735             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45736                 this.cleanAttr(node,a.name,a.value); // fixme..
45737                 continue;
45738             }
45739             if (a.name == 'style') {
45740                 this.cleanStyle(node,a.name,a.value);
45741                 continue;
45742             }
45743             /// clean up MS crap..
45744             // tecnically this should be a list of valid class'es..
45745             
45746             
45747             if (a.name == 'class') {
45748                 if (a.value.match(/^Mso/)) {
45749                     node.removeAttribute('class');
45750                 }
45751                 
45752                 if (a.value.match(/^body$/)) {
45753                     node.removeAttribute('class');
45754                 }
45755                 continue;
45756             }
45757             
45758             
45759             // style cleanup!?
45760             // class cleanup?
45761             
45762         }
45763         return true; // clean children
45764     },
45765         
45766     cleanAttr: function(node, n,v)
45767     {
45768         
45769         if (v.match(/^\./) || v.match(/^\//)) {
45770             return;
45771         }
45772         if (v.match(/^(http|https):\/\//)
45773             || v.match(/^mailto:/) 
45774             || v.match(/^ftp:/)
45775             || v.match(/^data:/)
45776             ) {
45777             return;
45778         }
45779         if (v.match(/^#/)) {
45780             return;
45781         }
45782         if (v.match(/^\{/)) { // allow template editing.
45783             return;
45784         }
45785 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45786         node.removeAttribute(n);
45787         
45788     },
45789     cleanStyle : function(node,  n,v)
45790     {
45791         if (v.match(/expression/)) { //XSS?? should we even bother..
45792             node.removeAttribute(n);
45793             return;
45794         }
45795         
45796         var parts = v.split(/;/);
45797         var clean = [];
45798         
45799         Roo.each(parts, function(p) {
45800             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45801             if (!p.length) {
45802                 return true;
45803             }
45804             var l = p.split(':').shift().replace(/\s+/g,'');
45805             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45806             
45807             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45808                 return true;
45809             }
45810             //Roo.log()
45811             // only allow 'c whitelisted system attributes'
45812             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45813                 return true;
45814             }
45815             
45816             
45817             clean.push(p);
45818             return true;
45819         },this);
45820         if (clean.length) { 
45821             node.setAttribute(n, clean.join(';'));
45822         } else {
45823             node.removeAttribute(n);
45824         }
45825         
45826     }
45827         
45828         
45829         
45830     
45831 });/**
45832  * @class Roo.htmleditor.FilterBlack
45833  * remove blacklisted elements.
45834  * @constructor
45835  * Run a new Blacklisted Filter
45836  * @param {Object} config Configuration options
45837  */
45838
45839 Roo.htmleditor.FilterBlack = function(cfg)
45840 {
45841     Roo.apply(this, cfg);
45842     this.walk(cfg.node);
45843 }
45844
45845 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45846 {
45847     tag : true, // all elements.
45848    
45849     replaceTag : function(n)
45850     {
45851         n.parentNode.removeChild(n);
45852     }
45853 });
45854 /**
45855  * @class Roo.htmleditor.FilterComment
45856  * remove comments.
45857  * @constructor
45858 * Run a new Comments Filter
45859 * @param {Object} config Configuration options
45860  */
45861 Roo.htmleditor.FilterComment = function(cfg)
45862 {
45863     this.walk(cfg.node);
45864 }
45865
45866 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45867 {
45868   
45869     replaceComment : function(n)
45870     {
45871         n.parentNode.removeChild(n);
45872     }
45873 });/**
45874  * @class Roo.htmleditor.FilterKeepChildren
45875  * remove tags but keep children
45876  * @constructor
45877  * Run a new Keep Children Filter
45878  * @param {Object} config Configuration options
45879  */
45880
45881 Roo.htmleditor.FilterKeepChildren = function(cfg)
45882 {
45883     Roo.apply(this, cfg);
45884     if (this.tag === false) {
45885         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45886     }
45887     this.walk(cfg.node);
45888 }
45889
45890 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45891 {
45892     
45893   
45894     replaceTag : function(node)
45895     {
45896         // walk children...
45897         //Roo.log(node);
45898         var ar = Array.from(node.childNodes);
45899         //remove first..
45900         for (var i = 0; i < ar.length; i++) {
45901             if (ar[i].nodeType == 1) {
45902                 if (
45903                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45904                     || // array and it matches
45905                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45906                 ) {
45907                     this.replaceTag(ar[i]); // child is blacklisted as well...
45908                     continue;
45909                 }
45910             }
45911         }  
45912         ar = Array.from(node.childNodes);
45913         for (var i = 0; i < ar.length; i++) {
45914          
45915             node.removeChild(ar[i]);
45916             // what if we need to walk these???
45917             node.parentNode.insertBefore(ar[i], node);
45918             if (this.tag !== false) {
45919                 this.walk(ar[i]);
45920                 
45921             }
45922         }
45923         node.parentNode.removeChild(node);
45924         return false; // don't walk children
45925         
45926         
45927     }
45928 });/**
45929  * @class Roo.htmleditor.FilterParagraph
45930  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45931  * like on 'push' to remove the <p> tags and replace them with line breaks.
45932  * @constructor
45933  * Run a new Paragraph Filter
45934  * @param {Object} config Configuration options
45935  */
45936
45937 Roo.htmleditor.FilterParagraph = function(cfg)
45938 {
45939     // no need to apply config.
45940     this.walk(cfg.node);
45941 }
45942
45943 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45944 {
45945     
45946      
45947     tag : 'P',
45948     
45949      
45950     replaceTag : function(node)
45951     {
45952         
45953         if (node.childNodes.length == 1 &&
45954             node.childNodes[0].nodeType == 3 &&
45955             node.childNodes[0].textContent.trim().length < 1
45956             ) {
45957             // remove and replace with '<BR>';
45958             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45959             return false; // no need to walk..
45960         }
45961         var ar = Array.from(node.childNodes);
45962         for (var i = 0; i < ar.length; i++) {
45963             node.removeChild(ar[i]);
45964             // what if we need to walk these???
45965             node.parentNode.insertBefore(ar[i], node);
45966         }
45967         // now what about this?
45968         // <p> &nbsp; </p>
45969         
45970         // double BR.
45971         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45972         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45973         node.parentNode.removeChild(node);
45974         
45975         return false;
45976
45977     }
45978     
45979 });/**
45980  * @class Roo.htmleditor.FilterSpan
45981  * filter span's with no attributes out..
45982  * @constructor
45983  * Run a new Span Filter
45984  * @param {Object} config Configuration options
45985  */
45986
45987 Roo.htmleditor.FilterSpan = function(cfg)
45988 {
45989     // no need to apply config.
45990     this.walk(cfg.node);
45991 }
45992
45993 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45994 {
45995      
45996     tag : 'SPAN',
45997      
45998  
45999     replaceTag : function(node)
46000     {
46001         if (node.attributes && node.attributes.length > 0) {
46002             return true; // walk if there are any.
46003         }
46004         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
46005         return false;
46006      
46007     }
46008     
46009 });/**
46010  * @class Roo.htmleditor.FilterTableWidth
46011   try and remove table width data - as that frequently messes up other stuff.
46012  * 
46013  *      was cleanTableWidths.
46014  *
46015  * Quite often pasting from word etc.. results in tables with column and widths.
46016  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
46017  *
46018  * @constructor
46019  * Run a new Table Filter
46020  * @param {Object} config Configuration options
46021  */
46022
46023 Roo.htmleditor.FilterTableWidth = function(cfg)
46024 {
46025     // no need to apply config.
46026     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
46027     this.walk(cfg.node);
46028 }
46029
46030 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
46031 {
46032      
46033      
46034     
46035     replaceTag: function(node) {
46036         
46037         
46038       
46039         if (node.hasAttribute('width')) {
46040             node.removeAttribute('width');
46041         }
46042         
46043          
46044         if (node.hasAttribute("style")) {
46045             // pretty basic...
46046             
46047             var styles = node.getAttribute("style").split(";");
46048             var nstyle = [];
46049             Roo.each(styles, function(s) {
46050                 if (!s.match(/:/)) {
46051                     return;
46052                 }
46053                 var kv = s.split(":");
46054                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
46055                     return;
46056                 }
46057                 // what ever is left... we allow.
46058                 nstyle.push(s);
46059             });
46060             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46061             if (!nstyle.length) {
46062                 node.removeAttribute('style');
46063             }
46064         }
46065         
46066         return true; // continue doing children..
46067     }
46068 });/**
46069  * @class Roo.htmleditor.FilterWord
46070  * try and clean up all the mess that Word generates.
46071  * 
46072  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
46073  
46074  * @constructor
46075  * Run a new Span Filter
46076  * @param {Object} config Configuration options
46077  */
46078
46079 Roo.htmleditor.FilterWord = function(cfg)
46080 {
46081     // no need to apply config.
46082     this.replaceDocBullets(cfg.node);
46083     
46084    // this.walk(cfg.node);
46085     
46086     
46087 }
46088
46089 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
46090 {
46091     tag: true,
46092      
46093     
46094     /**
46095      * Clean up MS wordisms...
46096      */
46097     replaceTag : function(node)
46098     {
46099          
46100         // no idea what this does - span with text, replaceds with just text.
46101         if(
46102                 node.nodeName == 'SPAN' &&
46103                 !node.hasAttributes() &&
46104                 node.childNodes.length == 1 &&
46105                 node.firstChild.nodeName == "#text"  
46106         ) {
46107             var textNode = node.firstChild;
46108             node.removeChild(textNode);
46109             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46110                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
46111             }
46112             node.parentNode.insertBefore(textNode, node);
46113             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46114                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
46115             }
46116             
46117             node.parentNode.removeChild(node);
46118             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
46119         }
46120         
46121    
46122         
46123         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
46124             node.parentNode.removeChild(node);
46125             return false; // dont do chidlren
46126         }
46127         //Roo.log(node.tagName);
46128         // remove - but keep children..
46129         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
46130             //Roo.log('-- removed');
46131             while (node.childNodes.length) {
46132                 var cn = node.childNodes[0];
46133                 node.removeChild(cn);
46134                 node.parentNode.insertBefore(cn, node);
46135                 // move node to parent - and clean it..
46136                 if (cn.nodeType == 1) {
46137                     this.replaceTag(cn);
46138                 }
46139                 
46140             }
46141             node.parentNode.removeChild(node);
46142             /// no need to iterate chidlren = it's got none..
46143             //this.iterateChildren(node, this.cleanWord);
46144             return false; // no need to iterate children.
46145         }
46146         // clean styles
46147         if (node.className.length) {
46148             
46149             var cn = node.className.split(/\W+/);
46150             var cna = [];
46151             Roo.each(cn, function(cls) {
46152                 if (cls.match(/Mso[a-zA-Z]+/)) {
46153                     return;
46154                 }
46155                 cna.push(cls);
46156             });
46157             node.className = cna.length ? cna.join(' ') : '';
46158             if (!cna.length) {
46159                 node.removeAttribute("class");
46160             }
46161         }
46162         
46163         if (node.hasAttribute("lang")) {
46164             node.removeAttribute("lang");
46165         }
46166         
46167         if (node.hasAttribute("style")) {
46168             
46169             var styles = node.getAttribute("style").split(";");
46170             var nstyle = [];
46171             Roo.each(styles, function(s) {
46172                 if (!s.match(/:/)) {
46173                     return;
46174                 }
46175                 var kv = s.split(":");
46176                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
46177                     return;
46178                 }
46179                 // what ever is left... we allow.
46180                 nstyle.push(s);
46181             });
46182             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46183             if (!nstyle.length) {
46184                 node.removeAttribute('style');
46185             }
46186         }
46187         return true; // do children
46188         
46189         
46190         
46191     },
46192     
46193     styleToObject: function(node)
46194     {
46195         var styles = (node.getAttribute("style") || '').split(";");
46196         var ret = {};
46197         Roo.each(styles, function(s) {
46198             if (!s.match(/:/)) {
46199                 return;
46200             }
46201             var kv = s.split(":");
46202              
46203             // what ever is left... we allow.
46204             ret[kv[0].trim()] = kv[1];
46205         });
46206         return ret;
46207     },
46208     
46209     
46210     replaceDocBullets : function(doc)
46211     {
46212         // this is a bit odd - but it appears some indents use ql-indent-1
46213         
46214         var listpara = doc.getElementsByClassName('ql-indent-1');
46215         while(listpara.length) {
46216             this.replaceDocBullet(listpara.item(0));
46217         }
46218         
46219         var listpara = doc.getElementsByClassName('MsoListParagraph');
46220         while(listpara.length) {
46221             this.replaceDocBullet(listpara.item(0));
46222         }
46223     },
46224     
46225     replaceDocBullet : function(p)
46226     {
46227         // gather all the siblings.
46228         var ns = p,
46229             parent = p.parentNode,
46230             doc = parent.ownerDocument,
46231             items = [];
46232             
46233             
46234         while (ns) {
46235             if (ns.nodeType != 1) {
46236                 ns = ns.nextSibling;
46237                 continue;
46238             }
46239             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
46240                 break;
46241             }
46242             items.push(ns);
46243             ns = ns.nextSibling;
46244         }
46245         
46246         
46247         var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
46248         parent.insertBefore(ul, p);
46249         var lvl = 0;
46250         var stack = [ ul ];
46251         var last_li = false;
46252         
46253         items.forEach(function(n, ipos) {
46254             //Roo.log("got innertHMLT=" + n.innerHTML);
46255             
46256             var spans = n.getElementsByTagName('span');
46257             if (!spans.length) {
46258                 //Roo.log("No spans found");
46259
46260                 parent.removeChild(n);
46261                 return; // skip it...
46262             }
46263            
46264                 
46265             
46266             var style = {};
46267             for(var i = 0; i < spans.length; i++) {
46268             
46269                 style = this.styleToObject(spans[i]);
46270                 if (typeof(style['mso-list']) == 'undefined') {
46271                     continue;
46272                 }
46273                 
46274                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
46275                 break;
46276             }
46277             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
46278             style = this.styleToObject(n); // mo-list is from the parent node.
46279             if (typeof(style['mso-list']) == 'undefined') {
46280                 //Roo.log("parent is missing level");
46281                 parent.removeChild(n);
46282                 return;
46283             }
46284             
46285             var nlvl =   (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1  ;
46286             
46287             
46288             
46289             if (nlvl > lvl) {
46290                 //new indent
46291                 var nul = doc.createElement('ul'); // what about number lists...
46292                 last_li.appendChild(nul);
46293                 stack[nlvl] = nul;
46294                 
46295             }
46296             lvl = nlvl;
46297             
46298             var nli = stack[nlvl].appendChild(doc.createElement('li'));
46299             last_li = nli;
46300             nli.innerHTML = n.innerHTML;
46301             //Roo.log("innerHTML = " + n.innerHTML);
46302             parent.removeChild(n);
46303             
46304             // copy children of p into nli
46305             /*while(n.firstChild) {
46306                 var fc = n.firstChild;
46307                 n.removeChild(fc);
46308                 nli.appendChild(fc);
46309             }*/
46310              
46311             
46312         },this);
46313         
46314         
46315         
46316         
46317     }
46318     
46319     
46320     
46321 });
46322 /**
46323  * @class Roo.htmleditor.FilterStyleToTag
46324  * part of the word stuff... - certain 'styles' should be converted to tags.
46325  * eg.
46326  *   font-weight: bold -> bold
46327  *   ?? super / subscrit etc..
46328  * 
46329  * @constructor
46330 * Run a new style to tag filter.
46331 * @param {Object} config Configuration options
46332  */
46333 Roo.htmleditor.FilterStyleToTag = function(cfg)
46334 {
46335     
46336     this.tags = {
46337         B  : [ 'fontWeight' , 'bold'],
46338         I :  [ 'fontStyle' , 'italic'],
46339         //pre :  [ 'font-style' , 'italic'],
46340         // h1.. h6 ?? font-size?
46341         SUP : [ 'verticalAlign' , 'super' ],
46342         SUB : [ 'verticalAlign' , 'sub' ]
46343         
46344         
46345     };
46346     
46347     Roo.apply(this, cfg);
46348      
46349     
46350     this.walk(cfg.node);
46351     
46352     
46353     
46354 }
46355
46356
46357 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46358 {
46359     tag: true, // all tags
46360     
46361     tags : false,
46362     
46363     
46364     replaceTag : function(node)
46365     {
46366         
46367         
46368         if (node.getAttribute("style") === null) {
46369             return true;
46370         }
46371         var inject = [];
46372         for (var k in this.tags) {
46373             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46374                 inject.push(k);
46375                 node.style.removeProperty(this.tags[k][0]);
46376             }
46377         }
46378         if (!inject.length) {
46379             return true; 
46380         }
46381         var cn = Array.from(node.childNodes);
46382         var nn = node;
46383         Roo.each(inject, function(t) {
46384             var nc = node.ownerDocument.createElement(t);
46385             nn.appendChild(nc);
46386             nn = nc;
46387         });
46388         for(var i = 0;i < cn.length;cn++) {
46389             node.removeChild(cn[i]);
46390             nn.appendChild(cn[i]);
46391         }
46392         return true /// iterate thru
46393     }
46394     
46395 })/**
46396  * @class Roo.htmleditor.FilterLongBr
46397  * BR/BR/BR - keep a maximum of 2...
46398  * @constructor
46399  * Run a new Long BR Filter
46400  * @param {Object} config Configuration options
46401  */
46402
46403 Roo.htmleditor.FilterLongBr = function(cfg)
46404 {
46405     // no need to apply config.
46406     this.walk(cfg.node);
46407 }
46408
46409 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46410 {
46411     
46412      
46413     tag : 'BR',
46414     
46415      
46416     replaceTag : function(node)
46417     {
46418         
46419         var ps = node.nextSibling;
46420         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46421             ps = ps.nextSibling;
46422         }
46423         
46424         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46425             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46426             return false;
46427         }
46428         
46429         if (!ps || ps.nodeType != 1) {
46430             return false;
46431         }
46432         
46433         if (!ps || ps.tagName != 'BR') {
46434            
46435             return false;
46436         }
46437         
46438         
46439         
46440         
46441         
46442         if (!node.previousSibling) {
46443             return false;
46444         }
46445         var ps = node.previousSibling;
46446         
46447         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46448             ps = ps.previousSibling;
46449         }
46450         if (!ps || ps.nodeType != 1) {
46451             return false;
46452         }
46453         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46454         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46455             return false;
46456         }
46457         
46458         node.parentNode.removeChild(node); // remove me...
46459         
46460         return false; // no need to do children
46461
46462     }
46463     
46464 }); 
46465
46466 /**
46467  * @class Roo.htmleditor.FilterBlock
46468  * removes id / data-block and contenteditable that are associated with blocks
46469  * usage should be done on a cloned copy of the dom
46470  * @constructor
46471 * Run a new Attribute Filter { node : xxxx }}
46472 * @param {Object} config Configuration options
46473  */
46474 Roo.htmleditor.FilterBlock = function(cfg)
46475 {
46476     Roo.apply(this, cfg);
46477     var qa = cfg.node.querySelectorAll;
46478     this.removeAttributes('data-block');
46479     this.removeAttributes('contenteditable');
46480     this.removeAttributes('id');
46481     
46482 }
46483
46484 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
46485 {
46486     node: true, // all tags
46487      
46488      
46489     removeAttributes : function(attr)
46490     {
46491         var ar = this.node.querySelectorAll('*[' + attr + ']');
46492         for (var i =0;i<ar.length;i++) {
46493             ar[i].removeAttribute(attr);
46494         }
46495     }
46496         
46497         
46498         
46499     
46500 });
46501 /***
46502  * This is based loosely on tinymce 
46503  * @class Roo.htmleditor.TidySerializer
46504  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46505  * @constructor
46506  * @method Serializer
46507  * @param {Object} settings Name/value settings object.
46508  */
46509
46510
46511 Roo.htmleditor.TidySerializer = function(settings)
46512 {
46513     Roo.apply(this, settings);
46514     
46515     this.writer = new Roo.htmleditor.TidyWriter(settings);
46516     
46517     
46518
46519 };
46520 Roo.htmleditor.TidySerializer.prototype = {
46521     
46522     /**
46523      * @param {boolean} inner do the inner of the node.
46524      */
46525     inner : false,
46526     
46527     writer : false,
46528     
46529     /**
46530     * Serializes the specified node into a string.
46531     *
46532     * @example
46533     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
46534     * @method serialize
46535     * @param {DomElement} node Node instance to serialize.
46536     * @return {String} String with HTML based on DOM tree.
46537     */
46538     serialize : function(node) {
46539         
46540         // = settings.validate;
46541         var writer = this.writer;
46542         var self  = this;
46543         this.handlers = {
46544             // #text
46545             3: function(node) {
46546                 
46547                 writer.text(node.nodeValue, node);
46548             },
46549             // #comment
46550             8: function(node) {
46551                 writer.comment(node.nodeValue);
46552             },
46553             // Processing instruction
46554             7: function(node) {
46555                 writer.pi(node.name, node.nodeValue);
46556             },
46557             // Doctype
46558             10: function(node) {
46559                 writer.doctype(node.nodeValue);
46560             },
46561             // CDATA
46562             4: function(node) {
46563                 writer.cdata(node.nodeValue);
46564             },
46565             // Document fragment
46566             11: function(node) {
46567                 node = node.firstChild;
46568                 if (!node) {
46569                     return;
46570                 }
46571                 while(node) {
46572                     self.walk(node);
46573                     node = node.nextSibling
46574                 }
46575             }
46576         };
46577         writer.reset();
46578         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
46579         return writer.getContent();
46580     },
46581
46582     walk: function(node)
46583     {
46584         var attrName, attrValue, sortedAttrs, i, l, elementRule,
46585             handler = this.handlers[node.nodeType];
46586             
46587         if (handler) {
46588             handler(node);
46589             return;
46590         }
46591     
46592         var name = node.nodeName;
46593         var isEmpty = node.childNodes.length < 1;
46594       
46595         var writer = this.writer;
46596         var attrs = node.attributes;
46597         // Sort attributes
46598         
46599         writer.start(node.nodeName, attrs, isEmpty, node);
46600         if (isEmpty) {
46601             return;
46602         }
46603         node = node.firstChild;
46604         if (!node) {
46605             writer.end(name);
46606             return;
46607         }
46608         while (node) {
46609             this.walk(node);
46610             node = node.nextSibling;
46611         }
46612         writer.end(name);
46613         
46614     
46615     }
46616     // Serialize element and treat all non elements as fragments
46617    
46618 }; 
46619
46620 /***
46621  * This is based loosely on tinymce 
46622  * @class Roo.htmleditor.TidyWriter
46623  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46624  *
46625  * Known issues?
46626  * - not tested much with 'PRE' formated elements.
46627  * 
46628  *
46629  *
46630  */
46631
46632 Roo.htmleditor.TidyWriter = function(settings)
46633 {
46634     
46635     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
46636     Roo.apply(this, settings);
46637     this.html = [];
46638     this.state = [];
46639      
46640     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
46641   
46642 }
46643 Roo.htmleditor.TidyWriter.prototype = {
46644
46645  
46646     state : false,
46647     
46648     indent :  '  ',
46649     
46650     // part of state...
46651     indentstr : '',
46652     in_pre: false,
46653     in_inline : false,
46654     last_inline : false,
46655     encode : false,
46656      
46657     
46658             /**
46659     * Writes the a start element such as <p id="a">.
46660     *
46661     * @method start
46662     * @param {String} name Name of the element.
46663     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
46664     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
46665     */
46666     start: function(name, attrs, empty, node)
46667     {
46668         var i, l, attr, value;
46669         
46670         // there are some situations where adding line break && indentation will not work. will not work.
46671         // <span / b / i ... formating?
46672         
46673         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46674         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
46675         
46676         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
46677         
46678         var add_lb = name == 'BR' ? false : in_inline;
46679         
46680         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
46681             i_inline = false;
46682         }
46683
46684         var indentstr =  this.indentstr;
46685         
46686         // e_inline = elements that can be inline, but still allow \n before and after?
46687         // only 'BR' ??? any others?
46688         
46689         // ADD LINE BEFORE tage
46690         if (!this.in_pre) {
46691             if (in_inline) {
46692                 //code
46693                 if (name == 'BR') {
46694                     this.addLine();
46695                 } else if (this.lastElementEndsWS()) {
46696                     this.addLine();
46697                 } else{
46698                     // otherwise - no new line. (and dont indent.)
46699                     indentstr = '';
46700                 }
46701                 
46702             } else {
46703                 this.addLine();
46704             }
46705         } else {
46706             indentstr = '';
46707         }
46708         
46709         this.html.push(indentstr + '<', name.toLowerCase());
46710         
46711         if (attrs) {
46712             for (i = 0, l = attrs.length; i < l; i++) {
46713                 attr = attrs[i];
46714                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
46715             }
46716         }
46717      
46718         if (empty) {
46719             if (is_short) {
46720                 this.html[this.html.length] = '/>';
46721             } else {
46722                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
46723             }
46724             var e_inline = name == 'BR' ? false : this.in_inline;
46725             
46726             if (!e_inline && !this.in_pre) {
46727                 this.addLine();
46728             }
46729             return;
46730         
46731         }
46732         // not empty..
46733         this.html[this.html.length] = '>';
46734         
46735         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
46736         /*
46737         if (!in_inline && !in_pre) {
46738             var cn = node.firstChild;
46739             while(cn) {
46740                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
46741                     in_inline = true
46742                     break;
46743                 }
46744                 cn = cn.nextSibling;
46745             }
46746              
46747         }
46748         */
46749         
46750         
46751         this.pushState({
46752             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
46753             in_pre : in_pre,
46754             in_inline :  in_inline
46755         });
46756         // add a line after if we are not in a
46757         
46758         if (!in_inline && !in_pre) {
46759             this.addLine();
46760         }
46761         
46762             
46763          
46764         
46765     },
46766     
46767     lastElementEndsWS : function()
46768     {
46769         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
46770         if (value === false) {
46771             return true;
46772         }
46773         return value.match(/\s+$/);
46774         
46775     },
46776     
46777     /**
46778      * Writes the a end element such as </p>.
46779      *
46780      * @method end
46781      * @param {String} name Name of the element.
46782      */
46783     end: function(name) {
46784         var value;
46785         this.popState();
46786         var indentstr = '';
46787         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46788         
46789         if (!this.in_pre && !in_inline) {
46790             this.addLine();
46791             indentstr  = this.indentstr;
46792         }
46793         this.html.push(indentstr + '</', name.toLowerCase(), '>');
46794         this.last_inline = in_inline;
46795         
46796         // pop the indent state..
46797     },
46798     /**
46799      * Writes a text node.
46800      *
46801      * In pre - we should not mess with the contents.
46802      * 
46803      *
46804      * @method text
46805      * @param {String} text String to write out.
46806      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
46807      */
46808     text: function(in_text, node)
46809     {
46810         // if not in whitespace critical
46811         if (in_text.length < 1) {
46812             return;
46813         }
46814         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
46815         
46816         if (this.in_pre) {
46817             this.html[this.html.length] =  text;
46818             return;   
46819         }
46820         
46821         if (this.in_inline) {
46822             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
46823             if (text != ' ') {
46824                 text = text.replace(/\s+/,' ');  // all white space to single white space
46825                 
46826                     
46827                 // if next tag is '<BR>', then we can trim right..
46828                 if (node.nextSibling &&
46829                     node.nextSibling.nodeType == 1 &&
46830                     node.nextSibling.nodeName == 'BR' )
46831                 {
46832                     text = text.replace(/\s+$/g,'');
46833                 }
46834                 // if previous tag was a BR, we can also trim..
46835                 if (node.previousSibling &&
46836                     node.previousSibling.nodeType == 1 &&
46837                     node.previousSibling.nodeName == 'BR' )
46838                 {
46839                     text = this.indentstr +  text.replace(/^\s+/g,'');
46840                 }
46841                 if (text.match(/\n/)) {
46842                     text = text.replace(
46843                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46844                     );
46845                     // remoeve the last whitespace / line break.
46846                     text = text.replace(/\n\s+$/,'');
46847                 }
46848                 // repace long lines
46849                 
46850             }
46851              
46852             this.html[this.html.length] =  text;
46853             return;   
46854         }
46855         // see if previous element was a inline element.
46856         var indentstr = this.indentstr;
46857    
46858         text = text.replace(/\s+/g," "); // all whitespace into single white space.
46859         
46860         // should trim left?
46861         if (node.previousSibling &&
46862             node.previousSibling.nodeType == 1 &&
46863             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
46864         {
46865             indentstr = '';
46866             
46867         } else {
46868             this.addLine();
46869             text = text.replace(/^\s+/,''); // trim left
46870           
46871         }
46872         // should trim right?
46873         if (node.nextSibling &&
46874             node.nextSibling.nodeType == 1 &&
46875             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
46876         {
46877           // noop
46878             
46879         }  else {
46880             text = text.replace(/\s+$/,''); // trim right
46881         }
46882          
46883               
46884         
46885         
46886         
46887         if (text.length < 1) {
46888             return;
46889         }
46890         if (!text.match(/\n/)) {
46891             this.html.push(indentstr + text);
46892             return;
46893         }
46894         
46895         text = this.indentstr + text.replace(
46896             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46897         );
46898         // remoeve the last whitespace / line break.
46899         text = text.replace(/\s+$/,''); 
46900         
46901         this.html.push(text);
46902         
46903         // split and indent..
46904         
46905         
46906     },
46907     /**
46908      * Writes a cdata node such as <![CDATA[data]]>.
46909      *
46910      * @method cdata
46911      * @param {String} text String to write out inside the cdata.
46912      */
46913     cdata: function(text) {
46914         this.html.push('<![CDATA[', text, ']]>');
46915     },
46916     /**
46917     * Writes a comment node such as <!-- Comment -->.
46918     *
46919     * @method cdata
46920     * @param {String} text String to write out inside the comment.
46921     */
46922    comment: function(text) {
46923        this.html.push('<!--', text, '-->');
46924    },
46925     /**
46926      * Writes a PI node such as <?xml attr="value" ?>.
46927      *
46928      * @method pi
46929      * @param {String} name Name of the pi.
46930      * @param {String} text String to write out inside the pi.
46931      */
46932     pi: function(name, text) {
46933         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
46934         this.indent != '' && this.html.push('\n');
46935     },
46936     /**
46937      * Writes a doctype node such as <!DOCTYPE data>.
46938      *
46939      * @method doctype
46940      * @param {String} text String to write out inside the doctype.
46941      */
46942     doctype: function(text) {
46943         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
46944     },
46945     /**
46946      * Resets the internal buffer if one wants to reuse the writer.
46947      *
46948      * @method reset
46949      */
46950     reset: function() {
46951         this.html.length = 0;
46952         this.state = [];
46953         this.pushState({
46954             indentstr : '',
46955             in_pre : false, 
46956             in_inline : false
46957         })
46958     },
46959     /**
46960      * Returns the contents that got serialized.
46961      *
46962      * @method getContent
46963      * @return {String} HTML contents that got written down.
46964      */
46965     getContent: function() {
46966         return this.html.join('').replace(/\n$/, '');
46967     },
46968     
46969     pushState : function(cfg)
46970     {
46971         this.state.push(cfg);
46972         Roo.apply(this, cfg);
46973     },
46974     
46975     popState : function()
46976     {
46977         if (this.state.length < 1) {
46978             return; // nothing to push
46979         }
46980         var cfg = {
46981             in_pre: false,
46982             indentstr : ''
46983         };
46984         this.state.pop();
46985         if (this.state.length > 0) {
46986             cfg = this.state[this.state.length-1]; 
46987         }
46988         Roo.apply(this, cfg);
46989     },
46990     
46991     addLine: function()
46992     {
46993         if (this.html.length < 1) {
46994             return;
46995         }
46996         
46997         
46998         var value = this.html[this.html.length - 1];
46999         if (value.length > 0 && '\n' !== value) {
47000             this.html.push('\n');
47001         }
47002     }
47003     
47004     
47005 //'pre script noscript style textarea video audio iframe object code'
47006 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
47007 // inline 
47008 };
47009
47010 Roo.htmleditor.TidyWriter.inline_elements = [
47011         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
47012         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
47013 ];
47014 Roo.htmleditor.TidyWriter.shortend_elements = [
47015     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
47016     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
47017 ];
47018
47019 Roo.htmleditor.TidyWriter.whitespace_elements = [
47020     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
47021 ];/***
47022  * This is based loosely on tinymce 
47023  * @class Roo.htmleditor.TidyEntities
47024  * @static
47025  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
47026  *
47027  * Not 100% sure this is actually used or needed.
47028  */
47029
47030 Roo.htmleditor.TidyEntities = {
47031     
47032     /**
47033      * initialize data..
47034      */
47035     init : function (){
47036      
47037         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
47038        
47039     },
47040
47041
47042     buildEntitiesLookup: function(items, radix) {
47043         var i, chr, entity, lookup = {};
47044         if (!items) {
47045             return {};
47046         }
47047         items = typeof(items) == 'string' ? items.split(',') : items;
47048         radix = radix || 10;
47049         // Build entities lookup table
47050         for (i = 0; i < items.length; i += 2) {
47051             chr = String.fromCharCode(parseInt(items[i], radix));
47052             // Only add non base entities
47053             if (!this.baseEntities[chr]) {
47054                 entity = '&' + items[i + 1] + ';';
47055                 lookup[chr] = entity;
47056                 lookup[entity] = chr;
47057             }
47058         }
47059         return lookup;
47060         
47061     },
47062     
47063     asciiMap : {
47064             128: '€',
47065             130: '‚',
47066             131: 'ƒ',
47067             132: '„',
47068             133: '…',
47069             134: '†',
47070             135: '‡',
47071             136: 'ˆ',
47072             137: '‰',
47073             138: 'Š',
47074             139: '‹',
47075             140: 'Œ',
47076             142: 'Ž',
47077             145: '‘',
47078             146: '’',
47079             147: '“',
47080             148: '”',
47081             149: '•',
47082             150: '–',
47083             151: '—',
47084             152: '˜',
47085             153: '™',
47086             154: 'š',
47087             155: '›',
47088             156: 'œ',
47089             158: 'ž',
47090             159: 'Ÿ'
47091     },
47092     // Raw entities
47093     baseEntities : {
47094         '"': '&quot;',
47095         // Needs to be escaped since the YUI compressor would otherwise break the code
47096         '\'': '&#39;',
47097         '<': '&lt;',
47098         '>': '&gt;',
47099         '&': '&amp;',
47100         '`': '&#96;'
47101     },
47102     // Reverse lookup table for raw entities
47103     reverseEntities : {
47104         '&lt;': '<',
47105         '&gt;': '>',
47106         '&amp;': '&',
47107         '&quot;': '"',
47108         '&apos;': '\''
47109     },
47110     
47111     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47112     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47113     rawCharsRegExp : /[<>&\"\']/g,
47114     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
47115     namedEntities  : false,
47116     namedEntitiesData : [ 
47117         '50',
47118         'nbsp',
47119         '51',
47120         'iexcl',
47121         '52',
47122         'cent',
47123         '53',
47124         'pound',
47125         '54',
47126         'curren',
47127         '55',
47128         'yen',
47129         '56',
47130         'brvbar',
47131         '57',
47132         'sect',
47133         '58',
47134         'uml',
47135         '59',
47136         'copy',
47137         '5a',
47138         'ordf',
47139         '5b',
47140         'laquo',
47141         '5c',
47142         'not',
47143         '5d',
47144         'shy',
47145         '5e',
47146         'reg',
47147         '5f',
47148         'macr',
47149         '5g',
47150         'deg',
47151         '5h',
47152         'plusmn',
47153         '5i',
47154         'sup2',
47155         '5j',
47156         'sup3',
47157         '5k',
47158         'acute',
47159         '5l',
47160         'micro',
47161         '5m',
47162         'para',
47163         '5n',
47164         'middot',
47165         '5o',
47166         'cedil',
47167         '5p',
47168         'sup1',
47169         '5q',
47170         'ordm',
47171         '5r',
47172         'raquo',
47173         '5s',
47174         'frac14',
47175         '5t',
47176         'frac12',
47177         '5u',
47178         'frac34',
47179         '5v',
47180         'iquest',
47181         '60',
47182         'Agrave',
47183         '61',
47184         'Aacute',
47185         '62',
47186         'Acirc',
47187         '63',
47188         'Atilde',
47189         '64',
47190         'Auml',
47191         '65',
47192         'Aring',
47193         '66',
47194         'AElig',
47195         '67',
47196         'Ccedil',
47197         '68',
47198         'Egrave',
47199         '69',
47200         'Eacute',
47201         '6a',
47202         'Ecirc',
47203         '6b',
47204         'Euml',
47205         '6c',
47206         'Igrave',
47207         '6d',
47208         'Iacute',
47209         '6e',
47210         'Icirc',
47211         '6f',
47212         'Iuml',
47213         '6g',
47214         'ETH',
47215         '6h',
47216         'Ntilde',
47217         '6i',
47218         'Ograve',
47219         '6j',
47220         'Oacute',
47221         '6k',
47222         'Ocirc',
47223         '6l',
47224         'Otilde',
47225         '6m',
47226         'Ouml',
47227         '6n',
47228         'times',
47229         '6o',
47230         'Oslash',
47231         '6p',
47232         'Ugrave',
47233         '6q',
47234         'Uacute',
47235         '6r',
47236         'Ucirc',
47237         '6s',
47238         'Uuml',
47239         '6t',
47240         'Yacute',
47241         '6u',
47242         'THORN',
47243         '6v',
47244         'szlig',
47245         '70',
47246         'agrave',
47247         '71',
47248         'aacute',
47249         '72',
47250         'acirc',
47251         '73',
47252         'atilde',
47253         '74',
47254         'auml',
47255         '75',
47256         'aring',
47257         '76',
47258         'aelig',
47259         '77',
47260         'ccedil',
47261         '78',
47262         'egrave',
47263         '79',
47264         'eacute',
47265         '7a',
47266         'ecirc',
47267         '7b',
47268         'euml',
47269         '7c',
47270         'igrave',
47271         '7d',
47272         'iacute',
47273         '7e',
47274         'icirc',
47275         '7f',
47276         'iuml',
47277         '7g',
47278         'eth',
47279         '7h',
47280         'ntilde',
47281         '7i',
47282         'ograve',
47283         '7j',
47284         'oacute',
47285         '7k',
47286         'ocirc',
47287         '7l',
47288         'otilde',
47289         '7m',
47290         'ouml',
47291         '7n',
47292         'divide',
47293         '7o',
47294         'oslash',
47295         '7p',
47296         'ugrave',
47297         '7q',
47298         'uacute',
47299         '7r',
47300         'ucirc',
47301         '7s',
47302         'uuml',
47303         '7t',
47304         'yacute',
47305         '7u',
47306         'thorn',
47307         '7v',
47308         'yuml',
47309         'ci',
47310         'fnof',
47311         'sh',
47312         'Alpha',
47313         'si',
47314         'Beta',
47315         'sj',
47316         'Gamma',
47317         'sk',
47318         'Delta',
47319         'sl',
47320         'Epsilon',
47321         'sm',
47322         'Zeta',
47323         'sn',
47324         'Eta',
47325         'so',
47326         'Theta',
47327         'sp',
47328         'Iota',
47329         'sq',
47330         'Kappa',
47331         'sr',
47332         'Lambda',
47333         'ss',
47334         'Mu',
47335         'st',
47336         'Nu',
47337         'su',
47338         'Xi',
47339         'sv',
47340         'Omicron',
47341         't0',
47342         'Pi',
47343         't1',
47344         'Rho',
47345         't3',
47346         'Sigma',
47347         't4',
47348         'Tau',
47349         't5',
47350         'Upsilon',
47351         't6',
47352         'Phi',
47353         't7',
47354         'Chi',
47355         't8',
47356         'Psi',
47357         't9',
47358         'Omega',
47359         'th',
47360         'alpha',
47361         'ti',
47362         'beta',
47363         'tj',
47364         'gamma',
47365         'tk',
47366         'delta',
47367         'tl',
47368         'epsilon',
47369         'tm',
47370         'zeta',
47371         'tn',
47372         'eta',
47373         'to',
47374         'theta',
47375         'tp',
47376         'iota',
47377         'tq',
47378         'kappa',
47379         'tr',
47380         'lambda',
47381         'ts',
47382         'mu',
47383         'tt',
47384         'nu',
47385         'tu',
47386         'xi',
47387         'tv',
47388         'omicron',
47389         'u0',
47390         'pi',
47391         'u1',
47392         'rho',
47393         'u2',
47394         'sigmaf',
47395         'u3',
47396         'sigma',
47397         'u4',
47398         'tau',
47399         'u5',
47400         'upsilon',
47401         'u6',
47402         'phi',
47403         'u7',
47404         'chi',
47405         'u8',
47406         'psi',
47407         'u9',
47408         'omega',
47409         'uh',
47410         'thetasym',
47411         'ui',
47412         'upsih',
47413         'um',
47414         'piv',
47415         '812',
47416         'bull',
47417         '816',
47418         'hellip',
47419         '81i',
47420         'prime',
47421         '81j',
47422         'Prime',
47423         '81u',
47424         'oline',
47425         '824',
47426         'frasl',
47427         '88o',
47428         'weierp',
47429         '88h',
47430         'image',
47431         '88s',
47432         'real',
47433         '892',
47434         'trade',
47435         '89l',
47436         'alefsym',
47437         '8cg',
47438         'larr',
47439         '8ch',
47440         'uarr',
47441         '8ci',
47442         'rarr',
47443         '8cj',
47444         'darr',
47445         '8ck',
47446         'harr',
47447         '8dl',
47448         'crarr',
47449         '8eg',
47450         'lArr',
47451         '8eh',
47452         'uArr',
47453         '8ei',
47454         'rArr',
47455         '8ej',
47456         'dArr',
47457         '8ek',
47458         'hArr',
47459         '8g0',
47460         'forall',
47461         '8g2',
47462         'part',
47463         '8g3',
47464         'exist',
47465         '8g5',
47466         'empty',
47467         '8g7',
47468         'nabla',
47469         '8g8',
47470         'isin',
47471         '8g9',
47472         'notin',
47473         '8gb',
47474         'ni',
47475         '8gf',
47476         'prod',
47477         '8gh',
47478         'sum',
47479         '8gi',
47480         'minus',
47481         '8gn',
47482         'lowast',
47483         '8gq',
47484         'radic',
47485         '8gt',
47486         'prop',
47487         '8gu',
47488         'infin',
47489         '8h0',
47490         'ang',
47491         '8h7',
47492         'and',
47493         '8h8',
47494         'or',
47495         '8h9',
47496         'cap',
47497         '8ha',
47498         'cup',
47499         '8hb',
47500         'int',
47501         '8hk',
47502         'there4',
47503         '8hs',
47504         'sim',
47505         '8i5',
47506         'cong',
47507         '8i8',
47508         'asymp',
47509         '8j0',
47510         'ne',
47511         '8j1',
47512         'equiv',
47513         '8j4',
47514         'le',
47515         '8j5',
47516         'ge',
47517         '8k2',
47518         'sub',
47519         '8k3',
47520         'sup',
47521         '8k4',
47522         'nsub',
47523         '8k6',
47524         'sube',
47525         '8k7',
47526         'supe',
47527         '8kl',
47528         'oplus',
47529         '8kn',
47530         'otimes',
47531         '8l5',
47532         'perp',
47533         '8m5',
47534         'sdot',
47535         '8o8',
47536         'lceil',
47537         '8o9',
47538         'rceil',
47539         '8oa',
47540         'lfloor',
47541         '8ob',
47542         'rfloor',
47543         '8p9',
47544         'lang',
47545         '8pa',
47546         'rang',
47547         '9ea',
47548         'loz',
47549         '9j0',
47550         'spades',
47551         '9j3',
47552         'clubs',
47553         '9j5',
47554         'hearts',
47555         '9j6',
47556         'diams',
47557         'ai',
47558         'OElig',
47559         'aj',
47560         'oelig',
47561         'b0',
47562         'Scaron',
47563         'b1',
47564         'scaron',
47565         'bo',
47566         'Yuml',
47567         'm6',
47568         'circ',
47569         'ms',
47570         'tilde',
47571         '802',
47572         'ensp',
47573         '803',
47574         'emsp',
47575         '809',
47576         'thinsp',
47577         '80c',
47578         'zwnj',
47579         '80d',
47580         'zwj',
47581         '80e',
47582         'lrm',
47583         '80f',
47584         'rlm',
47585         '80j',
47586         'ndash',
47587         '80k',
47588         'mdash',
47589         '80o',
47590         'lsquo',
47591         '80p',
47592         'rsquo',
47593         '80q',
47594         'sbquo',
47595         '80s',
47596         'ldquo',
47597         '80t',
47598         'rdquo',
47599         '80u',
47600         'bdquo',
47601         '810',
47602         'dagger',
47603         '811',
47604         'Dagger',
47605         '81g',
47606         'permil',
47607         '81p',
47608         'lsaquo',
47609         '81q',
47610         'rsaquo',
47611         '85c',
47612         'euro'
47613     ],
47614
47615          
47616     /**
47617      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
47618      *
47619      * @method encodeRaw
47620      * @param {String} text Text to encode.
47621      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47622      * @return {String} Entity encoded text.
47623      */
47624     encodeRaw: function(text, attr)
47625     {
47626         var t = this;
47627         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47628             return t.baseEntities[chr] || chr;
47629         });
47630     },
47631     /**
47632      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
47633      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
47634      * and is exposed as the DOMUtils.encode function.
47635      *
47636      * @method encodeAllRaw
47637      * @param {String} text Text to encode.
47638      * @return {String} Entity encoded text.
47639      */
47640     encodeAllRaw: function(text) {
47641         var t = this;
47642         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
47643             return t.baseEntities[chr] || chr;
47644         });
47645     },
47646     /**
47647      * Encodes the specified string using numeric entities. The core entities will be
47648      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
47649      *
47650      * @method encodeNumeric
47651      * @param {String} text Text to encode.
47652      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47653      * @return {String} Entity encoded text.
47654      */
47655     encodeNumeric: function(text, attr) {
47656         var t = this;
47657         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47658             // Multi byte sequence convert it to a single entity
47659             if (chr.length > 1) {
47660                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
47661             }
47662             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
47663         });
47664     },
47665     /**
47666      * Encodes the specified string using named entities. The core entities will be encoded
47667      * as named ones but all non lower ascii characters will be encoded into named entities.
47668      *
47669      * @method encodeNamed
47670      * @param {String} text Text to encode.
47671      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47672      * @param {Object} entities Optional parameter with entities to use.
47673      * @return {String} Entity encoded text.
47674      */
47675     encodeNamed: function(text, attr, entities) {
47676         var t = this;
47677         entities = entities || this.namedEntities;
47678         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47679             return t.baseEntities[chr] || entities[chr] || chr;
47680         });
47681     },
47682     /**
47683      * Returns an encode function based on the name(s) and it's optional entities.
47684      *
47685      * @method getEncodeFunc
47686      * @param {String} name Comma separated list of encoders for example named,numeric.
47687      * @param {String} entities Optional parameter with entities to use instead of the built in set.
47688      * @return {function} Encode function to be used.
47689      */
47690     getEncodeFunc: function(name, entities) {
47691         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
47692         var t = this;
47693         function encodeNamedAndNumeric(text, attr) {
47694             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
47695                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
47696             });
47697         }
47698
47699         function encodeCustomNamed(text, attr) {
47700             return t.encodeNamed(text, attr, entities);
47701         }
47702         // Replace + with , to be compatible with previous TinyMCE versions
47703         name = this.makeMap(name.replace(/\+/g, ','));
47704         // Named and numeric encoder
47705         if (name.named && name.numeric) {
47706             return this.encodeNamedAndNumeric;
47707         }
47708         // Named encoder
47709         if (name.named) {
47710             // Custom names
47711             if (entities) {
47712                 return encodeCustomNamed;
47713             }
47714             return this.encodeNamed;
47715         }
47716         // Numeric
47717         if (name.numeric) {
47718             return this.encodeNumeric;
47719         }
47720         // Raw encoder
47721         return this.encodeRaw;
47722     },
47723     /**
47724      * Decodes the specified string, this will replace entities with raw UTF characters.
47725      *
47726      * @method decode
47727      * @param {String} text Text to entity decode.
47728      * @return {String} Entity decoded string.
47729      */
47730     decode: function(text)
47731     {
47732         var  t = this;
47733         return text.replace(this.entityRegExp, function(all, numeric) {
47734             if (numeric) {
47735                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
47736                 // Support upper UTF
47737                 if (numeric > 65535) {
47738                     numeric -= 65536;
47739                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
47740                 }
47741                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
47742             }
47743             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
47744         });
47745     },
47746     nativeDecode : function (text) {
47747         return text;
47748     },
47749     makeMap : function (items, delim, map) {
47750                 var i;
47751                 items = items || [];
47752                 delim = delim || ',';
47753                 if (typeof items == "string") {
47754                         items = items.split(delim);
47755                 }
47756                 map = map || {};
47757                 i = items.length;
47758                 while (i--) {
47759                         map[items[i]] = {};
47760                 }
47761                 return map;
47762         }
47763 };
47764     
47765     
47766     
47767 Roo.htmleditor.TidyEntities.init();
47768 /**
47769  * @class Roo.htmleditor.KeyEnter
47770  * Handle Enter press..
47771  * @cfg {Roo.HtmlEditorCore} core the editor.
47772  * @constructor
47773  * Create a new Filter.
47774  * @param {Object} config Configuration options
47775  */
47776
47777
47778
47779
47780
47781 Roo.htmleditor.KeyEnter = function(cfg) {
47782     Roo.apply(this, cfg);
47783     // this does not actually call walk as it's really just a abstract class
47784  
47785     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
47786 }
47787
47788 //Roo.htmleditor.KeyEnter.i = 0;
47789
47790
47791 Roo.htmleditor.KeyEnter.prototype = {
47792     
47793     core : false,
47794     
47795     keypress : function(e)
47796     {
47797         if (e.charCode != 13 && e.charCode != 10) {
47798             Roo.log([e.charCode,e]);
47799             return true;
47800         }
47801         e.preventDefault();
47802         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
47803         var doc = this.core.doc;
47804           //add a new line
47805        
47806     
47807         var sel = this.core.getSelection();
47808         var range = sel.getRangeAt(0);
47809         var n = range.commonAncestorContainer;
47810         var pc = range.closest([ 'ol', 'ul']);
47811         var pli = range.closest('li');
47812         if (!pc || e.ctrlKey) {
47813             sel.insertNode('br', 'after'); 
47814          
47815             this.core.undoManager.addEvent();
47816             this.core.fireEditorEvent(e);
47817             return false;
47818         }
47819         
47820         // deal with <li> insetion
47821         if (pli.innerText.trim() == '' &&
47822             pli.previousSibling &&
47823             pli.previousSibling.nodeName == 'LI' &&
47824             pli.previousSibling.innerText.trim() ==  '') {
47825             pli.parentNode.removeChild(pli.previousSibling);
47826             sel.cursorAfter(pc);
47827             this.core.undoManager.addEvent();
47828             this.core.fireEditorEvent(e);
47829             return false;
47830         }
47831     
47832         var li = doc.createElement('LI');
47833         li.innerHTML = '&nbsp;';
47834         if (!pli || !pli.firstSibling) {
47835             pc.appendChild(li);
47836         } else {
47837             pli.parentNode.insertBefore(li, pli.firstSibling);
47838         }
47839         sel.cursorText (li.firstChild);
47840       
47841         this.core.undoManager.addEvent();
47842         this.core.fireEditorEvent(e);
47843
47844         return false;
47845         
47846     
47847         
47848         
47849          
47850     }
47851 };
47852      
47853 /**
47854  * @class Roo.htmleditor.Block
47855  * Base class for html editor blocks - do not use it directly .. extend it..
47856  * @cfg {DomElement} node The node to apply stuff to.
47857  * @cfg {String} friendly_name the name that appears in the context bar about this block
47858  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
47859  
47860  * @constructor
47861  * Create a new Filter.
47862  * @param {Object} config Configuration options
47863  */
47864
47865 Roo.htmleditor.Block  = function(cfg)
47866 {
47867     // do nothing .. should not be called really.
47868 }
47869 /**
47870  * factory method to get the block from an element (using cache if necessary)
47871  * @static
47872  * @param {HtmlElement} the dom element
47873  */
47874 Roo.htmleditor.Block.factory = function(node)
47875 {
47876     var cc = Roo.htmleditor.Block.cache;
47877     var id = Roo.get(node).id;
47878     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
47879         Roo.htmleditor.Block.cache[id].readElement(node);
47880         return Roo.htmleditor.Block.cache[id];
47881     }
47882     var db  = node.getAttribute('data-block');
47883     if (!db) {
47884         db = node.nodeName.toLowerCase().toUpperCaseFirst();
47885     }
47886     var cls = Roo.htmleditor['Block' + db];
47887     if (typeof(cls) == 'undefined') {
47888         //Roo.log(node.getAttribute('data-block'));
47889         Roo.log("OOps missing block : " + 'Block' + db);
47890         return false;
47891     }
47892     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
47893     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
47894 };
47895
47896 /**
47897  * initalize all Elements from content that are 'blockable'
47898  * @static
47899  * @param the body element
47900  */
47901 Roo.htmleditor.Block.initAll = function(body, type)
47902 {
47903     if (typeof(type) == 'undefined') {
47904         var ia = Roo.htmleditor.Block.initAll;
47905         ia(body,'table');
47906         ia(body,'td');
47907         ia(body,'figure');
47908         return;
47909     }
47910     Roo.each(Roo.get(body).query(type), function(e) {
47911         Roo.htmleditor.Block.factory(e);    
47912     },this);
47913 };
47914 // question goes here... do we need to clear out this cache sometimes?
47915 // or show we make it relivant to the htmleditor.
47916 Roo.htmleditor.Block.cache = {};
47917
47918 Roo.htmleditor.Block.prototype = {
47919     
47920     node : false,
47921     
47922      // used by context menu
47923     friendly_name : 'Based Block',
47924     
47925     // text for button to delete this element
47926     deleteTitle : false,
47927     
47928     context : false,
47929     /**
47930      * Update a node with values from this object
47931      * @param {DomElement} node
47932      */
47933     updateElement : function(node)
47934     {
47935         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
47936     },
47937      /**
47938      * convert to plain HTML for calling insertAtCursor..
47939      */
47940     toHTML : function()
47941     {
47942         return Roo.DomHelper.markup(this.toObject());
47943     },
47944     /**
47945      * used by readEleemnt to extract data from a node
47946      * may need improving as it's pretty basic
47947      
47948      * @param {DomElement} node
47949      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
47950      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
47951      * @param {String} style the style property - eg. text-align
47952      */
47953     getVal : function(node, tag, attr, style)
47954     {
47955         var n = node;
47956         if (tag !== true && n.tagName != tag.toUpperCase()) {
47957             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
47958             // but kiss for now.
47959             n = node.getElementsByTagName(tag).item(0);
47960         }
47961         if (!n) {
47962             return '';
47963         }
47964         if (attr === false) {
47965             return n;
47966         }
47967         if (attr == 'html') {
47968             return n.innerHTML;
47969         }
47970         if (attr == 'style') {
47971             return n.style[style]; 
47972         }
47973         
47974         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
47975             
47976     },
47977     /**
47978      * create a DomHelper friendly object - for use with 
47979      * Roo.DomHelper.markup / overwrite / etc..
47980      * (override this)
47981      */
47982     toObject : function()
47983     {
47984         return {};
47985     },
47986       /**
47987      * Read a node that has a 'data-block' property - and extract the values from it.
47988      * @param {DomElement} node - the node
47989      */
47990     readElement : function(node)
47991     {
47992         
47993     } 
47994     
47995     
47996 };
47997
47998  
47999
48000 /**
48001  * @class Roo.htmleditor.BlockFigure
48002  * Block that has an image and a figcaption
48003  * @cfg {String} image_src the url for the image
48004  * @cfg {String} align (left|right) alignment for the block default left
48005  * @cfg {String} caption the text to appear below  (and in the alt tag)
48006  * @cfg {String} caption_display (block|none) display or not the caption
48007  * @cfg {String|number} image_width the width of the image number or %?
48008  * @cfg {String|number} image_height the height of the image number or %?
48009  * 
48010  * @constructor
48011  * Create a new Filter.
48012  * @param {Object} config Configuration options
48013  */
48014
48015 Roo.htmleditor.BlockFigure = function(cfg)
48016 {
48017     if (cfg.node) {
48018         this.readElement(cfg.node);
48019         this.updateElement(cfg.node);
48020     }
48021     Roo.apply(this, cfg);
48022 }
48023 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
48024  
48025     
48026     // setable values.
48027     image_src: '',
48028     align: 'center',
48029     caption : '',
48030     caption_display : 'block',
48031     width : '100%',
48032     cls : '',
48033     href: '',
48034     video_url : '',
48035     
48036     // margin: '2%', not used
48037     
48038     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
48039
48040     
48041     // used by context menu
48042     friendly_name : 'Image with caption',
48043     deleteTitle : "Delete Image and Caption",
48044     
48045     contextMenu : function(toolbar)
48046     {
48047         
48048         var block = function() {
48049             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48050         };
48051         
48052         
48053         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48054         
48055         var syncValue = toolbar.editorcore.syncValue;
48056         
48057         var fields = {};
48058         
48059         return [
48060              {
48061                 xtype : 'TextItem',
48062                 text : "Source: ",
48063                 xns : rooui.Toolbar  //Boostrap?
48064             },
48065             {
48066                 xtype : 'Button',
48067                 text: 'Change Image URL',
48068                  
48069                 listeners : {
48070                     click: function (btn, state)
48071                     {
48072                         var b = block();
48073                         
48074                         Roo.MessageBox.show({
48075                             title : "Image Source URL",
48076                             msg : "Enter the url for the image",
48077                             buttons: Roo.MessageBox.OKCANCEL,
48078                             fn: function(btn, val){
48079                                 if (btn != 'ok') {
48080                                     return;
48081                                 }
48082                                 b.image_src = val;
48083                                 b.updateElement();
48084                                 syncValue();
48085                                 toolbar.editorcore.onEditorEvent();
48086                             },
48087                             minWidth:250,
48088                             prompt:true,
48089                             //multiline: multiline,
48090                             modal : true,
48091                             value : b.image_src
48092                         });
48093                     }
48094                 },
48095                 xns : rooui.Toolbar
48096             },
48097          
48098             {
48099                 xtype : 'Button',
48100                 text: 'Change Link URL',
48101                  
48102                 listeners : {
48103                     click: function (btn, state)
48104                     {
48105                         var b = block();
48106                         
48107                         Roo.MessageBox.show({
48108                             title : "Link URL",
48109                             msg : "Enter the url for the link - leave blank to have no link",
48110                             buttons: Roo.MessageBox.OKCANCEL,
48111                             fn: function(btn, val){
48112                                 if (btn != 'ok') {
48113                                     return;
48114                                 }
48115                                 b.href = val;
48116                                 b.updateElement();
48117                                 syncValue();
48118                                 toolbar.editorcore.onEditorEvent();
48119                             },
48120                             minWidth:250,
48121                             prompt:true,
48122                             //multiline: multiline,
48123                             modal : true,
48124                             value : b.href
48125                         });
48126                     }
48127                 },
48128                 xns : rooui.Toolbar
48129             },
48130             {
48131                 xtype : 'Button',
48132                 text: 'Show Video URL',
48133                  
48134                 listeners : {
48135                     click: function (btn, state)
48136                     {
48137                         Roo.MessageBox.alert("Video URL",
48138                             block().video_url == '' ? 'This image is not linked ot a video' :
48139                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
48140                     }
48141                 },
48142                 xns : rooui.Toolbar
48143             },
48144             
48145             
48146             {
48147                 xtype : 'TextItem',
48148                 text : "Width: ",
48149                 xns : rooui.Toolbar  //Boostrap?
48150             },
48151             {
48152                 xtype : 'ComboBox',
48153                 allowBlank : false,
48154                 displayField : 'val',
48155                 editable : true,
48156                 listWidth : 100,
48157                 triggerAction : 'all',
48158                 typeAhead : true,
48159                 valueField : 'val',
48160                 width : 70,
48161                 name : 'width',
48162                 listeners : {
48163                     select : function (combo, r, index)
48164                     {
48165                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48166                         var b = block();
48167                         b.width = r.get('val');
48168                         b.updateElement();
48169                         syncValue();
48170                         toolbar.editorcore.onEditorEvent();
48171                     }
48172                 },
48173                 xns : rooui.form,
48174                 store : {
48175                     xtype : 'SimpleStore',
48176                     data : [
48177                         ['50%'],
48178                         ['80%'],
48179                         ['100%']
48180                     ],
48181                     fields : [ 'val'],
48182                     xns : Roo.data
48183                 }
48184             },
48185             {
48186                 xtype : 'TextItem',
48187                 text : "Align: ",
48188                 xns : rooui.Toolbar  //Boostrap?
48189             },
48190             {
48191                 xtype : 'ComboBox',
48192                 allowBlank : false,
48193                 displayField : 'val',
48194                 editable : true,
48195                 listWidth : 100,
48196                 triggerAction : 'all',
48197                 typeAhead : true,
48198                 valueField : 'val',
48199                 width : 70,
48200                 name : 'align',
48201                 listeners : {
48202                     select : function (combo, r, index)
48203                     {
48204                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48205                         var b = block();
48206                         b.align = r.get('val');
48207                         b.updateElement();
48208                         syncValue();
48209                         toolbar.editorcore.onEditorEvent();
48210                     }
48211                 },
48212                 xns : rooui.form,
48213                 store : {
48214                     xtype : 'SimpleStore',
48215                     data : [
48216                         ['left'],
48217                         ['right'],
48218                         ['center']
48219                     ],
48220                     fields : [ 'val'],
48221                     xns : Roo.data
48222                 }
48223             },
48224             
48225             
48226             {
48227                 xtype : 'Button',
48228                 text: 'Hide Caption',
48229                 name : 'caption_display',
48230                 pressed : false,
48231                 enableToggle : true,
48232                 setValue : function(v) {
48233                     // this trigger toggle.
48234                      
48235                     this.setText(v ? "Hide Caption" : "Show Caption");
48236                     this.setPressed(v != 'block');
48237                 },
48238                 listeners : {
48239                     toggle: function (btn, state)
48240                     {
48241                         var b  = block();
48242                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
48243                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
48244                         b.updateElement();
48245                         syncValue();
48246                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48247                         toolbar.editorcore.onEditorEvent();
48248                     }
48249                 },
48250                 xns : rooui.Toolbar
48251             }
48252         ];
48253         
48254     },
48255     /**
48256      * create a DomHelper friendly object - for use with
48257      * Roo.DomHelper.markup / overwrite / etc..
48258      */
48259     toObject : function()
48260     {
48261         var d = document.createElement('div');
48262         d.innerHTML = this.caption;
48263         
48264         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
48265         
48266         var iw = this.align == 'center' ? this.width : '100%';
48267         var img =   {
48268             tag : 'img',
48269             contenteditable : 'false',
48270             src : this.image_src,
48271             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
48272             style: {
48273                 width : iw,
48274                 maxWidth : iw + ' !important', // this is not getting rendered?
48275                 margin : m  
48276                 
48277             }
48278         };
48279         /*
48280         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
48281                     '<a href="{2}">' + 
48282                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
48283                     '</a>' + 
48284                 '</div>',
48285         */
48286                 
48287         if (this.href.length > 0) {
48288             img = {
48289                 tag : 'a',
48290                 href: this.href,
48291                 contenteditable : 'true',
48292                 cn : [
48293                     img
48294                 ]
48295             };
48296         }
48297         
48298         
48299         if (this.video_url.length > 0) {
48300             img = {
48301                 tag : 'div',
48302                 cls : this.cls,
48303                 frameborder : 0,
48304                 allowfullscreen : true,
48305                 width : 420,  // these are for video tricks - that we replace the outer
48306                 height : 315,
48307                 src : this.video_url,
48308                 cn : [
48309                     img
48310                 ]
48311             };
48312         }
48313         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
48314         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
48315         
48316   
48317         var ret =   {
48318             tag: 'figure',
48319             'data-block' : 'Figure',
48320             'data-width' : this.width, 
48321             contenteditable : 'false',
48322             
48323             style : {
48324                 display: 'block',
48325                 float :  this.align ,
48326                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
48327                 width : this.align == 'center' ? '100%' : this.width,
48328                 margin:  '0px',
48329                 padding: this.align == 'center' ? '0' : '0 10px' ,
48330                 textAlign : this.align   // seems to work for email..
48331                 
48332             },
48333            
48334             
48335             align : this.align,
48336             cn : [
48337                 img,
48338               
48339                 {
48340                     tag: 'figcaption',
48341                     'data-display' : this.caption_display,
48342                     style : {
48343                         textAlign : 'left',
48344                         fontSize : '16px',
48345                         lineHeight : '24px',
48346                         display : this.caption_display,
48347                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
48348                         margin: m,
48349                         width: this.align == 'center' ?  this.width : '100%' 
48350                     
48351                          
48352                     },
48353                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
48354                     cn : [
48355                         {
48356                             tag: 'div',
48357                             style  : {
48358                                 marginTop : '16px',
48359                                 textAlign : 'left'
48360                             },
48361                             align: 'left',
48362                             cn : [
48363                                 {
48364                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
48365                                     tag : 'i',
48366                                     contenteditable : true,
48367                                     html : captionhtml
48368                                 }
48369                                 
48370                             ]
48371                         }
48372                         
48373                     ]
48374                     
48375                 }
48376             ]
48377         };
48378         return ret;
48379          
48380     },
48381     
48382     readElement : function(node)
48383     {
48384         // this should not really come from the link...
48385         this.video_url = this.getVal(node, 'div', 'src');
48386         this.cls = this.getVal(node, 'div', 'class');
48387         this.href = this.getVal(node, 'a', 'href');
48388         
48389         
48390         this.image_src = this.getVal(node, 'img', 'src');
48391          
48392         this.align = this.getVal(node, 'figure', 'align');
48393         var figcaption = this.getVal(node, 'figcaption', false);
48394         if (figcaption !== '') {
48395             this.caption = this.getVal(figcaption, 'i', 'html');
48396         }
48397         
48398
48399         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
48400         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
48401         this.width = this.getVal(node, true, 'data-width');
48402         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
48403         
48404     },
48405     removeNode : function()
48406     {
48407         return this.node;
48408     }
48409     
48410   
48411    
48412      
48413     
48414     
48415     
48416     
48417 })
48418
48419  
48420
48421 /**
48422  * @class Roo.htmleditor.BlockTable
48423  * Block that manages a table
48424  * 
48425  * @constructor
48426  * Create a new Filter.
48427  * @param {Object} config Configuration options
48428  */
48429
48430 Roo.htmleditor.BlockTable = function(cfg)
48431 {
48432     if (cfg.node) {
48433         this.readElement(cfg.node);
48434         this.updateElement(cfg.node);
48435     }
48436     Roo.apply(this, cfg);
48437     if (!cfg.node) {
48438         this.rows = [];
48439         for(var r = 0; r < this.no_row; r++) {
48440             this.rows[r] = [];
48441             for(var c = 0; c < this.no_col; c++) {
48442                 this.rows[r][c] = this.emptyCell();
48443             }
48444         }
48445     }
48446     
48447     
48448 }
48449 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
48450  
48451     rows : false,
48452     no_col : 1,
48453     no_row : 1,
48454     
48455     
48456     width: '100%',
48457     
48458     // used by context menu
48459     friendly_name : 'Table',
48460     deleteTitle : 'Delete Table',
48461     // context menu is drawn once..
48462     
48463     contextMenu : function(toolbar)
48464     {
48465         
48466         var block = function() {
48467             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48468         };
48469         
48470         
48471         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48472         
48473         var syncValue = toolbar.editorcore.syncValue;
48474         
48475         var fields = {};
48476         
48477         return [
48478             {
48479                 xtype : 'TextItem',
48480                 text : "Width: ",
48481                 xns : rooui.Toolbar  //Boostrap?
48482             },
48483             {
48484                 xtype : 'ComboBox',
48485                 allowBlank : false,
48486                 displayField : 'val',
48487                 editable : true,
48488                 listWidth : 100,
48489                 triggerAction : 'all',
48490                 typeAhead : true,
48491                 valueField : 'val',
48492                 width : 100,
48493                 name : 'width',
48494                 listeners : {
48495                     select : function (combo, r, index)
48496                     {
48497                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48498                         var b = block();
48499                         b.width = r.get('val');
48500                         b.updateElement();
48501                         syncValue();
48502                         toolbar.editorcore.onEditorEvent();
48503                     }
48504                 },
48505                 xns : rooui.form,
48506                 store : {
48507                     xtype : 'SimpleStore',
48508                     data : [
48509                         ['100%'],
48510                         ['auto']
48511                     ],
48512                     fields : [ 'val'],
48513                     xns : Roo.data
48514                 }
48515             },
48516             // -------- Cols
48517             
48518             {
48519                 xtype : 'TextItem',
48520                 text : "Columns: ",
48521                 xns : rooui.Toolbar  //Boostrap?
48522             },
48523          
48524             {
48525                 xtype : 'Button',
48526                 text: '-',
48527                 listeners : {
48528                     click : function (_self, e)
48529                     {
48530                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48531                         block().removeColumn();
48532                         syncValue();
48533                         toolbar.editorcore.onEditorEvent();
48534                     }
48535                 },
48536                 xns : rooui.Toolbar
48537             },
48538             {
48539                 xtype : 'Button',
48540                 text: '+',
48541                 listeners : {
48542                     click : function (_self, e)
48543                     {
48544                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48545                         block().addColumn();
48546                         syncValue();
48547                         toolbar.editorcore.onEditorEvent();
48548                     }
48549                 },
48550                 xns : rooui.Toolbar
48551             },
48552             // -------- ROWS
48553             {
48554                 xtype : 'TextItem',
48555                 text : "Rows: ",
48556                 xns : rooui.Toolbar  //Boostrap?
48557             },
48558          
48559             {
48560                 xtype : 'Button',
48561                 text: '-',
48562                 listeners : {
48563                     click : function (_self, e)
48564                     {
48565                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48566                         block().removeRow();
48567                         syncValue();
48568                         toolbar.editorcore.onEditorEvent();
48569                     }
48570                 },
48571                 xns : rooui.Toolbar
48572             },
48573             {
48574                 xtype : 'Button',
48575                 text: '+',
48576                 listeners : {
48577                     click : function (_self, e)
48578                     {
48579                         block().addRow();
48580                         syncValue();
48581                         toolbar.editorcore.onEditorEvent();
48582                     }
48583                 },
48584                 xns : rooui.Toolbar
48585             },
48586             // -------- ROWS
48587             {
48588                 xtype : 'Button',
48589                 text: 'Reset Column Widths',
48590                 listeners : {
48591                     
48592                     click : function (_self, e)
48593                     {
48594                         block().resetWidths();
48595                         syncValue();
48596                         toolbar.editorcore.onEditorEvent();
48597                     }
48598                 },
48599                 xns : rooui.Toolbar
48600             } 
48601             
48602             
48603             
48604         ];
48605         
48606     },
48607     
48608     
48609   /**
48610      * create a DomHelper friendly object - for use with
48611      * Roo.DomHelper.markup / overwrite / etc..
48612      * ?? should it be called with option to hide all editing features?
48613      */
48614     toObject : function()
48615     {
48616         
48617         var ret = {
48618             tag : 'table',
48619             contenteditable : 'false', // this stops cell selection from picking the table.
48620             'data-block' : 'Table',
48621             style : {
48622                 width:  this.width,
48623                 border : 'solid 1px #000', // ??? hard coded?
48624                 'border-collapse' : 'collapse' 
48625             },
48626             cn : [
48627                 { tag : 'tbody' , cn : [] }
48628             ]
48629         };
48630         
48631         // do we have a head = not really 
48632         var ncols = 0;
48633         Roo.each(this.rows, function( row ) {
48634             var tr = {
48635                 tag: 'tr',
48636                 style : {
48637                     margin: '6px',
48638                     border : 'solid 1px #000',
48639                     textAlign : 'left' 
48640                 },
48641                 cn : [ ]
48642             };
48643             
48644             ret.cn[0].cn.push(tr);
48645             // does the row have any properties? ?? height?
48646             var nc = 0;
48647             Roo.each(row, function( cell ) {
48648                 
48649                 var td = {
48650                     tag : 'td',
48651                     contenteditable :  'true',
48652                     'data-block' : 'Td',
48653                     html : cell.html,
48654                     style : cell.style
48655                 };
48656                 if (cell.colspan > 1) {
48657                     td.colspan = cell.colspan ;
48658                     nc += cell.colspan;
48659                 } else {
48660                     nc++;
48661                 }
48662                 if (cell.rowspan > 1) {
48663                     td.rowspan = cell.rowspan ;
48664                 }
48665                 
48666                 
48667                 // widths ?
48668                 tr.cn.push(td);
48669                     
48670                 
48671             }, this);
48672             ncols = Math.max(nc, ncols);
48673             
48674             
48675         }, this);
48676         // add the header row..
48677         
48678         ncols++;
48679          
48680         
48681         return ret;
48682          
48683     },
48684     
48685     readElement : function(node)
48686     {
48687         node  = node ? node : this.node ;
48688         this.width = this.getVal(node, true, 'style', 'width') || '100%';
48689         
48690         this.rows = [];
48691         this.no_row = 0;
48692         var trs = Array.from(node.rows);
48693         trs.forEach(function(tr) {
48694             var row =  [];
48695             this.rows.push(row);
48696             
48697             this.no_row++;
48698             var no_column = 0;
48699             Array.from(tr.cells).forEach(function(td) {
48700                 
48701                 var add = {
48702                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
48703                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
48704                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
48705                     html : td.innerHTML
48706                 };
48707                 no_column += add.colspan;
48708                      
48709                 
48710                 row.push(add);
48711                 
48712                 
48713             },this);
48714             this.no_col = Math.max(this.no_col, no_column);
48715             
48716             
48717         },this);
48718         
48719         
48720     },
48721     normalizeRows: function()
48722     {
48723         var ret= [];
48724         var rid = -1;
48725         this.rows.forEach(function(row) {
48726             rid++;
48727             ret[rid] = [];
48728             row = this.normalizeRow(row);
48729             var cid = 0;
48730             row.forEach(function(c) {
48731                 while (typeof(ret[rid][cid]) != 'undefined') {
48732                     cid++;
48733                 }
48734                 if (typeof(ret[rid]) == 'undefined') {
48735                     ret[rid] = [];
48736                 }
48737                 ret[rid][cid] = c;
48738                 c.row = rid;
48739                 c.col = cid;
48740                 if (c.rowspan < 2) {
48741                     return;
48742                 }
48743                 
48744                 for(var i = 1 ;i < c.rowspan; i++) {
48745                     if (typeof(ret[rid+i]) == 'undefined') {
48746                         ret[rid+i] = [];
48747                     }
48748                     ret[rid+i][cid] = c;
48749                 }
48750             });
48751         }, this);
48752         return ret;
48753     
48754     },
48755     
48756     normalizeRow: function(row)
48757     {
48758         var ret= [];
48759         row.forEach(function(c) {
48760             if (c.colspan < 2) {
48761                 ret.push(c);
48762                 return;
48763             }
48764             for(var i =0 ;i < c.colspan; i++) {
48765                 ret.push(c);
48766             }
48767         });
48768         return ret;
48769     
48770     },
48771     
48772     deleteColumn : function(sel)
48773     {
48774         if (!sel || sel.type != 'col') {
48775             return;
48776         }
48777         if (this.no_col < 2) {
48778             return;
48779         }
48780         
48781         this.rows.forEach(function(row) {
48782             var cols = this.normalizeRow(row);
48783             var col = cols[sel.col];
48784             if (col.colspan > 1) {
48785                 col.colspan --;
48786             } else {
48787                 row.remove(col);
48788             }
48789             
48790         }, this);
48791         this.no_col--;
48792         
48793     },
48794     removeColumn : function()
48795     {
48796         this.deleteColumn({
48797             type: 'col',
48798             col : this.no_col-1
48799         });
48800         this.updateElement();
48801     },
48802     
48803      
48804     addColumn : function()
48805     {
48806         
48807         this.rows.forEach(function(row) {
48808             row.push(this.emptyCell());
48809            
48810         }, this);
48811         this.updateElement();
48812     },
48813     
48814     deleteRow : function(sel)
48815     {
48816         if (!sel || sel.type != 'row') {
48817             return;
48818         }
48819         
48820         if (this.no_row < 2) {
48821             return;
48822         }
48823         
48824         var rows = this.normalizeRows();
48825         
48826         
48827         rows[sel.row].forEach(function(col) {
48828             if (col.rowspan > 1) {
48829                 col.rowspan--;
48830             } else {
48831                 col.remove = 1; // flage it as removed.
48832             }
48833             
48834         }, this);
48835         var newrows = [];
48836         this.rows.forEach(function(row) {
48837             newrow = [];
48838             row.forEach(function(c) {
48839                 if (typeof(c.remove) == 'undefined') {
48840                     newrow.push(c);
48841                 }
48842                 
48843             });
48844             if (newrow.length > 0) {
48845                 newrows.push(row);
48846             }
48847         });
48848         this.rows =  newrows;
48849         
48850         
48851         
48852         this.no_row--;
48853         this.updateElement();
48854         
48855     },
48856     removeRow : function()
48857     {
48858         this.deleteRow({
48859             type: 'row',
48860             row : this.no_row-1
48861         });
48862         
48863     },
48864     
48865      
48866     addRow : function()
48867     {
48868         
48869         var row = [];
48870         for (var i = 0; i < this.no_col; i++ ) {
48871             
48872             row.push(this.emptyCell());
48873            
48874         }
48875         this.rows.push(row);
48876         this.updateElement();
48877         
48878     },
48879      
48880     // the default cell object... at present...
48881     emptyCell : function() {
48882         return (new Roo.htmleditor.BlockTd({})).toObject();
48883         
48884      
48885     },
48886     
48887     removeNode : function()
48888     {
48889         return this.node;
48890     },
48891     
48892     
48893     
48894     resetWidths : function()
48895     {
48896         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
48897             var nn = Roo.htmleditor.Block.factory(n);
48898             nn.width = '';
48899             nn.updateElement(n);
48900         });
48901     }
48902     
48903     
48904     
48905     
48906 })
48907
48908 /**
48909  *
48910  * editing a TD?
48911  *
48912  * since selections really work on the table cell, then editing really should work from there
48913  *
48914  * The original plan was to support merging etc... - but that may not be needed yet..
48915  *
48916  * So this simple version will support:
48917  *   add/remove cols
48918  *   adjust the width +/-
48919  *   reset the width...
48920  *   
48921  *
48922  */
48923
48924
48925  
48926
48927 /**
48928  * @class Roo.htmleditor.BlockTable
48929  * Block that manages a table
48930  * 
48931  * @constructor
48932  * Create a new Filter.
48933  * @param {Object} config Configuration options
48934  */
48935
48936 Roo.htmleditor.BlockTd = function(cfg)
48937 {
48938     if (cfg.node) {
48939         this.readElement(cfg.node);
48940         this.updateElement(cfg.node);
48941     }
48942     Roo.apply(this, cfg);
48943      
48944     
48945     
48946 }
48947 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
48948  
48949     node : false,
48950     
48951     width: '',
48952     textAlign : 'left',
48953     valign : 'top',
48954     
48955     colspan : 1,
48956     rowspan : 1,
48957     
48958     
48959     // used by context menu
48960     friendly_name : 'Table Cell',
48961     deleteTitle : false, // use our customer delete
48962     
48963     // context menu is drawn once..
48964     
48965     contextMenu : function(toolbar)
48966     {
48967         
48968         var cell = function() {
48969             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48970         };
48971         
48972         var table = function() {
48973             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
48974         };
48975         
48976         var lr = false;
48977         var saveSel = function()
48978         {
48979             lr = toolbar.editorcore.getSelection().getRangeAt(0);
48980         }
48981         var restoreSel = function()
48982         {
48983             if (lr) {
48984                 (function() {
48985                     toolbar.editorcore.focus();
48986                     var cr = toolbar.editorcore.getSelection();
48987                     cr.removeAllRanges();
48988                     cr.addRange(lr);
48989                     toolbar.editorcore.onEditorEvent();
48990                 }).defer(10, this);
48991                 
48992                 
48993             }
48994         }
48995         
48996         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48997         
48998         var syncValue = toolbar.editorcore.syncValue;
48999         
49000         var fields = {};
49001         
49002         return [
49003             {
49004                 xtype : 'Button',
49005                 text : 'Edit Table',
49006                 listeners : {
49007                     click : function() {
49008                         var t = toolbar.tb.selectedNode.closest('table');
49009                         toolbar.editorcore.selectNode(t);
49010                         toolbar.editorcore.onEditorEvent();                        
49011                     }
49012                 }
49013                 
49014             },
49015               
49016            
49017              
49018             {
49019                 xtype : 'TextItem',
49020                 text : "Column Width: ",
49021                  xns : rooui.Toolbar 
49022                
49023             },
49024             {
49025                 xtype : 'Button',
49026                 text: '-',
49027                 listeners : {
49028                     click : function (_self, e)
49029                     {
49030                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49031                         cell().shrinkColumn();
49032                         syncValue();
49033                          toolbar.editorcore.onEditorEvent();
49034                     }
49035                 },
49036                 xns : rooui.Toolbar
49037             },
49038             {
49039                 xtype : 'Button',
49040                 text: '+',
49041                 listeners : {
49042                     click : function (_self, e)
49043                     {
49044                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49045                         cell().growColumn();
49046                         syncValue();
49047                         toolbar.editorcore.onEditorEvent();
49048                     }
49049                 },
49050                 xns : rooui.Toolbar
49051             },
49052             
49053             {
49054                 xtype : 'TextItem',
49055                 text : "Vertical Align: ",
49056                 xns : rooui.Toolbar  //Boostrap?
49057             },
49058             {
49059                 xtype : 'ComboBox',
49060                 allowBlank : false,
49061                 displayField : 'val',
49062                 editable : true,
49063                 listWidth : 100,
49064                 triggerAction : 'all',
49065                 typeAhead : true,
49066                 valueField : 'val',
49067                 width : 100,
49068                 name : 'valign',
49069                 listeners : {
49070                     select : function (combo, r, index)
49071                     {
49072                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49073                         var b = cell();
49074                         b.valign = r.get('val');
49075                         b.updateElement();
49076                         syncValue();
49077                         toolbar.editorcore.onEditorEvent();
49078                     }
49079                 },
49080                 xns : rooui.form,
49081                 store : {
49082                     xtype : 'SimpleStore',
49083                     data : [
49084                         ['top'],
49085                         ['middle'],
49086                         ['bottom'] // there are afew more... 
49087                     ],
49088                     fields : [ 'val'],
49089                     xns : Roo.data
49090                 }
49091             },
49092             
49093             {
49094                 xtype : 'TextItem',
49095                 text : "Merge Cells: ",
49096                  xns : rooui.Toolbar 
49097                
49098             },
49099             
49100             
49101             {
49102                 xtype : 'Button',
49103                 text: 'Right',
49104                 listeners : {
49105                     click : function (_self, e)
49106                     {
49107                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49108                         cell().mergeRight();
49109                         //block().growColumn();
49110                         syncValue();
49111                         toolbar.editorcore.onEditorEvent();
49112                     }
49113                 },
49114                 xns : rooui.Toolbar
49115             },
49116              
49117             {
49118                 xtype : 'Button',
49119                 text: 'Below',
49120                 listeners : {
49121                     click : function (_self, e)
49122                     {
49123                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49124                         cell().mergeBelow();
49125                         //block().growColumn();
49126                         syncValue();
49127                         toolbar.editorcore.onEditorEvent();
49128                     }
49129                 },
49130                 xns : rooui.Toolbar
49131             },
49132             {
49133                 xtype : 'TextItem',
49134                 text : "| ",
49135                  xns : rooui.Toolbar 
49136                
49137             },
49138             
49139             {
49140                 xtype : 'Button',
49141                 text: 'Split',
49142                 listeners : {
49143                     click : function (_self, e)
49144                     {
49145                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49146                         cell().split();
49147                         syncValue();
49148                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49149                         toolbar.editorcore.onEditorEvent();
49150                                              
49151                     }
49152                 },
49153                 xns : rooui.Toolbar
49154             },
49155             {
49156                 xtype : 'Fill',
49157                 xns : rooui.Toolbar 
49158                
49159             },
49160         
49161           
49162             {
49163                 xtype : 'Button',
49164                 text: 'Delete',
49165                  
49166                 xns : rooui.Toolbar,
49167                 menu : {
49168                     xtype : 'Menu',
49169                     xns : rooui.menu,
49170                     items : [
49171                         {
49172                             xtype : 'Item',
49173                             html: 'Column',
49174                             listeners : {
49175                                 click : function (_self, e)
49176                                 {
49177                                     var t = table();
49178                                     
49179                                     cell().deleteColumn();
49180                                     syncValue();
49181                                     toolbar.editorcore.selectNode(t.node);
49182                                     toolbar.editorcore.onEditorEvent();   
49183                                 }
49184                             },
49185                             xns : rooui.menu
49186                         },
49187                         {
49188                             xtype : 'Item',
49189                             html: 'Row',
49190                             listeners : {
49191                                 click : function (_self, e)
49192                                 {
49193                                     var t = table();
49194                                     cell().deleteRow();
49195                                     syncValue();
49196                                     
49197                                     toolbar.editorcore.selectNode(t.node);
49198                                     toolbar.editorcore.onEditorEvent();   
49199                                                          
49200                                 }
49201                             },
49202                             xns : rooui.menu
49203                         },
49204                        {
49205                             xtype : 'Separator',
49206                             xns : rooui.menu
49207                         },
49208                         {
49209                             xtype : 'Item',
49210                             html: 'Table',
49211                             listeners : {
49212                                 click : function (_self, e)
49213                                 {
49214                                     var t = table();
49215                                     var nn = t.node.nextSibling || t.node.previousSibling;
49216                                     t.node.parentNode.removeChild(t.node);
49217                                     if (nn) { 
49218                                         toolbar.editorcore.selectNode(nn, true);
49219                                     }
49220                                     toolbar.editorcore.onEditorEvent();   
49221                                                          
49222                                 }
49223                             },
49224                             xns : rooui.menu
49225                         }
49226                     ]
49227                 }
49228             }
49229             
49230             // align... << fixme
49231             
49232         ];
49233         
49234     },
49235     
49236     
49237   /**
49238      * create a DomHelper friendly object - for use with
49239      * Roo.DomHelper.markup / overwrite / etc..
49240      * ?? should it be called with option to hide all editing features?
49241      */
49242  /**
49243      * create a DomHelper friendly object - for use with
49244      * Roo.DomHelper.markup / overwrite / etc..
49245      * ?? should it be called with option to hide all editing features?
49246      */
49247     toObject : function()
49248     {
49249         
49250         var ret = {
49251             tag : 'td',
49252             contenteditable : 'true', // this stops cell selection from picking the table.
49253             'data-block' : 'Td',
49254             valign : this.valign,
49255             style : {  
49256                 'text-align' :  this.textAlign,
49257                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
49258                 'border-collapse' : 'collapse',
49259                 padding : '6px', // 8 for desktop / 4 for mobile
49260                 'vertical-align': this.valign
49261             },
49262             html : this.html
49263         };
49264         if (this.width != '') {
49265             ret.width = this.width;
49266             ret.style.width = this.width;
49267         }
49268         
49269         
49270         if (this.colspan > 1) {
49271             ret.colspan = this.colspan ;
49272         } 
49273         if (this.rowspan > 1) {
49274             ret.rowspan = this.rowspan ;
49275         }
49276         
49277            
49278         
49279         return ret;
49280          
49281     },
49282     
49283     readElement : function(node)
49284     {
49285         node  = node ? node : this.node ;
49286         this.width = node.style.width;
49287         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
49288         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
49289         this.html = node.innerHTML;
49290         
49291         
49292     },
49293      
49294     // the default cell object... at present...
49295     emptyCell : function() {
49296         return {
49297             colspan :  1,
49298             rowspan :  1,
49299             textAlign : 'left',
49300             html : "&nbsp;" // is this going to be editable now?
49301         };
49302      
49303     },
49304     
49305     removeNode : function()
49306     {
49307         return this.node.closest('table');
49308          
49309     },
49310     
49311     cellData : false,
49312     
49313     colWidths : false,
49314     
49315     toTableArray  : function()
49316     {
49317         var ret = [];
49318         var tab = this.node.closest('tr').closest('table');
49319         Array.from(tab.rows).forEach(function(r, ri){
49320             ret[ri] = [];
49321         });
49322         var rn = 0;
49323         this.colWidths = [];
49324         var all_auto = true;
49325         Array.from(tab.rows).forEach(function(r, ri){
49326             
49327             var cn = 0;
49328             Array.from(r.cells).forEach(function(ce, ci){
49329                 var c =  {
49330                     cell : ce,
49331                     row : rn,
49332                     col: cn,
49333                     colspan : ce.colSpan,
49334                     rowspan : ce.rowSpan
49335                 };
49336                 if (ce.isEqualNode(this.node)) {
49337                     this.cellData = c;
49338                 }
49339                 // if we have been filled up by a row?
49340                 if (typeof(ret[rn][cn]) != 'undefined') {
49341                     while(typeof(ret[rn][cn]) != 'undefined') {
49342                         cn++;
49343                     }
49344                     c.col = cn;
49345                 }
49346                 
49347                 if (typeof(this.colWidths[cn]) == 'undefined') {
49348                     this.colWidths[cn] =   ce.style.width;
49349                     if (this.colWidths[cn] != '') {
49350                         all_auto = false;
49351                     }
49352                 }
49353                 
49354                 
49355                 if (c.colspan < 2 && c.rowspan < 2 ) {
49356                     ret[rn][cn] = c;
49357                     cn++;
49358                     return;
49359                 }
49360                 for(var j = 0; j < c.rowspan; j++) {
49361                     if (typeof(ret[rn+j]) == 'undefined') {
49362                         continue; // we have a problem..
49363                     }
49364                     ret[rn+j][cn] = c;
49365                     for(var i = 0; i < c.colspan; i++) {
49366                         ret[rn+j][cn+i] = c;
49367                     }
49368                 }
49369                 
49370                 cn += c.colspan;
49371             }, this);
49372             rn++;
49373         }, this);
49374         
49375         // initalize widths.?
49376         // either all widths or no widths..
49377         if (all_auto) {
49378             this.colWidths[0] = false; // no widths flag.
49379         }
49380         
49381         
49382         return ret;
49383         
49384     },
49385     
49386     
49387     
49388     
49389     mergeRight: function()
49390     {
49391          
49392         // get the contents of the next cell along..
49393         var tr = this.node.closest('tr');
49394         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
49395         if (i >= tr.childNodes.length - 1) {
49396             return; // no cells on right to merge with.
49397         }
49398         var table = this.toTableArray();
49399         
49400         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
49401             return; // nothing right?
49402         }
49403         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
49404         // right cell - must be same rowspan and on the same row.
49405         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
49406             return; // right hand side is not same rowspan.
49407         }
49408         
49409         
49410         
49411         this.node.innerHTML += ' ' + rc.cell.innerHTML;
49412         tr.removeChild(rc.cell);
49413         this.colspan += rc.colspan;
49414         this.node.setAttribute('colspan', this.colspan);
49415
49416     },
49417     
49418     
49419     mergeBelow : function()
49420     {
49421         var table = this.toTableArray();
49422         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
49423             return; // no row below
49424         }
49425         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
49426             return; // nothing right?
49427         }
49428         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
49429         
49430         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
49431             return; // right hand side is not same rowspan.
49432         }
49433         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
49434         rc.cell.parentNode.removeChild(rc.cell);
49435         this.rowspan += rc.rowspan;
49436         this.node.setAttribute('rowspan', this.rowspan);
49437     },
49438     
49439     split: function()
49440     {
49441         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
49442             return;
49443         }
49444         var table = this.toTableArray();
49445         var cd = this.cellData;
49446         this.rowspan = 1;
49447         this.colspan = 1;
49448         
49449         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
49450             
49451             
49452             
49453             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
49454                 if (r == cd.row && c == cd.col) {
49455                     this.node.removeAttribute('rowspan');
49456                     this.node.removeAttribute('colspan');
49457                     continue;
49458                 }
49459                  
49460                 var ntd = this.node.cloneNode(); // which col/row should be 0..
49461                 ntd.removeAttribute('id'); //
49462                 //ntd.style.width  = '';
49463                 ntd.innerHTML = '';
49464                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
49465             }
49466             
49467         }
49468         this.redrawAllCells(table);
49469         
49470          
49471         
49472     },
49473     
49474     
49475     
49476     redrawAllCells: function(table)
49477     {
49478         
49479          
49480         var tab = this.node.closest('tr').closest('table');
49481         var ctr = tab.rows[0].parentNode;
49482         Array.from(tab.rows).forEach(function(r, ri){
49483             
49484             Array.from(r.cells).forEach(function(ce, ci){
49485                 ce.parentNode.removeChild(ce);
49486             });
49487             r.parentNode.removeChild(r);
49488         });
49489         for(var r = 0 ; r < table.length; r++) {
49490             var re = tab.rows[r];
49491             
49492             var re = tab.ownerDocument.createElement('tr');
49493             ctr.appendChild(re);
49494             for(var c = 0 ; c < table[r].length; c++) {
49495                 if (table[r][c].cell === false) {
49496                     continue;
49497                 }
49498                 
49499                 re.appendChild(table[r][c].cell);
49500                  
49501                 table[r][c].cell = false;
49502             }
49503         }
49504         
49505     },
49506     updateWidths : function(table)
49507     {
49508         for(var r = 0 ; r < table.length; r++) {
49509            
49510             for(var c = 0 ; c < table[r].length; c++) {
49511                 if (table[r][c].cell === false) {
49512                     continue;
49513                 }
49514                 
49515                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
49516                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49517                     el.width = Math.floor(this.colWidths[c])  +'%';
49518                     el.updateElement(el.node);
49519                 }
49520                 table[r][c].cell = false; // done
49521             }
49522         }
49523     },
49524     normalizeWidths : function(table)
49525     {
49526     
49527         if (this.colWidths[0] === false) {
49528             var nw = 100.0 / this.colWidths.length;
49529             this.colWidths.forEach(function(w,i) {
49530                 this.colWidths[i] = nw;
49531             },this);
49532             return;
49533         }
49534     
49535         var t = 0, missing = [];
49536         
49537         this.colWidths.forEach(function(w,i) {
49538             //if you mix % and
49539             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
49540             var add =  this.colWidths[i];
49541             if (add > 0) {
49542                 t+=add;
49543                 return;
49544             }
49545             missing.push(i);
49546             
49547             
49548         },this);
49549         var nc = this.colWidths.length;
49550         if (missing.length) {
49551             var mult = (nc - missing.length) / (1.0 * nc);
49552             var t = mult * t;
49553             var ew = (100 -t) / (1.0 * missing.length);
49554             this.colWidths.forEach(function(w,i) {
49555                 if (w > 0) {
49556                     this.colWidths[i] = w * mult;
49557                     return;
49558                 }
49559                 
49560                 this.colWidths[i] = ew;
49561             }, this);
49562             // have to make up numbers..
49563              
49564         }
49565         // now we should have all the widths..
49566         
49567     
49568     },
49569     
49570     shrinkColumn : function()
49571     {
49572         var table = this.toTableArray();
49573         this.normalizeWidths(table);
49574         var col = this.cellData.col;
49575         var nw = this.colWidths[col] * 0.8;
49576         if (nw < 5) {
49577             return;
49578         }
49579         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
49580         this.colWidths.forEach(function(w,i) {
49581             if (i == col) {
49582                  this.colWidths[i] = nw;
49583                 return;
49584             }
49585             this.colWidths[i] += otherAdd
49586         }, this);
49587         this.updateWidths(table);
49588          
49589     },
49590     growColumn : function()
49591     {
49592         var table = this.toTableArray();
49593         this.normalizeWidths(table);
49594         var col = this.cellData.col;
49595         var nw = this.colWidths[col] * 1.2;
49596         if (nw > 90) {
49597             return;
49598         }
49599         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
49600         this.colWidths.forEach(function(w,i) {
49601             if (i == col) {
49602                 this.colWidths[i] = nw;
49603                 return;
49604             }
49605             this.colWidths[i] -= otherSub
49606         }, this);
49607         this.updateWidths(table);
49608          
49609     },
49610     deleteRow : function()
49611     {
49612         // delete this rows 'tr'
49613         // if any of the cells in this row have a rowspan > 1 && row!= this row..
49614         // then reduce the rowspan.
49615         var table = this.toTableArray();
49616         // this.cellData.row;
49617         for (var i =0;i< table[this.cellData.row].length ; i++) {
49618             var c = table[this.cellData.row][i];
49619             if (c.row != this.cellData.row) {
49620                 
49621                 c.rowspan--;
49622                 c.cell.setAttribute('rowspan', c.rowspan);
49623                 continue;
49624             }
49625             if (c.rowspan > 1) {
49626                 c.rowspan--;
49627                 c.cell.setAttribute('rowspan', c.rowspan);
49628             }
49629         }
49630         table.splice(this.cellData.row,1);
49631         this.redrawAllCells(table);
49632         
49633     },
49634     deleteColumn : function()
49635     {
49636         var table = this.toTableArray();
49637         
49638         for (var i =0;i< table.length ; i++) {
49639             var c = table[i][this.cellData.col];
49640             if (c.col != this.cellData.col) {
49641                 table[i][this.cellData.col].colspan--;
49642             } else if (c.colspan > 1) {
49643                 c.colspan--;
49644                 c.cell.setAttribute('colspan', c.colspan);
49645             }
49646             table[i].splice(this.cellData.col,1);
49647         }
49648         
49649         this.redrawAllCells(table);
49650     }
49651     
49652     
49653     
49654     
49655 })
49656
49657 //<script type="text/javascript">
49658
49659 /*
49660  * Based  Ext JS Library 1.1.1
49661  * Copyright(c) 2006-2007, Ext JS, LLC.
49662  * LGPL
49663  *
49664  */
49665  
49666 /**
49667  * @class Roo.HtmlEditorCore
49668  * @extends Roo.Component
49669  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
49670  *
49671  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
49672  */
49673
49674 Roo.HtmlEditorCore = function(config){
49675     
49676     
49677     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
49678     
49679     
49680     this.addEvents({
49681         /**
49682          * @event initialize
49683          * Fires when the editor is fully initialized (including the iframe)
49684          * @param {Roo.HtmlEditorCore} this
49685          */
49686         initialize: true,
49687         /**
49688          * @event activate
49689          * Fires when the editor is first receives the focus. Any insertion must wait
49690          * until after this event.
49691          * @param {Roo.HtmlEditorCore} this
49692          */
49693         activate: true,
49694          /**
49695          * @event beforesync
49696          * Fires before the textarea is updated with content from the editor iframe. Return false
49697          * to cancel the sync.
49698          * @param {Roo.HtmlEditorCore} this
49699          * @param {String} html
49700          */
49701         beforesync: true,
49702          /**
49703          * @event beforepush
49704          * Fires before the iframe editor is updated with content from the textarea. Return false
49705          * to cancel the push.
49706          * @param {Roo.HtmlEditorCore} this
49707          * @param {String} html
49708          */
49709         beforepush: true,
49710          /**
49711          * @event sync
49712          * Fires when the textarea is updated with content from the editor iframe.
49713          * @param {Roo.HtmlEditorCore} this
49714          * @param {String} html
49715          */
49716         sync: true,
49717          /**
49718          * @event push
49719          * Fires when the iframe editor is updated with content from the textarea.
49720          * @param {Roo.HtmlEditorCore} this
49721          * @param {String} html
49722          */
49723         push: true,
49724         
49725         /**
49726          * @event editorevent
49727          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
49728          * @param {Roo.HtmlEditorCore} this
49729          */
49730         editorevent: true 
49731          
49732         
49733     });
49734     
49735     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
49736     
49737     // defaults : white / black...
49738     this.applyBlacklists();
49739     
49740     
49741     
49742 };
49743
49744
49745 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
49746
49747
49748      /**
49749      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
49750      */
49751     
49752     owner : false,
49753     
49754      /**
49755      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
49756      *                        Roo.resizable.
49757      */
49758     resizable : false,
49759      /**
49760      * @cfg {Number} height (in pixels)
49761      */   
49762     height: 300,
49763    /**
49764      * @cfg {Number} width (in pixels)
49765      */   
49766     width: 500,
49767      /**
49768      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
49769      *         if you are doing an email editor, this probably needs disabling, it's designed
49770      */
49771     autoClean: true,
49772     
49773     /**
49774      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
49775      */
49776     enableBlocks : true,
49777     /**
49778      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
49779      * 
49780      */
49781     stylesheets: false,
49782      /**
49783      * @cfg {String} language default en - language of text (usefull for rtl languages)
49784      * 
49785      */
49786     language: 'en',
49787     
49788     /**
49789      * @cfg {boolean} allowComments - default false - allow comments in HTML source
49790      *          - by default they are stripped - if you are editing email you may need this.
49791      */
49792     allowComments: false,
49793     // id of frame..
49794     frameId: false,
49795     
49796     // private properties
49797     validationEvent : false,
49798     deferHeight: true,
49799     initialized : false,
49800     activated : false,
49801     sourceEditMode : false,
49802     onFocus : Roo.emptyFn,
49803     iframePad:3,
49804     hideMode:'offsets',
49805     
49806     clearUp: true,
49807     
49808     // blacklist + whitelisted elements..
49809     black: false,
49810     white: false,
49811      
49812     bodyCls : '',
49813
49814     
49815     undoManager : false,
49816     /**
49817      * Protected method that will not generally be called directly. It
49818      * is called when the editor initializes the iframe with HTML contents. Override this method if you
49819      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
49820      */
49821     getDocMarkup : function(){
49822         // body styles..
49823         var st = '';
49824         
49825         // inherit styels from page...?? 
49826         if (this.stylesheets === false) {
49827             
49828             Roo.get(document.head).select('style').each(function(node) {
49829                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49830             });
49831             
49832             Roo.get(document.head).select('link').each(function(node) { 
49833                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49834             });
49835             
49836         } else if (!this.stylesheets.length) {
49837                 // simple..
49838                 st = '<style type="text/css">' +
49839                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49840                    '</style>';
49841         } else {
49842             for (var i in this.stylesheets) {
49843                 if (typeof(this.stylesheets[i]) != 'string') {
49844                     continue;
49845                 }
49846                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
49847             }
49848             
49849         }
49850         
49851         st +=  '<style type="text/css">' +
49852             'IMG { cursor: pointer } ' +
49853         '</style>';
49854         
49855         st += '<meta name="google" content="notranslate">';
49856         
49857         var cls = 'notranslate roo-htmleditor-body';
49858         
49859         if(this.bodyCls.length){
49860             cls += ' ' + this.bodyCls;
49861         }
49862         
49863         return '<html  class="notranslate" translate="no"><head>' + st  +
49864             //<style type="text/css">' +
49865             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49866             //'</style>' +
49867             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
49868     },
49869
49870     // private
49871     onRender : function(ct, position)
49872     {
49873         var _t = this;
49874         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
49875         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
49876         
49877         
49878         this.el.dom.style.border = '0 none';
49879         this.el.dom.setAttribute('tabIndex', -1);
49880         this.el.addClass('x-hidden hide');
49881         
49882         
49883         
49884         if(Roo.isIE){ // fix IE 1px bogus margin
49885             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
49886         }
49887        
49888         
49889         this.frameId = Roo.id();
49890         
49891          
49892         
49893         var iframe = this.owner.wrap.createChild({
49894             tag: 'iframe',
49895             cls: 'form-control', // bootstrap..
49896             id: this.frameId,
49897             name: this.frameId,
49898             frameBorder : 'no',
49899             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
49900         }, this.el
49901         );
49902         
49903         
49904         this.iframe = iframe.dom;
49905
49906         this.assignDocWin();
49907         
49908         this.doc.designMode = 'on';
49909        
49910         this.doc.open();
49911         this.doc.write(this.getDocMarkup());
49912         this.doc.close();
49913
49914         
49915         var task = { // must defer to wait for browser to be ready
49916             run : function(){
49917                 //console.log("run task?" + this.doc.readyState);
49918                 this.assignDocWin();
49919                 if(this.doc.body || this.doc.readyState == 'complete'){
49920                     try {
49921                         this.doc.designMode="on";
49922                         
49923                     } catch (e) {
49924                         return;
49925                     }
49926                     Roo.TaskMgr.stop(task);
49927                     this.initEditor.defer(10, this);
49928                 }
49929             },
49930             interval : 10,
49931             duration: 10000,
49932             scope: this
49933         };
49934         Roo.TaskMgr.start(task);
49935
49936     },
49937
49938     // private
49939     onResize : function(w, h)
49940     {
49941          Roo.log('resize: ' +w + ',' + h );
49942         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
49943         if(!this.iframe){
49944             return;
49945         }
49946         if(typeof w == 'number'){
49947             
49948             this.iframe.style.width = w + 'px';
49949         }
49950         if(typeof h == 'number'){
49951             
49952             this.iframe.style.height = h + 'px';
49953             if(this.doc){
49954                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
49955             }
49956         }
49957         
49958     },
49959
49960     /**
49961      * Toggles the editor between standard and source edit mode.
49962      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
49963      */
49964     toggleSourceEdit : function(sourceEditMode){
49965         
49966         this.sourceEditMode = sourceEditMode === true;
49967         
49968         if(this.sourceEditMode){
49969  
49970             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
49971             
49972         }else{
49973             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
49974             //this.iframe.className = '';
49975             this.deferFocus();
49976         }
49977         //this.setSize(this.owner.wrap.getSize());
49978         //this.fireEvent('editmodechange', this, this.sourceEditMode);
49979     },
49980
49981     
49982   
49983
49984     /**
49985      * Protected method that will not generally be called directly. If you need/want
49986      * custom HTML cleanup, this is the method you should override.
49987      * @param {String} html The HTML to be cleaned
49988      * return {String} The cleaned HTML
49989      */
49990     cleanHtml : function(html)
49991     {
49992         html = String(html);
49993         if(html.length > 5){
49994             if(Roo.isSafari){ // strip safari nonsense
49995                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
49996             }
49997         }
49998         if(html == '&nbsp;'){
49999             html = '';
50000         }
50001         return html;
50002     },
50003
50004     /**
50005      * HTML Editor -> Textarea
50006      * Protected method that will not generally be called directly. Syncs the contents
50007      * of the editor iframe with the textarea.
50008      */
50009     syncValue : function()
50010     {
50011         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
50012         if(this.initialized){
50013             
50014             if (this.undoManager) {
50015                 this.undoManager.addEvent();
50016             }
50017
50018             
50019             var bd = (this.doc.body || this.doc.documentElement);
50020            
50021             
50022             var sel = this.win.getSelection();
50023             
50024             var div = document.createElement('div');
50025             div.innerHTML = bd.innerHTML;
50026             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
50027             if (gtx.length > 0) {
50028                 var rm = gtx.item(0).parentNode;
50029                 rm.parentNode.removeChild(rm);
50030             }
50031             
50032            
50033             if (this.enableBlocks) {
50034                 new Roo.htmleditor.FilterBlock({ node : div });
50035             }
50036             //?? tidy?
50037             var tidy = new Roo.htmleditor.TidySerializer({
50038                 inner:  true
50039             });
50040             var html  = tidy.serialize(div);
50041             
50042             
50043             if(Roo.isSafari){
50044                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
50045                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
50046                 if(m && m[1]){
50047                     html = '<div style="'+m[0]+'">' + html + '</div>';
50048                 }
50049             }
50050             html = this.cleanHtml(html);
50051             // fix up the special chars.. normaly like back quotes in word...
50052             // however we do not want to do this with chinese..
50053             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
50054                 
50055                 var cc = match.charCodeAt();
50056
50057                 // Get the character value, handling surrogate pairs
50058                 if (match.length == 2) {
50059                     // It's a surrogate pair, calculate the Unicode code point
50060                     var high = match.charCodeAt(0) - 0xD800;
50061                     var low  = match.charCodeAt(1) - 0xDC00;
50062                     cc = (high * 0x400) + low + 0x10000;
50063                 }  else if (
50064                     (cc >= 0x4E00 && cc < 0xA000 ) ||
50065                     (cc >= 0x3400 && cc < 0x4E00 ) ||
50066                     (cc >= 0xf900 && cc < 0xfb00 )
50067                 ) {
50068                         return match;
50069                 }  
50070          
50071                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
50072                 return "&#" + cc + ";";
50073                 
50074                 
50075             });
50076             
50077             
50078              
50079             if(this.owner.fireEvent('beforesync', this, html) !== false){
50080                 this.el.dom.value = html;
50081                 this.owner.fireEvent('sync', this, html);
50082             }
50083         }
50084     },
50085
50086     /**
50087      * TEXTAREA -> EDITABLE
50088      * Protected method that will not generally be called directly. Pushes the value of the textarea
50089      * into the iframe editor.
50090      */
50091     pushValue : function()
50092     {
50093         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
50094         if(this.initialized){
50095             var v = this.el.dom.value.trim();
50096             
50097             
50098             if(this.owner.fireEvent('beforepush', this, v) !== false){
50099                 var d = (this.doc.body || this.doc.documentElement);
50100                 d.innerHTML = v;
50101                  
50102                 this.el.dom.value = d.innerHTML;
50103                 this.owner.fireEvent('push', this, v);
50104             }
50105             if (this.autoClean) {
50106                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
50107                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
50108             }
50109             if (this.enableBlocks) {
50110                 Roo.htmleditor.Block.initAll(this.doc.body);
50111             }
50112             
50113             this.updateLanguage();
50114             
50115             var lc = this.doc.body.lastChild;
50116             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
50117                 // add an extra line at the end.
50118                 this.doc.body.appendChild(this.doc.createElement('br'));
50119             }
50120             
50121             
50122         }
50123     },
50124
50125     // private
50126     deferFocus : function(){
50127         this.focus.defer(10, this);
50128     },
50129
50130     // doc'ed in Field
50131     focus : function(){
50132         if(this.win && !this.sourceEditMode){
50133             this.win.focus();
50134         }else{
50135             this.el.focus();
50136         }
50137     },
50138     
50139     assignDocWin: function()
50140     {
50141         var iframe = this.iframe;
50142         
50143          if(Roo.isIE){
50144             this.doc = iframe.contentWindow.document;
50145             this.win = iframe.contentWindow;
50146         } else {
50147 //            if (!Roo.get(this.frameId)) {
50148 //                return;
50149 //            }
50150 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50151 //            this.win = Roo.get(this.frameId).dom.contentWindow;
50152             
50153             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
50154                 return;
50155             }
50156             
50157             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50158             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
50159         }
50160     },
50161     
50162     // private
50163     initEditor : function(){
50164         //console.log("INIT EDITOR");
50165         this.assignDocWin();
50166         
50167         
50168         
50169         this.doc.designMode="on";
50170         this.doc.open();
50171         this.doc.write(this.getDocMarkup());
50172         this.doc.close();
50173         
50174         var dbody = (this.doc.body || this.doc.documentElement);
50175         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
50176         // this copies styles from the containing element into thsi one..
50177         // not sure why we need all of this..
50178         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
50179         
50180         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
50181         //ss['background-attachment'] = 'fixed'; // w3c
50182         dbody.bgProperties = 'fixed'; // ie
50183         dbody.setAttribute("translate", "no");
50184         
50185         //Roo.DomHelper.applyStyles(dbody, ss);
50186         Roo.EventManager.on(this.doc, {
50187              
50188             'mouseup': this.onEditorEvent,
50189             'dblclick': this.onEditorEvent,
50190             'click': this.onEditorEvent,
50191             'keyup': this.onEditorEvent,
50192             
50193             buffer:100,
50194             scope: this
50195         });
50196         Roo.EventManager.on(this.doc, {
50197             'paste': this.onPasteEvent,
50198             scope : this
50199         });
50200         if(Roo.isGecko){
50201             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
50202         }
50203         //??? needed???
50204         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
50205             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
50206         }
50207         this.initialized = true;
50208
50209         
50210         // initialize special key events - enter
50211         new Roo.htmleditor.KeyEnter({core : this});
50212         
50213          
50214         
50215         this.owner.fireEvent('initialize', this);
50216         this.pushValue();
50217     },
50218     // this is to prevent a href clicks resulting in a redirect?
50219    
50220     onPasteEvent : function(e,v)
50221     {
50222         // I think we better assume paste is going to be a dirty load of rubish from word..
50223         
50224         // even pasting into a 'email version' of this widget will have to clean up that mess.
50225         var cd = (e.browserEvent.clipboardData || window.clipboardData);
50226         
50227         // check what type of paste - if it's an image, then handle it differently.
50228         if (cd.files && cd.files.length > 0) {
50229             // pasting images?
50230             var urlAPI = (window.createObjectURL && window) || 
50231                 (window.URL && URL.revokeObjectURL && URL) || 
50232                 (window.webkitURL && webkitURL);
50233     
50234             var url = urlAPI.createObjectURL( cd.files[0]);
50235             this.insertAtCursor('<img src=" + url + ">');
50236             return false;
50237         }
50238         if (cd.types.indexOf('text/html') < 0 ) {
50239             return false;
50240         }
50241         var images = [];
50242         var html = cd.getData('text/html'); // clipboard event
50243         if (cd.types.indexOf('text/rtf') > -1) {
50244             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
50245             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
50246         }
50247         //Roo.log(images);
50248         //Roo.log(imgs);
50249         // fixme..
50250         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
50251                        .map(function(g) { return g.toDataURL(); })
50252                        .filter(function(g) { return g != 'about:blank'; });
50253         
50254         
50255         html = this.cleanWordChars(html);
50256         
50257         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
50258         
50259         
50260         var sn = this.getParentElement();
50261         // check if d contains a table, and prevent nesting??
50262         //Roo.log(d.getElementsByTagName('table'));
50263         //Roo.log(sn);
50264         //Roo.log(sn.closest('table'));
50265         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
50266             e.preventDefault();
50267             this.insertAtCursor("You can not nest tables");
50268             //Roo.log("prevent?"); // fixme - 
50269             return false;
50270         }
50271         
50272         if (images.length > 0) {
50273             Roo.each(d.getElementsByTagName('img'), function(img, i) {
50274                 img.setAttribute('src', images[i]);
50275             });
50276         }
50277         if (this.autoClean) {
50278             new Roo.htmleditor.FilterWord({ node : d });
50279             
50280             new Roo.htmleditor.FilterStyleToTag({ node : d });
50281             new Roo.htmleditor.FilterAttributes({
50282                 node : d,
50283                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
50284                 attrib_clean : ['href', 'src' ] 
50285             });
50286             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
50287             // should be fonts..
50288             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
50289             new Roo.htmleditor.FilterParagraph({ node : d });
50290             new Roo.htmleditor.FilterSpan({ node : d });
50291             new Roo.htmleditor.FilterLongBr({ node : d });
50292             new Roo.htmleditor.FilterComment({ node : d });
50293             
50294             
50295         }
50296         if (this.enableBlocks) {
50297                 
50298             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50299                 if (img.closest('figure')) { // assume!! that it's aready
50300                     return;
50301                 }
50302                 var fig  = new Roo.htmleditor.BlockFigure({
50303                     image_src  : img.src
50304                 });
50305                 fig.updateElement(img); // replace it..
50306                 
50307             });
50308         }
50309         
50310         
50311         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
50312         if (this.enableBlocks) {
50313             Roo.htmleditor.Block.initAll(this.doc.body);
50314         }
50315          
50316         
50317         e.preventDefault();
50318         return false;
50319         // default behaveiour should be our local cleanup paste? (optional?)
50320         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
50321         //this.owner.fireEvent('paste', e, v);
50322     },
50323     // private
50324     onDestroy : function(){
50325         
50326         
50327         
50328         if(this.rendered){
50329             
50330             //for (var i =0; i < this.toolbars.length;i++) {
50331             //    // fixme - ask toolbars for heights?
50332             //    this.toolbars[i].onDestroy();
50333            // }
50334             
50335             //this.wrap.dom.innerHTML = '';
50336             //this.wrap.remove();
50337         }
50338     },
50339
50340     // private
50341     onFirstFocus : function(){
50342         
50343         this.assignDocWin();
50344         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
50345         
50346         this.activated = true;
50347          
50348     
50349         if(Roo.isGecko){ // prevent silly gecko errors
50350             this.win.focus();
50351             var s = this.win.getSelection();
50352             if(!s.focusNode || s.focusNode.nodeType != 3){
50353                 var r = s.getRangeAt(0);
50354                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
50355                 r.collapse(true);
50356                 this.deferFocus();
50357             }
50358             try{
50359                 this.execCmd('useCSS', true);
50360                 this.execCmd('styleWithCSS', false);
50361             }catch(e){}
50362         }
50363         this.owner.fireEvent('activate', this);
50364     },
50365
50366     // private
50367     adjustFont: function(btn){
50368         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
50369         //if(Roo.isSafari){ // safari
50370         //    adjust *= 2;
50371        // }
50372         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
50373         if(Roo.isSafari){ // safari
50374             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
50375             v =  (v < 10) ? 10 : v;
50376             v =  (v > 48) ? 48 : v;
50377             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
50378             
50379         }
50380         
50381         
50382         v = Math.max(1, v+adjust);
50383         
50384         this.execCmd('FontSize', v  );
50385     },
50386
50387     onEditorEvent : function(e)
50388     {
50389          
50390         
50391         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
50392             return; // we do not handle this.. (undo manager does..)
50393         }
50394         // in theory this detects if the last element is not a br, then we try and do that.
50395         // its so clicking in space at bottom triggers adding a br and moving the cursor.
50396         if (e &&
50397             e.target.nodeName == 'BODY' &&
50398             e.type == "mouseup" &&
50399             this.doc.body.lastChild
50400            ) {
50401             var lc = this.doc.body.lastChild;
50402             // gtx-trans is google translate plugin adding crap.
50403             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
50404                 lc = lc.previousSibling;
50405             }
50406             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
50407             // if last element is <BR> - then dont do anything.
50408             
50409                 var ns = this.doc.createElement('br');
50410                 this.doc.body.appendChild(ns);
50411                 range = this.doc.createRange();
50412                 range.setStartAfter(ns);
50413                 range.collapse(true);
50414                 var sel = this.win.getSelection();
50415                 sel.removeAllRanges();
50416                 sel.addRange(range);
50417             }
50418         }
50419         
50420         
50421         
50422         this.fireEditorEvent(e);
50423       //  this.updateToolbar();
50424         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
50425     },
50426     
50427     fireEditorEvent: function(e)
50428     {
50429         this.owner.fireEvent('editorevent', this, e);
50430     },
50431
50432     insertTag : function(tg)
50433     {
50434         // could be a bit smarter... -> wrap the current selected tRoo..
50435         if (tg.toLowerCase() == 'span' ||
50436             tg.toLowerCase() == 'code' ||
50437             tg.toLowerCase() == 'sup' ||
50438             tg.toLowerCase() == 'sub' 
50439             ) {
50440             
50441             range = this.createRange(this.getSelection());
50442             var wrappingNode = this.doc.createElement(tg.toLowerCase());
50443             wrappingNode.appendChild(range.extractContents());
50444             range.insertNode(wrappingNode);
50445
50446             return;
50447             
50448             
50449             
50450         }
50451         this.execCmd("formatblock",   tg);
50452         this.undoManager.addEvent(); 
50453     },
50454     
50455     insertText : function(txt)
50456     {
50457         
50458         
50459         var range = this.createRange();
50460         range.deleteContents();
50461                //alert(Sender.getAttribute('label'));
50462                
50463         range.insertNode(this.doc.createTextNode(txt));
50464         this.undoManager.addEvent();
50465     } ,
50466     
50467      
50468
50469     /**
50470      * Executes a Midas editor command on the editor document and performs necessary focus and
50471      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
50472      * @param {String} cmd The Midas command
50473      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50474      */
50475     relayCmd : function(cmd, value)
50476     {
50477         
50478         switch (cmd) {
50479             case 'justifyleft':
50480             case 'justifyright':
50481             case 'justifycenter':
50482                 // if we are in a cell, then we will adjust the
50483                 var n = this.getParentElement();
50484                 var td = n.closest('td');
50485                 if (td) {
50486                     var bl = Roo.htmleditor.Block.factory(td);
50487                     bl.textAlign = cmd.replace('justify','');
50488                     bl.updateElement();
50489                     this.owner.fireEvent('editorevent', this);
50490                     return;
50491                 }
50492                 this.execCmd('styleWithCSS', true); // 
50493                 break;
50494             case 'bold':
50495             case 'italic':
50496                 // if there is no selection, then we insert, and set the curson inside it..
50497                 this.execCmd('styleWithCSS', false); 
50498                 break;
50499                 
50500         
50501             default:
50502                 break;
50503         }
50504         
50505         
50506         this.win.focus();
50507         this.execCmd(cmd, value);
50508         this.owner.fireEvent('editorevent', this);
50509         //this.updateToolbar();
50510         this.owner.deferFocus();
50511     },
50512
50513     /**
50514      * Executes a Midas editor command directly on the editor document.
50515      * For visual commands, you should use {@link #relayCmd} instead.
50516      * <b>This should only be called after the editor is initialized.</b>
50517      * @param {String} cmd The Midas command
50518      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50519      */
50520     execCmd : function(cmd, value){
50521         this.doc.execCommand(cmd, false, value === undefined ? null : value);
50522         this.syncValue();
50523     },
50524  
50525  
50526    
50527     /**
50528      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
50529      * to insert tRoo.
50530      * @param {String} text | dom node.. 
50531      */
50532     insertAtCursor : function(text)
50533     {
50534         
50535         if(!this.activated){
50536             return;
50537         }
50538          
50539         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
50540             this.win.focus();
50541             
50542             
50543             // from jquery ui (MIT licenced)
50544             var range, node;
50545             var win = this.win;
50546             
50547             if (win.getSelection && win.getSelection().getRangeAt) {
50548                 
50549                 // delete the existing?
50550                 
50551                 this.createRange(this.getSelection()).deleteContents();
50552                 range = win.getSelection().getRangeAt(0);
50553                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
50554                 range.insertNode(node);
50555                 range = range.cloneRange();
50556                 range.collapse(false);
50557                  
50558                 win.getSelection().removeAllRanges();
50559                 win.getSelection().addRange(range);
50560                 
50561                 
50562                 
50563             } else if (win.document.selection && win.document.selection.createRange) {
50564                 // no firefox support
50565                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50566                 win.document.selection.createRange().pasteHTML(txt);
50567             
50568             } else {
50569                 // no firefox support
50570                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50571                 this.execCmd('InsertHTML', txt);
50572             } 
50573             this.syncValue();
50574             
50575             this.deferFocus();
50576         }
50577     },
50578  // private
50579     mozKeyPress : function(e){
50580         if(e.ctrlKey){
50581             var c = e.getCharCode(), cmd;
50582           
50583             if(c > 0){
50584                 c = String.fromCharCode(c).toLowerCase();
50585                 switch(c){
50586                     case 'b':
50587                         cmd = 'bold';
50588                         break;
50589                     case 'i':
50590                         cmd = 'italic';
50591                         break;
50592                     
50593                     case 'u':
50594                         cmd = 'underline';
50595                         break;
50596                     
50597                     //case 'v':
50598                       //  this.cleanUpPaste.defer(100, this);
50599                       //  return;
50600                         
50601                 }
50602                 if(cmd){
50603                     
50604                     this.relayCmd(cmd);
50605                     //this.win.focus();
50606                     //this.execCmd(cmd);
50607                     //this.deferFocus();
50608                     e.preventDefault();
50609                 }
50610                 
50611             }
50612         }
50613     },
50614
50615     // private
50616     fixKeys : function(){ // load time branching for fastest keydown performance
50617         
50618         
50619         if(Roo.isIE){
50620             return function(e){
50621                 var k = e.getKey(), r;
50622                 if(k == e.TAB){
50623                     e.stopEvent();
50624                     r = this.doc.selection.createRange();
50625                     if(r){
50626                         r.collapse(true);
50627                         r.pasteHTML('&#160;&#160;&#160;&#160;');
50628                         this.deferFocus();
50629                     }
50630                     return;
50631                 }
50632                 /// this is handled by Roo.htmleditor.KeyEnter
50633                  /*
50634                 if(k == e.ENTER){
50635                     r = this.doc.selection.createRange();
50636                     if(r){
50637                         var target = r.parentElement();
50638                         if(!target || target.tagName.toLowerCase() != 'li'){
50639                             e.stopEvent();
50640                             r.pasteHTML('<br/>');
50641                             r.collapse(false);
50642                             r.select();
50643                         }
50644                     }
50645                 }
50646                 */
50647                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50648                 //    this.cleanUpPaste.defer(100, this);
50649                 //    return;
50650                 //}
50651                 
50652                 
50653             };
50654         }else if(Roo.isOpera){
50655             return function(e){
50656                 var k = e.getKey();
50657                 if(k == e.TAB){
50658                     e.stopEvent();
50659                     this.win.focus();
50660                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
50661                     this.deferFocus();
50662                 }
50663                
50664                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50665                 //    this.cleanUpPaste.defer(100, this);
50666                  //   return;
50667                 //}
50668                 
50669             };
50670         }else if(Roo.isSafari){
50671             return function(e){
50672                 var k = e.getKey();
50673                 
50674                 if(k == e.TAB){
50675                     e.stopEvent();
50676                     this.execCmd('InsertText','\t');
50677                     this.deferFocus();
50678                     return;
50679                 }
50680                  this.mozKeyPress(e);
50681                 
50682                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50683                  //   this.cleanUpPaste.defer(100, this);
50684                  //   return;
50685                // }
50686                 
50687              };
50688         }
50689     }(),
50690     
50691     getAllAncestors: function()
50692     {
50693         var p = this.getSelectedNode();
50694         var a = [];
50695         if (!p) {
50696             a.push(p); // push blank onto stack..
50697             p = this.getParentElement();
50698         }
50699         
50700         
50701         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
50702             a.push(p);
50703             p = p.parentNode;
50704         }
50705         a.push(this.doc.body);
50706         return a;
50707     },
50708     lastSel : false,
50709     lastSelNode : false,
50710     
50711     
50712     getSelection : function() 
50713     {
50714         this.assignDocWin();
50715         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
50716     },
50717     /**
50718      * Select a dom node
50719      * @param {DomElement} node the node to select
50720      */
50721     selectNode : function(node, collapse)
50722     {
50723         var nodeRange = node.ownerDocument.createRange();
50724         try {
50725             nodeRange.selectNode(node);
50726         } catch (e) {
50727             nodeRange.selectNodeContents(node);
50728         }
50729         if (collapse === true) {
50730             nodeRange.collapse(true);
50731         }
50732         //
50733         var s = this.win.getSelection();
50734         s.removeAllRanges();
50735         s.addRange(nodeRange);
50736     },
50737     
50738     getSelectedNode: function() 
50739     {
50740         // this may only work on Gecko!!!
50741         
50742         // should we cache this!!!!
50743         
50744          
50745          
50746         var range = this.createRange(this.getSelection()).cloneRange();
50747         
50748         if (Roo.isIE) {
50749             var parent = range.parentElement();
50750             while (true) {
50751                 var testRange = range.duplicate();
50752                 testRange.moveToElementText(parent);
50753                 if (testRange.inRange(range)) {
50754                     break;
50755                 }
50756                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
50757                     break;
50758                 }
50759                 parent = parent.parentElement;
50760             }
50761             return parent;
50762         }
50763         
50764         // is ancestor a text element.
50765         var ac =  range.commonAncestorContainer;
50766         if (ac.nodeType == 3) {
50767             ac = ac.parentNode;
50768         }
50769         
50770         var ar = ac.childNodes;
50771          
50772         var nodes = [];
50773         var other_nodes = [];
50774         var has_other_nodes = false;
50775         for (var i=0;i<ar.length;i++) {
50776             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
50777                 continue;
50778             }
50779             // fullly contained node.
50780             
50781             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
50782                 nodes.push(ar[i]);
50783                 continue;
50784             }
50785             
50786             // probably selected..
50787             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
50788                 other_nodes.push(ar[i]);
50789                 continue;
50790             }
50791             // outer..
50792             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
50793                 continue;
50794             }
50795             
50796             
50797             has_other_nodes = true;
50798         }
50799         if (!nodes.length && other_nodes.length) {
50800             nodes= other_nodes;
50801         }
50802         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
50803             return false;
50804         }
50805         
50806         return nodes[0];
50807     },
50808     
50809     
50810     createRange: function(sel)
50811     {
50812         // this has strange effects when using with 
50813         // top toolbar - not sure if it's a great idea.
50814         //this.editor.contentWindow.focus();
50815         if (typeof sel != "undefined") {
50816             try {
50817                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
50818             } catch(e) {
50819                 return this.doc.createRange();
50820             }
50821         } else {
50822             return this.doc.createRange();
50823         }
50824     },
50825     getParentElement: function()
50826     {
50827         
50828         this.assignDocWin();
50829         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
50830         
50831         var range = this.createRange(sel);
50832          
50833         try {
50834             var p = range.commonAncestorContainer;
50835             while (p.nodeType == 3) { // text node
50836                 p = p.parentNode;
50837             }
50838             return p;
50839         } catch (e) {
50840             return null;
50841         }
50842     
50843     },
50844     /***
50845      *
50846      * Range intersection.. the hard stuff...
50847      *  '-1' = before
50848      *  '0' = hits..
50849      *  '1' = after.
50850      *         [ -- selected range --- ]
50851      *   [fail]                        [fail]
50852      *
50853      *    basically..
50854      *      if end is before start or  hits it. fail.
50855      *      if start is after end or hits it fail.
50856      *
50857      *   if either hits (but other is outside. - then it's not 
50858      *   
50859      *    
50860      **/
50861     
50862     
50863     // @see http://www.thismuchiknow.co.uk/?p=64.
50864     rangeIntersectsNode : function(range, node)
50865     {
50866         var nodeRange = node.ownerDocument.createRange();
50867         try {
50868             nodeRange.selectNode(node);
50869         } catch (e) {
50870             nodeRange.selectNodeContents(node);
50871         }
50872     
50873         var rangeStartRange = range.cloneRange();
50874         rangeStartRange.collapse(true);
50875     
50876         var rangeEndRange = range.cloneRange();
50877         rangeEndRange.collapse(false);
50878     
50879         var nodeStartRange = nodeRange.cloneRange();
50880         nodeStartRange.collapse(true);
50881     
50882         var nodeEndRange = nodeRange.cloneRange();
50883         nodeEndRange.collapse(false);
50884     
50885         return rangeStartRange.compareBoundaryPoints(
50886                  Range.START_TO_START, nodeEndRange) == -1 &&
50887                rangeEndRange.compareBoundaryPoints(
50888                  Range.START_TO_START, nodeStartRange) == 1;
50889         
50890          
50891     },
50892     rangeCompareNode : function(range, node)
50893     {
50894         var nodeRange = node.ownerDocument.createRange();
50895         try {
50896             nodeRange.selectNode(node);
50897         } catch (e) {
50898             nodeRange.selectNodeContents(node);
50899         }
50900         
50901         
50902         range.collapse(true);
50903     
50904         nodeRange.collapse(true);
50905      
50906         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
50907         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
50908          
50909         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
50910         
50911         var nodeIsBefore   =  ss == 1;
50912         var nodeIsAfter    = ee == -1;
50913         
50914         if (nodeIsBefore && nodeIsAfter) {
50915             return 0; // outer
50916         }
50917         if (!nodeIsBefore && nodeIsAfter) {
50918             return 1; //right trailed.
50919         }
50920         
50921         if (nodeIsBefore && !nodeIsAfter) {
50922             return 2;  // left trailed.
50923         }
50924         // fully contined.
50925         return 3;
50926     },
50927  
50928     cleanWordChars : function(input) {// change the chars to hex code
50929         
50930        var swapCodes  = [ 
50931             [    8211, "&#8211;" ], 
50932             [    8212, "&#8212;" ], 
50933             [    8216,  "'" ],  
50934             [    8217, "'" ],  
50935             [    8220, '"' ],  
50936             [    8221, '"' ],  
50937             [    8226, "*" ],  
50938             [    8230, "..." ]
50939         ]; 
50940         var output = input;
50941         Roo.each(swapCodes, function(sw) { 
50942             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
50943             
50944             output = output.replace(swapper, sw[1]);
50945         });
50946         
50947         return output;
50948     },
50949     
50950      
50951     
50952         
50953     
50954     cleanUpChild : function (node)
50955     {
50956         
50957         new Roo.htmleditor.FilterComment({node : node});
50958         new Roo.htmleditor.FilterAttributes({
50959                 node : node,
50960                 attrib_black : this.ablack,
50961                 attrib_clean : this.aclean,
50962                 style_white : this.cwhite,
50963                 style_black : this.cblack
50964         });
50965         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
50966         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
50967          
50968         
50969     },
50970     
50971     /**
50972      * Clean up MS wordisms...
50973      * @deprecated - use filter directly
50974      */
50975     cleanWord : function(node)
50976     {
50977         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
50978         
50979     },
50980    
50981     
50982     /**
50983
50984      * @deprecated - use filters
50985      */
50986     cleanTableWidths : function(node)
50987     {
50988         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
50989         
50990  
50991     },
50992     
50993      
50994         
50995     applyBlacklists : function()
50996     {
50997         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
50998         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
50999         
51000         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
51001         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
51002         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
51003         
51004         this.white = [];
51005         this.black = [];
51006         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
51007             if (b.indexOf(tag) > -1) {
51008                 return;
51009             }
51010             this.white.push(tag);
51011             
51012         }, this);
51013         
51014         Roo.each(w, function(tag) {
51015             if (b.indexOf(tag) > -1) {
51016                 return;
51017             }
51018             if (this.white.indexOf(tag) > -1) {
51019                 return;
51020             }
51021             this.white.push(tag);
51022             
51023         }, this);
51024         
51025         
51026         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
51027             if (w.indexOf(tag) > -1) {
51028                 return;
51029             }
51030             this.black.push(tag);
51031             
51032         }, this);
51033         
51034         Roo.each(b, function(tag) {
51035             if (w.indexOf(tag) > -1) {
51036                 return;
51037             }
51038             if (this.black.indexOf(tag) > -1) {
51039                 return;
51040             }
51041             this.black.push(tag);
51042             
51043         }, this);
51044         
51045         
51046         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
51047         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
51048         
51049         this.cwhite = [];
51050         this.cblack = [];
51051         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
51052             if (b.indexOf(tag) > -1) {
51053                 return;
51054             }
51055             this.cwhite.push(tag);
51056             
51057         }, this);
51058         
51059         Roo.each(w, function(tag) {
51060             if (b.indexOf(tag) > -1) {
51061                 return;
51062             }
51063             if (this.cwhite.indexOf(tag) > -1) {
51064                 return;
51065             }
51066             this.cwhite.push(tag);
51067             
51068         }, this);
51069         
51070         
51071         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
51072             if (w.indexOf(tag) > -1) {
51073                 return;
51074             }
51075             this.cblack.push(tag);
51076             
51077         }, this);
51078         
51079         Roo.each(b, function(tag) {
51080             if (w.indexOf(tag) > -1) {
51081                 return;
51082             }
51083             if (this.cblack.indexOf(tag) > -1) {
51084                 return;
51085             }
51086             this.cblack.push(tag);
51087             
51088         }, this);
51089     },
51090     
51091     setStylesheets : function(stylesheets)
51092     {
51093         if(typeof(stylesheets) == 'string'){
51094             Roo.get(this.iframe.contentDocument.head).createChild({
51095                 tag : 'link',
51096                 rel : 'stylesheet',
51097                 type : 'text/css',
51098                 href : stylesheets
51099             });
51100             
51101             return;
51102         }
51103         var _this = this;
51104      
51105         Roo.each(stylesheets, function(s) {
51106             if(!s.length){
51107                 return;
51108             }
51109             
51110             Roo.get(_this.iframe.contentDocument.head).createChild({
51111                 tag : 'link',
51112                 rel : 'stylesheet',
51113                 type : 'text/css',
51114                 href : s
51115             });
51116         });
51117
51118         
51119     },
51120     
51121     
51122     updateLanguage : function()
51123     {
51124         if (!this.iframe || !this.iframe.contentDocument) {
51125             return;
51126         }
51127         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
51128     },
51129     
51130     
51131     removeStylesheets : function()
51132     {
51133         var _this = this;
51134         
51135         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
51136             s.remove();
51137         });
51138     },
51139     
51140     setStyle : function(style)
51141     {
51142         Roo.get(this.iframe.contentDocument.head).createChild({
51143             tag : 'style',
51144             type : 'text/css',
51145             html : style
51146         });
51147
51148         return;
51149     }
51150     
51151     // hide stuff that is not compatible
51152     /**
51153      * @event blur
51154      * @hide
51155      */
51156     /**
51157      * @event change
51158      * @hide
51159      */
51160     /**
51161      * @event focus
51162      * @hide
51163      */
51164     /**
51165      * @event specialkey
51166      * @hide
51167      */
51168     /**
51169      * @cfg {String} fieldClass @hide
51170      */
51171     /**
51172      * @cfg {String} focusClass @hide
51173      */
51174     /**
51175      * @cfg {String} autoCreate @hide
51176      */
51177     /**
51178      * @cfg {String} inputType @hide
51179      */
51180     /**
51181      * @cfg {String} invalidClass @hide
51182      */
51183     /**
51184      * @cfg {String} invalidText @hide
51185      */
51186     /**
51187      * @cfg {String} msgFx @hide
51188      */
51189     /**
51190      * @cfg {String} validateOnBlur @hide
51191      */
51192 });
51193
51194 Roo.HtmlEditorCore.white = [
51195         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
51196         
51197        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
51198        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
51199        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
51200        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
51201        'TABLE',   'UL',         'XMP', 
51202        
51203        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
51204       'THEAD',   'TR', 
51205      
51206       'DIR', 'MENU', 'OL', 'UL', 'DL',
51207        
51208       'EMBED',  'OBJECT'
51209 ];
51210
51211
51212 Roo.HtmlEditorCore.black = [
51213     //    'embed',  'object', // enable - backend responsiblity to clean thiese
51214         'APPLET', // 
51215         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
51216         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
51217         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
51218         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
51219         //'FONT' // CLEAN LATER..
51220         'COLGROUP', 'COL'   // messy tables.
51221         
51222         
51223 ];
51224 Roo.HtmlEditorCore.clean = [ // ?? needed???
51225      'SCRIPT', 'STYLE', 'TITLE', 'XML'
51226 ];
51227 Roo.HtmlEditorCore.tag_remove = [
51228     'FONT', 'TBODY'  
51229 ];
51230 // attributes..
51231
51232 Roo.HtmlEditorCore.ablack = [
51233     'on'
51234 ];
51235     
51236 Roo.HtmlEditorCore.aclean = [ 
51237     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
51238 ];
51239
51240 // protocols..
51241 Roo.HtmlEditorCore.pwhite= [
51242         'http',  'https',  'mailto'
51243 ];
51244
51245 // white listed style attributes.
51246 Roo.HtmlEditorCore.cwhite= [
51247       //  'text-align', /// default is to allow most things..
51248       
51249          
51250 //        'font-size'//??
51251 ];
51252
51253 // black listed style attributes.
51254 Roo.HtmlEditorCore.cblack= [
51255       //  'font-size' -- this can be set by the project 
51256 ];
51257
51258
51259
51260
51261     //<script type="text/javascript">
51262
51263 /*
51264  * Ext JS Library 1.1.1
51265  * Copyright(c) 2006-2007, Ext JS, LLC.
51266  * Licence LGPL
51267  * 
51268  */
51269  
51270  
51271 Roo.form.HtmlEditor = function(config){
51272     
51273     
51274     
51275     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
51276     
51277     if (!this.toolbars) {
51278         this.toolbars = [];
51279     }
51280     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
51281     
51282     
51283 };
51284
51285 /**
51286  * @class Roo.form.HtmlEditor
51287  * @extends Roo.form.Field
51288  * Provides a lightweight HTML Editor component.
51289  *
51290  * This has been tested on Fireforx / Chrome.. IE may not be so great..
51291  * 
51292  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
51293  * supported by this editor.</b><br/><br/>
51294  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
51295  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
51296  */
51297 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
51298     /**
51299      * @cfg {Boolean} clearUp
51300      */
51301     clearUp : true,
51302       /**
51303      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
51304      */
51305     toolbars : false,
51306    
51307      /**
51308      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
51309      *                        Roo.resizable.
51310      */
51311     resizable : false,
51312      /**
51313      * @cfg {Number} height (in pixels)
51314      */   
51315     height: 300,
51316    /**
51317      * @cfg {Number} width (in pixels)
51318      */   
51319     width: 500,
51320     
51321     /**
51322      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
51323      * 
51324      */
51325     stylesheets: false,
51326     
51327     
51328      /**
51329      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
51330      * 
51331      */
51332     cblack: false,
51333     /**
51334      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
51335      * 
51336      */
51337     cwhite: false,
51338     
51339      /**
51340      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
51341      * 
51342      */
51343     black: false,
51344     /**
51345      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
51346      * 
51347      */
51348     white: false,
51349     /**
51350      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
51351      */
51352     allowComments: false,
51353     /**
51354      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
51355      */
51356     enableBlocks : true,
51357     
51358     /**
51359      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
51360      *         if you are doing an email editor, this probably needs disabling, it's designed
51361      */
51362     autoClean: true,
51363     /**
51364      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
51365      */
51366     bodyCls : '',
51367     /**
51368      * @cfg {String} language default en - language of text (usefull for rtl languages)
51369      * 
51370      */
51371     language: 'en',
51372     
51373      
51374     // id of frame..
51375     frameId: false,
51376     
51377     // private properties
51378     validationEvent : false,
51379     deferHeight: true,
51380     initialized : false,
51381     activated : false,
51382     
51383     onFocus : Roo.emptyFn,
51384     iframePad:3,
51385     hideMode:'offsets',
51386     
51387     actionMode : 'container', // defaults to hiding it...
51388     
51389     defaultAutoCreate : { // modified by initCompnoent..
51390         tag: "textarea",
51391         style:"width:500px;height:300px;",
51392         autocomplete: "new-password"
51393     },
51394
51395     // private
51396     initComponent : function(){
51397         this.addEvents({
51398             /**
51399              * @event initialize
51400              * Fires when the editor is fully initialized (including the iframe)
51401              * @param {HtmlEditor} this
51402              */
51403             initialize: true,
51404             /**
51405              * @event activate
51406              * Fires when the editor is first receives the focus. Any insertion must wait
51407              * until after this event.
51408              * @param {HtmlEditor} this
51409              */
51410             activate: true,
51411              /**
51412              * @event beforesync
51413              * Fires before the textarea is updated with content from the editor iframe. Return false
51414              * to cancel the sync.
51415              * @param {HtmlEditor} this
51416              * @param {String} html
51417              */
51418             beforesync: true,
51419              /**
51420              * @event beforepush
51421              * Fires before the iframe editor is updated with content from the textarea. Return false
51422              * to cancel the push.
51423              * @param {HtmlEditor} this
51424              * @param {String} html
51425              */
51426             beforepush: true,
51427              /**
51428              * @event sync
51429              * Fires when the textarea is updated with content from the editor iframe.
51430              * @param {HtmlEditor} this
51431              * @param {String} html
51432              */
51433             sync: true,
51434              /**
51435              * @event push
51436              * Fires when the iframe editor is updated with content from the textarea.
51437              * @param {HtmlEditor} this
51438              * @param {String} html
51439              */
51440             push: true,
51441              /**
51442              * @event editmodechange
51443              * Fires when the editor switches edit modes
51444              * @param {HtmlEditor} this
51445              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
51446              */
51447             editmodechange: true,
51448             /**
51449              * @event editorevent
51450              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
51451              * @param {HtmlEditor} this
51452              */
51453             editorevent: true,
51454             /**
51455              * @event firstfocus
51456              * Fires when on first focus - needed by toolbars..
51457              * @param {HtmlEditor} this
51458              */
51459             firstfocus: true,
51460             /**
51461              * @event autosave
51462              * Auto save the htmlEditor value as a file into Events
51463              * @param {HtmlEditor} this
51464              */
51465             autosave: true,
51466             /**
51467              * @event savedpreview
51468              * preview the saved version of htmlEditor
51469              * @param {HtmlEditor} this
51470              */
51471             savedpreview: true,
51472             
51473             /**
51474             * @event stylesheetsclick
51475             * Fires when press the Sytlesheets button
51476             * @param {Roo.HtmlEditorCore} this
51477             */
51478             stylesheetsclick: true,
51479             /**
51480             * @event paste
51481             * Fires when press user pastes into the editor
51482             * @param {Roo.HtmlEditorCore} this
51483             */
51484             paste: true 
51485         });
51486         this.defaultAutoCreate =  {
51487             tag: "textarea",
51488             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
51489             autocomplete: "new-password"
51490         };
51491     },
51492
51493     /**
51494      * Protected method that will not generally be called directly. It
51495      * is called when the editor creates its toolbar. Override this method if you need to
51496      * add custom toolbar buttons.
51497      * @param {HtmlEditor} editor
51498      */
51499     createToolbar : function(editor){
51500         Roo.log("create toolbars");
51501         if (!editor.toolbars || !editor.toolbars.length) {
51502             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
51503         }
51504         
51505         for (var i =0 ; i < editor.toolbars.length;i++) {
51506             editor.toolbars[i] = Roo.factory(
51507                     typeof(editor.toolbars[i]) == 'string' ?
51508                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
51509                 Roo.form.HtmlEditor);
51510             editor.toolbars[i].init(editor);
51511         }
51512          
51513         
51514     },
51515     /**
51516      * get the Context selected node
51517      * @returns {DomElement|boolean} selected node if active or false if none
51518      * 
51519      */
51520     getSelectedNode : function()
51521     {
51522         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
51523             return false;
51524         }
51525         return this.toolbars[1].tb.selectedNode;
51526     
51527     },
51528     // private
51529     onRender : function(ct, position)
51530     {
51531         var _t = this;
51532         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
51533         
51534         this.wrap = this.el.wrap({
51535             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
51536         });
51537         
51538         this.editorcore.onRender(ct, position);
51539          
51540         if (this.resizable) {
51541             this.resizeEl = new Roo.Resizable(this.wrap, {
51542                 pinned : true,
51543                 wrap: true,
51544                 dynamic : true,
51545                 minHeight : this.height,
51546                 height: this.height,
51547                 handles : this.resizable,
51548                 width: this.width,
51549                 listeners : {
51550                     resize : function(r, w, h) {
51551                         _t.onResize(w,h); // -something
51552                     }
51553                 }
51554             });
51555             
51556         }
51557         this.createToolbar(this);
51558        
51559         
51560         if(!this.width){
51561             this.setSize(this.wrap.getSize());
51562         }
51563         if (this.resizeEl) {
51564             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
51565             // should trigger onReize..
51566         }
51567         
51568         this.keyNav = new Roo.KeyNav(this.el, {
51569             
51570             "tab" : function(e){
51571                 e.preventDefault();
51572                 
51573                 var value = this.getValue();
51574                 
51575                 var start = this.el.dom.selectionStart;
51576                 var end = this.el.dom.selectionEnd;
51577                 
51578                 if(!e.shiftKey){
51579                     
51580                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
51581                     this.el.dom.setSelectionRange(end + 1, end + 1);
51582                     return;
51583                 }
51584                 
51585                 var f = value.substring(0, start).split("\t");
51586                 
51587                 if(f.pop().length != 0){
51588                     return;
51589                 }
51590                 
51591                 this.setValue(f.join("\t") + value.substring(end));
51592                 this.el.dom.setSelectionRange(start - 1, start - 1);
51593                 
51594             },
51595             
51596             "home" : function(e){
51597                 e.preventDefault();
51598                 
51599                 var curr = this.el.dom.selectionStart;
51600                 var lines = this.getValue().split("\n");
51601                 
51602                 if(!lines.length){
51603                     return;
51604                 }
51605                 
51606                 if(e.ctrlKey){
51607                     this.el.dom.setSelectionRange(0, 0);
51608                     return;
51609                 }
51610                 
51611                 var pos = 0;
51612                 
51613                 for (var i = 0; i < lines.length;i++) {
51614                     pos += lines[i].length;
51615                     
51616                     if(i != 0){
51617                         pos += 1;
51618                     }
51619                     
51620                     if(pos < curr){
51621                         continue;
51622                     }
51623                     
51624                     pos -= lines[i].length;
51625                     
51626                     break;
51627                 }
51628                 
51629                 if(!e.shiftKey){
51630                     this.el.dom.setSelectionRange(pos, pos);
51631                     return;
51632                 }
51633                 
51634                 this.el.dom.selectionStart = pos;
51635                 this.el.dom.selectionEnd = curr;
51636             },
51637             
51638             "end" : function(e){
51639                 e.preventDefault();
51640                 
51641                 var curr = this.el.dom.selectionStart;
51642                 var lines = this.getValue().split("\n");
51643                 
51644                 if(!lines.length){
51645                     return;
51646                 }
51647                 
51648                 if(e.ctrlKey){
51649                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
51650                     return;
51651                 }
51652                 
51653                 var pos = 0;
51654                 
51655                 for (var i = 0; i < lines.length;i++) {
51656                     
51657                     pos += lines[i].length;
51658                     
51659                     if(i != 0){
51660                         pos += 1;
51661                     }
51662                     
51663                     if(pos < curr){
51664                         continue;
51665                     }
51666                     
51667                     break;
51668                 }
51669                 
51670                 if(!e.shiftKey){
51671                     this.el.dom.setSelectionRange(pos, pos);
51672                     return;
51673                 }
51674                 
51675                 this.el.dom.selectionStart = curr;
51676                 this.el.dom.selectionEnd = pos;
51677             },
51678
51679             scope : this,
51680
51681             doRelay : function(foo, bar, hname){
51682                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
51683             },
51684
51685             forceKeyDown: true
51686         });
51687         
51688 //        if(this.autosave && this.w){
51689 //            this.autoSaveFn = setInterval(this.autosave, 1000);
51690 //        }
51691     },
51692
51693     // private
51694     onResize : function(w, h)
51695     {
51696         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
51697         var ew = false;
51698         var eh = false;
51699         
51700         if(this.el ){
51701             if(typeof w == 'number'){
51702                 var aw = w - this.wrap.getFrameWidth('lr');
51703                 this.el.setWidth(this.adjustWidth('textarea', aw));
51704                 ew = aw;
51705             }
51706             if(typeof h == 'number'){
51707                 var tbh = 0;
51708                 for (var i =0; i < this.toolbars.length;i++) {
51709                     // fixme - ask toolbars for heights?
51710                     tbh += this.toolbars[i].tb.el.getHeight();
51711                     if (this.toolbars[i].footer) {
51712                         tbh += this.toolbars[i].footer.el.getHeight();
51713                     }
51714                 }
51715                 
51716                 
51717                 
51718                 
51719                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
51720                 ah -= 5; // knock a few pixes off for look..
51721 //                Roo.log(ah);
51722                 this.el.setHeight(this.adjustWidth('textarea', ah));
51723                 var eh = ah;
51724             }
51725         }
51726         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
51727         this.editorcore.onResize(ew,eh);
51728         
51729     },
51730
51731     /**
51732      * Toggles the editor between standard and source edit mode.
51733      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
51734      */
51735     toggleSourceEdit : function(sourceEditMode)
51736     {
51737         this.editorcore.toggleSourceEdit(sourceEditMode);
51738         
51739         if(this.editorcore.sourceEditMode){
51740             Roo.log('editor - showing textarea');
51741             
51742 //            Roo.log('in');
51743 //            Roo.log(this.syncValue());
51744             this.editorcore.syncValue();
51745             this.el.removeClass('x-hidden');
51746             this.el.dom.removeAttribute('tabIndex');
51747             this.el.focus();
51748             this.el.dom.scrollTop = 0;
51749             
51750             
51751             for (var i = 0; i < this.toolbars.length; i++) {
51752                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51753                     this.toolbars[i].tb.hide();
51754                     this.toolbars[i].footer.hide();
51755                 }
51756             }
51757             
51758         }else{
51759             Roo.log('editor - hiding textarea');
51760 //            Roo.log('out')
51761 //            Roo.log(this.pushValue()); 
51762             this.editorcore.pushValue();
51763             
51764             this.el.addClass('x-hidden');
51765             this.el.dom.setAttribute('tabIndex', -1);
51766             
51767             for (var i = 0; i < this.toolbars.length; i++) {
51768                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51769                     this.toolbars[i].tb.show();
51770                     this.toolbars[i].footer.show();
51771                 }
51772             }
51773             
51774             //this.deferFocus();
51775         }
51776         
51777         this.setSize(this.wrap.getSize());
51778         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
51779         
51780         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
51781     },
51782  
51783     // private (for BoxComponent)
51784     adjustSize : Roo.BoxComponent.prototype.adjustSize,
51785
51786     // private (for BoxComponent)
51787     getResizeEl : function(){
51788         return this.wrap;
51789     },
51790
51791     // private (for BoxComponent)
51792     getPositionEl : function(){
51793         return this.wrap;
51794     },
51795
51796     // private
51797     initEvents : function(){
51798         this.originalValue = this.getValue();
51799     },
51800
51801     /**
51802      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51803      * @method
51804      */
51805     markInvalid : Roo.emptyFn,
51806     /**
51807      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51808      * @method
51809      */
51810     clearInvalid : Roo.emptyFn,
51811
51812     setValue : function(v){
51813         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
51814         this.editorcore.pushValue();
51815     },
51816
51817     /**
51818      * update the language in the body - really done by core
51819      * @param {String} language - eg. en / ar / zh-CN etc..
51820      */
51821     updateLanguage : function(lang)
51822     {
51823         this.language = lang;
51824         this.editorcore.language = lang;
51825         this.editorcore.updateLanguage();
51826      
51827     },
51828     // private
51829     deferFocus : function(){
51830         this.focus.defer(10, this);
51831     },
51832
51833     // doc'ed in Field
51834     focus : function(){
51835         this.editorcore.focus();
51836         
51837     },
51838       
51839
51840     // private
51841     onDestroy : function(){
51842         
51843         
51844         
51845         if(this.rendered){
51846             
51847             for (var i =0; i < this.toolbars.length;i++) {
51848                 // fixme - ask toolbars for heights?
51849                 this.toolbars[i].onDestroy();
51850             }
51851             
51852             this.wrap.dom.innerHTML = '';
51853             this.wrap.remove();
51854         }
51855     },
51856
51857     // private
51858     onFirstFocus : function(){
51859         //Roo.log("onFirstFocus");
51860         this.editorcore.onFirstFocus();
51861          for (var i =0; i < this.toolbars.length;i++) {
51862             this.toolbars[i].onFirstFocus();
51863         }
51864         
51865     },
51866     
51867     // private
51868     syncValue : function()
51869     {
51870         this.editorcore.syncValue();
51871     },
51872     
51873     pushValue : function()
51874     {
51875         this.editorcore.pushValue();
51876     },
51877     
51878     setStylesheets : function(stylesheets)
51879     {
51880         this.editorcore.setStylesheets(stylesheets);
51881     },
51882     
51883     removeStylesheets : function()
51884     {
51885         this.editorcore.removeStylesheets();
51886     }
51887      
51888     
51889     // hide stuff that is not compatible
51890     /**
51891      * @event blur
51892      * @hide
51893      */
51894     /**
51895      * @event change
51896      * @hide
51897      */
51898     /**
51899      * @event focus
51900      * @hide
51901      */
51902     /**
51903      * @event specialkey
51904      * @hide
51905      */
51906     /**
51907      * @cfg {String} fieldClass @hide
51908      */
51909     /**
51910      * @cfg {String} focusClass @hide
51911      */
51912     /**
51913      * @cfg {String} autoCreate @hide
51914      */
51915     /**
51916      * @cfg {String} inputType @hide
51917      */
51918     /**
51919      * @cfg {String} invalidClass @hide
51920      */
51921     /**
51922      * @cfg {String} invalidText @hide
51923      */
51924     /**
51925      * @cfg {String} msgFx @hide
51926      */
51927     /**
51928      * @cfg {String} validateOnBlur @hide
51929      */
51930 });
51931  
51932     /*
51933  * Based on
51934  * Ext JS Library 1.1.1
51935  * Copyright(c) 2006-2007, Ext JS, LLC.
51936  *  
51937  
51938  */
51939
51940 /**
51941  * @class Roo.form.HtmlEditor.ToolbarStandard
51942  * Basic Toolbar
51943
51944  * Usage:
51945  *
51946  new Roo.form.HtmlEditor({
51947     ....
51948     toolbars : [
51949         new Roo.form.HtmlEditorToolbar1({
51950             disable : { fonts: 1 , format: 1, ..., ... , ...],
51951             btns : [ .... ]
51952         })
51953     }
51954      
51955  * 
51956  * @cfg {Object} disable List of elements to disable..
51957  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
51958  * 
51959  * 
51960  * NEEDS Extra CSS? 
51961  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
51962  */
51963  
51964 Roo.form.HtmlEditor.ToolbarStandard = function(config)
51965 {
51966     
51967     Roo.apply(this, config);
51968     
51969     // default disabled, based on 'good practice'..
51970     this.disable = this.disable || {};
51971     Roo.applyIf(this.disable, {
51972         fontSize : true,
51973         colors : true,
51974         specialElements : true
51975     });
51976     
51977     
51978     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
51979     // dont call parent... till later.
51980 }
51981
51982 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
51983     
51984     tb: false,
51985     
51986     rendered: false,
51987     
51988     editor : false,
51989     editorcore : false,
51990     /**
51991      * @cfg {Object} disable  List of toolbar elements to disable
51992          
51993      */
51994     disable : false,
51995     
51996     
51997      /**
51998      * @cfg {String} createLinkText The default text for the create link prompt
51999      */
52000     createLinkText : 'Please enter the URL for the link:',
52001     /**
52002      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
52003      */
52004     defaultLinkValue : 'http:/'+'/',
52005    
52006     
52007       /**
52008      * @cfg {Array} fontFamilies An array of available font families
52009      */
52010     fontFamilies : [
52011         'Arial',
52012         'Courier New',
52013         'Tahoma',
52014         'Times New Roman',
52015         'Verdana'
52016     ],
52017     
52018     specialChars : [
52019            "&#169;",
52020           "&#174;",     
52021           "&#8482;",    
52022           "&#163;" ,    
52023          // "&#8212;",    
52024           "&#8230;",    
52025           "&#247;" ,    
52026         //  "&#225;" ,     ?? a acute?
52027            "&#8364;"    , //Euro
52028        //   "&#8220;"    ,
52029         //  "&#8221;"    ,
52030         //  "&#8226;"    ,
52031           "&#176;"  //   , // degrees
52032
52033          // "&#233;"     , // e ecute
52034          // "&#250;"     , // u ecute?
52035     ],
52036     
52037     specialElements : [
52038         {
52039             text: "Insert Table",
52040             xtype: 'MenuItem',
52041             xns : Roo.Menu,
52042             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
52043                 
52044         },
52045         {    
52046             text: "Insert Image",
52047             xtype: 'MenuItem',
52048             xns : Roo.Menu,
52049             ihtml : '<img src="about:blank"/>'
52050             
52051         }
52052         
52053          
52054     ],
52055     
52056     
52057     inputElements : [ 
52058             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
52059             "input:submit", "input:button", "select", "textarea", "label" ],
52060     formats : [
52061         ["p"] ,  
52062         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
52063         ["pre"],[ "code"], 
52064         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
52065         ['div'],['span'],
52066         ['sup'],['sub']
52067     ],
52068     
52069     cleanStyles : [
52070         "font-size"
52071     ],
52072      /**
52073      * @cfg {String} defaultFont default font to use.
52074      */
52075     defaultFont: 'tahoma',
52076    
52077     fontSelect : false,
52078     
52079     
52080     formatCombo : false,
52081     
52082     init : function(editor)
52083     {
52084         this.editor = editor;
52085         this.editorcore = editor.editorcore ? editor.editorcore : editor;
52086         var editorcore = this.editorcore;
52087         
52088         var _t = this;
52089         
52090         var fid = editorcore.frameId;
52091         var etb = this;
52092         function btn(id, toggle, handler){
52093             var xid = fid + '-'+ id ;
52094             return {
52095                 id : xid,
52096                 cmd : id,
52097                 cls : 'x-btn-icon x-edit-'+id,
52098                 enableToggle:toggle !== false,
52099                 scope: _t, // was editor...
52100                 handler:handler||_t.relayBtnCmd,
52101                 clickEvent:'mousedown',
52102                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52103                 tabIndex:-1
52104             };
52105         }
52106         
52107         
52108         
52109         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52110         this.tb = tb;
52111          // stop form submits
52112         tb.el.on('click', function(e){
52113             e.preventDefault(); // what does this do?
52114         });
52115
52116         if(!this.disable.font) { // && !Roo.isSafari){
52117             /* why no safari for fonts 
52118             editor.fontSelect = tb.el.createChild({
52119                 tag:'select',
52120                 tabIndex: -1,
52121                 cls:'x-font-select',
52122                 html: this.createFontOptions()
52123             });
52124             
52125             editor.fontSelect.on('change', function(){
52126                 var font = editor.fontSelect.dom.value;
52127                 editor.relayCmd('fontname', font);
52128                 editor.deferFocus();
52129             }, editor);
52130             
52131             tb.add(
52132                 editor.fontSelect.dom,
52133                 '-'
52134             );
52135             */
52136             
52137         };
52138         if(!this.disable.formats){
52139             this.formatCombo = new Roo.form.ComboBox({
52140                 store: new Roo.data.SimpleStore({
52141                     id : 'tag',
52142                     fields: ['tag'],
52143                     data : this.formats // from states.js
52144                 }),
52145                 blockFocus : true,
52146                 name : '',
52147                 //autoCreate : {tag: "div",  size: "20"},
52148                 displayField:'tag',
52149                 typeAhead: false,
52150                 mode: 'local',
52151                 editable : false,
52152                 triggerAction: 'all',
52153                 emptyText:'Add tag',
52154                 selectOnFocus:true,
52155                 width:135,
52156                 listeners : {
52157                     'select': function(c, r, i) {
52158                         editorcore.insertTag(r.get('tag'));
52159                         editor.focus();
52160                     }
52161                 }
52162
52163             });
52164             tb.addField(this.formatCombo);
52165             
52166         }
52167         
52168         if(!this.disable.format){
52169             tb.add(
52170                 btn('bold'),
52171                 btn('italic'),
52172                 btn('underline'),
52173                 btn('strikethrough')
52174             );
52175         };
52176         if(!this.disable.fontSize){
52177             tb.add(
52178                 '-',
52179                 
52180                 
52181                 btn('increasefontsize', false, editorcore.adjustFont),
52182                 btn('decreasefontsize', false, editorcore.adjustFont)
52183             );
52184         };
52185         
52186         
52187         if(!this.disable.colors){
52188             tb.add(
52189                 '-', {
52190                     id:editorcore.frameId +'-forecolor',
52191                     cls:'x-btn-icon x-edit-forecolor',
52192                     clickEvent:'mousedown',
52193                     tooltip: this.buttonTips['forecolor'] || undefined,
52194                     tabIndex:-1,
52195                     menu : new Roo.menu.ColorMenu({
52196                         allowReselect: true,
52197                         focus: Roo.emptyFn,
52198                         value:'000000',
52199                         plain:true,
52200                         selectHandler: function(cp, color){
52201                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
52202                             editor.deferFocus();
52203                         },
52204                         scope: editorcore,
52205                         clickEvent:'mousedown'
52206                     })
52207                 }, {
52208                     id:editorcore.frameId +'backcolor',
52209                     cls:'x-btn-icon x-edit-backcolor',
52210                     clickEvent:'mousedown',
52211                     tooltip: this.buttonTips['backcolor'] || undefined,
52212                     tabIndex:-1,
52213                     menu : new Roo.menu.ColorMenu({
52214                         focus: Roo.emptyFn,
52215                         value:'FFFFFF',
52216                         plain:true,
52217                         allowReselect: true,
52218                         selectHandler: function(cp, color){
52219                             if(Roo.isGecko){
52220                                 editorcore.execCmd('useCSS', false);
52221                                 editorcore.execCmd('hilitecolor', color);
52222                                 editorcore.execCmd('useCSS', true);
52223                                 editor.deferFocus();
52224                             }else{
52225                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
52226                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
52227                                 editor.deferFocus();
52228                             }
52229                         },
52230                         scope:editorcore,
52231                         clickEvent:'mousedown'
52232                     })
52233                 }
52234             );
52235         };
52236         // now add all the items...
52237         
52238
52239         if(!this.disable.alignments){
52240             tb.add(
52241                 '-',
52242                 btn('justifyleft'),
52243                 btn('justifycenter'),
52244                 btn('justifyright')
52245             );
52246         };
52247
52248         //if(!Roo.isSafari){
52249             if(!this.disable.links){
52250                 tb.add(
52251                     '-',
52252                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
52253                 );
52254             };
52255
52256             if(!this.disable.lists){
52257                 tb.add(
52258                     '-',
52259                     btn('insertorderedlist'),
52260                     btn('insertunorderedlist')
52261                 );
52262             }
52263             if(!this.disable.sourceEdit){
52264                 tb.add(
52265                     '-',
52266                     btn('sourceedit', true, function(btn){
52267                         this.toggleSourceEdit(btn.pressed);
52268                     })
52269                 );
52270             }
52271         //}
52272         
52273         var smenu = { };
52274         // special menu.. - needs to be tidied up..
52275         if (!this.disable.special) {
52276             smenu = {
52277                 text: "&#169;",
52278                 cls: 'x-edit-none',
52279                 
52280                 menu : {
52281                     items : []
52282                 }
52283             };
52284             for (var i =0; i < this.specialChars.length; i++) {
52285                 smenu.menu.items.push({
52286                     
52287                     html: this.specialChars[i],
52288                     handler: function(a,b) {
52289                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
52290                         //editor.insertAtCursor(a.html);
52291                         
52292                     },
52293                     tabIndex:-1
52294                 });
52295             }
52296             
52297             
52298             tb.add(smenu);
52299             
52300             
52301         }
52302         
52303         var cmenu = { };
52304         if (!this.disable.cleanStyles) {
52305             cmenu = {
52306                 cls: 'x-btn-icon x-btn-clear',
52307                 
52308                 menu : {
52309                     items : []
52310                 }
52311             };
52312             for (var i =0; i < this.cleanStyles.length; i++) {
52313                 cmenu.menu.items.push({
52314                     actiontype : this.cleanStyles[i],
52315                     html: 'Remove ' + this.cleanStyles[i],
52316                     handler: function(a,b) {
52317 //                        Roo.log(a);
52318 //                        Roo.log(b);
52319                         var c = Roo.get(editorcore.doc.body);
52320                         c.select('[style]').each(function(s) {
52321                             s.dom.style.removeProperty(a.actiontype);
52322                         });
52323                         editorcore.syncValue();
52324                     },
52325                     tabIndex:-1
52326                 });
52327             }
52328             cmenu.menu.items.push({
52329                 actiontype : 'tablewidths',
52330                 html: 'Remove Table Widths',
52331                 handler: function(a,b) {
52332                     editorcore.cleanTableWidths();
52333                     editorcore.syncValue();
52334                 },
52335                 tabIndex:-1
52336             });
52337             cmenu.menu.items.push({
52338                 actiontype : 'word',
52339                 html: 'Remove MS Word Formating',
52340                 handler: function(a,b) {
52341                     editorcore.cleanWord();
52342                     editorcore.syncValue();
52343                 },
52344                 tabIndex:-1
52345             });
52346             
52347             cmenu.menu.items.push({
52348                 actiontype : 'all',
52349                 html: 'Remove All Styles',
52350                 handler: function(a,b) {
52351                     
52352                     var c = Roo.get(editorcore.doc.body);
52353                     c.select('[style]').each(function(s) {
52354                         s.dom.removeAttribute('style');
52355                     });
52356                     editorcore.syncValue();
52357                 },
52358                 tabIndex:-1
52359             });
52360             
52361             cmenu.menu.items.push({
52362                 actiontype : 'all',
52363                 html: 'Remove All CSS Classes',
52364                 handler: function(a,b) {
52365                     
52366                     var c = Roo.get(editorcore.doc.body);
52367                     c.select('[class]').each(function(s) {
52368                         s.dom.removeAttribute('class');
52369                     });
52370                     editorcore.cleanWord();
52371                     editorcore.syncValue();
52372                 },
52373                 tabIndex:-1
52374             });
52375             
52376              cmenu.menu.items.push({
52377                 actiontype : 'tidy',
52378                 html: 'Tidy HTML Source',
52379                 handler: function(a,b) {
52380                     new Roo.htmleditor.Tidy(editorcore.doc.body);
52381                     editorcore.syncValue();
52382                 },
52383                 tabIndex:-1
52384             });
52385             
52386             
52387             tb.add(cmenu);
52388         }
52389          
52390         if (!this.disable.specialElements) {
52391             var semenu = {
52392                 text: "Other;",
52393                 cls: 'x-edit-none',
52394                 menu : {
52395                     items : []
52396                 }
52397             };
52398             for (var i =0; i < this.specialElements.length; i++) {
52399                 semenu.menu.items.push(
52400                     Roo.apply({ 
52401                         handler: function(a,b) {
52402                             editor.insertAtCursor(this.ihtml);
52403                         }
52404                     }, this.specialElements[i])
52405                 );
52406                     
52407             }
52408             
52409             tb.add(semenu);
52410             
52411             
52412         }
52413          
52414         
52415         if (this.btns) {
52416             for(var i =0; i< this.btns.length;i++) {
52417                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
52418                 b.cls =  'x-edit-none';
52419                 
52420                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
52421                     b.cls += ' x-init-enable';
52422                 }
52423                 
52424                 b.scope = editorcore;
52425                 tb.add(b);
52426             }
52427         
52428         }
52429         
52430         
52431         
52432         // disable everything...
52433         
52434         this.tb.items.each(function(item){
52435             
52436            if(
52437                 item.id != editorcore.frameId+ '-sourceedit' && 
52438                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
52439             ){
52440                 
52441                 item.disable();
52442             }
52443         });
52444         this.rendered = true;
52445         
52446         // the all the btns;
52447         editor.on('editorevent', this.updateToolbar, this);
52448         // other toolbars need to implement this..
52449         //editor.on('editmodechange', this.updateToolbar, this);
52450     },
52451     
52452     
52453     relayBtnCmd : function(btn) {
52454         this.editorcore.relayCmd(btn.cmd);
52455     },
52456     // private used internally
52457     createLink : function(){
52458         //Roo.log("create link?");
52459         var ec = this.editorcore;
52460         var ar = ec.getAllAncestors();
52461         var n = false;
52462         for(var i = 0;i< ar.length;i++) {
52463             if (ar[i] && ar[i].nodeName == 'A') {
52464                 n = ar[i];
52465                 break;
52466             }
52467         }
52468         
52469         (function() {
52470             
52471             Roo.MessageBox.show({
52472                 title : "Add / Edit Link URL",
52473                 msg : "Enter the url for the link",
52474                 buttons: Roo.MessageBox.OKCANCEL,
52475                 fn: function(btn, url){
52476                     if (btn != 'ok') {
52477                         return;
52478                     }
52479                     if(url && url != 'http:/'+'/'){
52480                         if (n) {
52481                             n.setAttribute('href', url);
52482                         } else {
52483                             ec.relayCmd('createlink', url);
52484                         }
52485                     }
52486                 },
52487                 minWidth:250,
52488                 prompt:true,
52489                 //multiline: multiline,
52490                 modal : true,
52491                 value :  n  ? n.getAttribute('href') : '' 
52492             });
52493             
52494              
52495         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
52496         
52497     },
52498
52499     
52500     /**
52501      * Protected method that will not generally be called directly. It triggers
52502      * a toolbar update by reading the markup state of the current selection in the editor.
52503      */
52504     updateToolbar: function(){
52505
52506         if(!this.editorcore.activated){
52507             this.editor.onFirstFocus();
52508             return;
52509         }
52510
52511         var btns = this.tb.items.map, 
52512             doc = this.editorcore.doc,
52513             frameId = this.editorcore.frameId;
52514
52515         if(!this.disable.font && !Roo.isSafari){
52516             /*
52517             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
52518             if(name != this.fontSelect.dom.value){
52519                 this.fontSelect.dom.value = name;
52520             }
52521             */
52522         }
52523         if(!this.disable.format){
52524             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
52525             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
52526             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
52527             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
52528         }
52529         if(!this.disable.alignments){
52530             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
52531             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
52532             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
52533         }
52534         if(!Roo.isSafari && !this.disable.lists){
52535             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
52536             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
52537         }
52538         
52539         var ans = this.editorcore.getAllAncestors();
52540         if (this.formatCombo) {
52541             
52542             
52543             var store = this.formatCombo.store;
52544             this.formatCombo.setValue("");
52545             for (var i =0; i < ans.length;i++) {
52546                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
52547                     // select it..
52548                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
52549                     break;
52550                 }
52551             }
52552         }
52553         
52554         
52555         
52556         // hides menus... - so this cant be on a menu...
52557         Roo.menu.MenuMgr.hideAll();
52558
52559         //this.editorsyncValue();
52560     },
52561    
52562     
52563     createFontOptions : function(){
52564         var buf = [], fs = this.fontFamilies, ff, lc;
52565         
52566         
52567         
52568         for(var i = 0, len = fs.length; i< len; i++){
52569             ff = fs[i];
52570             lc = ff.toLowerCase();
52571             buf.push(
52572                 '<option value="',lc,'" style="font-family:',ff,';"',
52573                     (this.defaultFont == lc ? ' selected="true">' : '>'),
52574                     ff,
52575                 '</option>'
52576             );
52577         }
52578         return buf.join('');
52579     },
52580     
52581     toggleSourceEdit : function(sourceEditMode){
52582         
52583         Roo.log("toolbar toogle");
52584         if(sourceEditMode === undefined){
52585             sourceEditMode = !this.sourceEditMode;
52586         }
52587         this.sourceEditMode = sourceEditMode === true;
52588         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
52589         // just toggle the button?
52590         if(btn.pressed !== this.sourceEditMode){
52591             btn.toggle(this.sourceEditMode);
52592             return;
52593         }
52594         
52595         if(sourceEditMode){
52596             Roo.log("disabling buttons");
52597             this.tb.items.each(function(item){
52598                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
52599                     item.disable();
52600                 }
52601             });
52602           
52603         }else{
52604             Roo.log("enabling buttons");
52605             if(this.editorcore.initialized){
52606                 this.tb.items.each(function(item){
52607                     item.enable();
52608                 });
52609                 // initialize 'blocks'
52610                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
52611                     Roo.htmleditor.Block.factory(e).updateElement(e);
52612                 },this);
52613             
52614             }
52615             
52616         }
52617         Roo.log("calling toggole on editor");
52618         // tell the editor that it's been pressed..
52619         this.editor.toggleSourceEdit(sourceEditMode);
52620        
52621     },
52622      /**
52623      * Object collection of toolbar tooltips for the buttons in the editor. The key
52624      * is the command id associated with that button and the value is a valid QuickTips object.
52625      * For example:
52626 <pre><code>
52627 {
52628     bold : {
52629         title: 'Bold (Ctrl+B)',
52630         text: 'Make the selected text bold.',
52631         cls: 'x-html-editor-tip'
52632     },
52633     italic : {
52634         title: 'Italic (Ctrl+I)',
52635         text: 'Make the selected text italic.',
52636         cls: 'x-html-editor-tip'
52637     },
52638     ...
52639 </code></pre>
52640     * @type Object
52641      */
52642     buttonTips : {
52643         bold : {
52644             title: 'Bold (Ctrl+B)',
52645             text: 'Make the selected text bold.',
52646             cls: 'x-html-editor-tip'
52647         },
52648         italic : {
52649             title: 'Italic (Ctrl+I)',
52650             text: 'Make the selected text italic.',
52651             cls: 'x-html-editor-tip'
52652         },
52653         underline : {
52654             title: 'Underline (Ctrl+U)',
52655             text: 'Underline the selected text.',
52656             cls: 'x-html-editor-tip'
52657         },
52658         strikethrough : {
52659             title: 'Strikethrough',
52660             text: 'Strikethrough the selected text.',
52661             cls: 'x-html-editor-tip'
52662         },
52663         increasefontsize : {
52664             title: 'Grow Text',
52665             text: 'Increase the font size.',
52666             cls: 'x-html-editor-tip'
52667         },
52668         decreasefontsize : {
52669             title: 'Shrink Text',
52670             text: 'Decrease the font size.',
52671             cls: 'x-html-editor-tip'
52672         },
52673         backcolor : {
52674             title: 'Text Highlight Color',
52675             text: 'Change the background color of the selected text.',
52676             cls: 'x-html-editor-tip'
52677         },
52678         forecolor : {
52679             title: 'Font Color',
52680             text: 'Change the color of the selected text.',
52681             cls: 'x-html-editor-tip'
52682         },
52683         justifyleft : {
52684             title: 'Align Text Left',
52685             text: 'Align text to the left.',
52686             cls: 'x-html-editor-tip'
52687         },
52688         justifycenter : {
52689             title: 'Center Text',
52690             text: 'Center text in the editor.',
52691             cls: 'x-html-editor-tip'
52692         },
52693         justifyright : {
52694             title: 'Align Text Right',
52695             text: 'Align text to the right.',
52696             cls: 'x-html-editor-tip'
52697         },
52698         insertunorderedlist : {
52699             title: 'Bullet List',
52700             text: 'Start a bulleted list.',
52701             cls: 'x-html-editor-tip'
52702         },
52703         insertorderedlist : {
52704             title: 'Numbered List',
52705             text: 'Start a numbered list.',
52706             cls: 'x-html-editor-tip'
52707         },
52708         createlink : {
52709             title: 'Hyperlink',
52710             text: 'Make the selected text a hyperlink.',
52711             cls: 'x-html-editor-tip'
52712         },
52713         sourceedit : {
52714             title: 'Source Edit',
52715             text: 'Switch to source editing mode.',
52716             cls: 'x-html-editor-tip'
52717         }
52718     },
52719     // private
52720     onDestroy : function(){
52721         if(this.rendered){
52722             
52723             this.tb.items.each(function(item){
52724                 if(item.menu){
52725                     item.menu.removeAll();
52726                     if(item.menu.el){
52727                         item.menu.el.destroy();
52728                     }
52729                 }
52730                 item.destroy();
52731             });
52732              
52733         }
52734     },
52735     onFirstFocus: function() {
52736         this.tb.items.each(function(item){
52737            item.enable();
52738         });
52739     }
52740 };
52741
52742
52743
52744
52745 // <script type="text/javascript">
52746 /*
52747  * Based on
52748  * Ext JS Library 1.1.1
52749  * Copyright(c) 2006-2007, Ext JS, LLC.
52750  *  
52751  
52752  */
52753
52754  
52755 /**
52756  * @class Roo.form.HtmlEditor.ToolbarContext
52757  * Context Toolbar
52758  * 
52759  * Usage:
52760  *
52761  new Roo.form.HtmlEditor({
52762     ....
52763     toolbars : [
52764         { xtype: 'ToolbarStandard', styles : {} }
52765         { xtype: 'ToolbarContext', disable : {} }
52766     ]
52767 })
52768
52769      
52770  * 
52771  * @config : {Object} disable List of elements to disable.. (not done yet.)
52772  * @config : {Object} styles  Map of styles available.
52773  * 
52774  */
52775
52776 Roo.form.HtmlEditor.ToolbarContext = function(config)
52777 {
52778     
52779     Roo.apply(this, config);
52780     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52781     // dont call parent... till later.
52782     this.styles = this.styles || {};
52783 }
52784
52785  
52786
52787 Roo.form.HtmlEditor.ToolbarContext.types = {
52788     'IMG' : [
52789         {
52790             name : 'width',
52791             title: "Width",
52792             width: 40
52793         },
52794         {
52795             name : 'height',
52796             title: "Height",
52797             width: 40
52798         },
52799         {
52800             name : 'align',
52801             title: "Align",
52802             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52803             width : 80
52804             
52805         },
52806         {
52807             name : 'border',
52808             title: "Border",
52809             width: 40
52810         },
52811         {
52812             name : 'alt',
52813             title: "Alt",
52814             width: 120
52815         },
52816         {
52817             name : 'src',
52818             title: "Src",
52819             width: 220
52820         }
52821         
52822     ],
52823     
52824     'FIGURE' : [
52825         {
52826             name : 'align',
52827             title: "Align",
52828             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52829             width : 80  
52830         }
52831     ],
52832     'A' : [
52833         {
52834             name : 'name',
52835             title: "Name",
52836             width: 50
52837         },
52838         {
52839             name : 'target',
52840             title: "Target",
52841             width: 120
52842         },
52843         {
52844             name : 'href',
52845             title: "Href",
52846             width: 220
52847         } // border?
52848         
52849     ],
52850     
52851     'INPUT' : [
52852         {
52853             name : 'name',
52854             title: "name",
52855             width: 120
52856         },
52857         {
52858             name : 'value',
52859             title: "Value",
52860             width: 120
52861         },
52862         {
52863             name : 'width',
52864             title: "Width",
52865             width: 40
52866         }
52867     ],
52868     'LABEL' : [
52869          {
52870             name : 'for',
52871             title: "For",
52872             width: 120
52873         }
52874     ],
52875     'TEXTAREA' : [
52876         {
52877             name : 'name',
52878             title: "name",
52879             width: 120
52880         },
52881         {
52882             name : 'rows',
52883             title: "Rows",
52884             width: 20
52885         },
52886         {
52887             name : 'cols',
52888             title: "Cols",
52889             width: 20
52890         }
52891     ],
52892     'SELECT' : [
52893         {
52894             name : 'name',
52895             title: "name",
52896             width: 120
52897         },
52898         {
52899             name : 'selectoptions',
52900             title: "Options",
52901             width: 200
52902         }
52903     ],
52904     
52905     // should we really allow this??
52906     // should this just be 
52907     'BODY' : [
52908         
52909         {
52910             name : 'title',
52911             title: "Title",
52912             width: 200,
52913             disabled : true
52914         }
52915     ],
52916  
52917     '*' : [
52918         // empty.
52919     ]
52920
52921 };
52922
52923 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
52924 Roo.form.HtmlEditor.ToolbarContext.stores = false;
52925
52926 Roo.form.HtmlEditor.ToolbarContext.options = {
52927         'font-family'  : [ 
52928                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
52929                 [ 'Courier New', 'Courier New'],
52930                 [ 'Tahoma', 'Tahoma'],
52931                 [ 'Times New Roman,serif', 'Times'],
52932                 [ 'Verdana','Verdana' ]
52933         ]
52934 };
52935
52936 // fixme - these need to be configurable..
52937  
52938
52939 //Roo.form.HtmlEditor.ToolbarContext.types
52940
52941
52942 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
52943     
52944     tb: false,
52945     
52946     rendered: false,
52947     
52948     editor : false,
52949     editorcore : false,
52950     /**
52951      * @cfg {Object} disable  List of toolbar elements to disable
52952          
52953      */
52954     disable : false,
52955     /**
52956      * @cfg {Object} styles List of styles 
52957      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
52958      *
52959      * These must be defined in the page, so they get rendered correctly..
52960      * .headline { }
52961      * TD.underline { }
52962      * 
52963      */
52964     styles : false,
52965     
52966     options: false,
52967     
52968     toolbars : false,
52969     
52970     init : function(editor)
52971     {
52972         this.editor = editor;
52973         this.editorcore = editor.editorcore ? editor.editorcore : editor;
52974         var editorcore = this.editorcore;
52975         
52976         var fid = editorcore.frameId;
52977         var etb = this;
52978         function btn(id, toggle, handler){
52979             var xid = fid + '-'+ id ;
52980             return {
52981                 id : xid,
52982                 cmd : id,
52983                 cls : 'x-btn-icon x-edit-'+id,
52984                 enableToggle:toggle !== false,
52985                 scope: editorcore, // was editor...
52986                 handler:handler||editorcore.relayBtnCmd,
52987                 clickEvent:'mousedown',
52988                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52989                 tabIndex:-1
52990             };
52991         }
52992         // create a new element.
52993         var wdiv = editor.wrap.createChild({
52994                 tag: 'div'
52995             }, editor.wrap.dom.firstChild.nextSibling, true);
52996         
52997         // can we do this more than once??
52998         
52999          // stop form submits
53000       
53001  
53002         // disable everything...
53003         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
53004         this.toolbars = {};
53005         // block toolbars are built in updateToolbar when needed.
53006         for (var i in  ty) {
53007             
53008             this.toolbars[i] = this.buildToolbar(ty[i],i);
53009         }
53010         this.tb = this.toolbars.BODY;
53011         this.tb.el.show();
53012         this.buildFooter();
53013         this.footer.show();
53014         editor.on('hide', function( ) { this.footer.hide() }, this);
53015         editor.on('show', function( ) { this.footer.show() }, this);
53016         
53017          
53018         this.rendered = true;
53019         
53020         // the all the btns;
53021         editor.on('editorevent', this.updateToolbar, this);
53022         // other toolbars need to implement this..
53023         //editor.on('editmodechange', this.updateToolbar, this);
53024     },
53025     
53026     
53027     
53028     /**
53029      * Protected method that will not generally be called directly. It triggers
53030      * a toolbar update by reading the markup state of the current selection in the editor.
53031      *
53032      * Note you can force an update by calling on('editorevent', scope, false)
53033      */
53034     updateToolbar: function(editor ,ev, sel)
53035     {
53036         
53037         if (ev) {
53038             ev.stopEvent(); // se if we can stop this looping with mutiple events.
53039         }
53040         
53041         //Roo.log(ev);
53042         // capture mouse up - this is handy for selecting images..
53043         // perhaps should go somewhere else...
53044         if(!this.editorcore.activated){
53045              this.editor.onFirstFocus();
53046             return;
53047         }
53048         //Roo.log(ev ? ev.target : 'NOTARGET');
53049         
53050         
53051         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
53052         // selectNode - might want to handle IE?
53053         
53054         
53055         
53056         if (ev &&
53057             (ev.type == 'mouseup' || ev.type == 'click' ) &&
53058             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
53059             // they have click on an image...
53060             // let's see if we can change the selection...
53061             sel = ev.target;
53062             
53063             // this triggers looping?
53064             //this.editorcore.selectNode(sel);
53065              
53066         }
53067         
53068         // this forces an id..
53069         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
53070              e.classList.remove('roo-ed-selection');
53071         });
53072         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
53073         //Roo.get(node).addClass('roo-ed-selection');
53074       
53075         //var updateFooter = sel ? false : true; 
53076         
53077         
53078         var ans = this.editorcore.getAllAncestors();
53079         
53080         // pick
53081         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
53082         
53083         if (!sel) { 
53084             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
53085             sel = sel ? sel : this.editorcore.doc.body;
53086             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
53087             
53088         }
53089         
53090         var tn = sel.tagName.toUpperCase();
53091         var lastSel = this.tb.selectedNode;
53092         this.tb.selectedNode = sel;
53093         var left_label = tn;
53094         
53095         // ok see if we are editing a block?
53096         
53097         var db = false;
53098         // you are not actually selecting the block.
53099         if (sel && sel.hasAttribute('data-block')) {
53100             db = sel;
53101         } else if (sel && sel.closest('[data-block]')) {
53102             
53103             db = sel.closest('[data-block]');
53104             //var cepar = sel.closest('[contenteditable=true]');
53105             //if (db && cepar && cepar.tagName != 'BODY') {
53106             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
53107             //}   
53108         }
53109         
53110         
53111         var block = false;
53112         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
53113         if (db && this.editorcore.enableBlocks) {
53114             block = Roo.htmleditor.Block.factory(db);
53115             
53116             
53117             if (block) {
53118                  db.className = (
53119                         db.classList.length > 0  ? db.className + ' ' : ''
53120                     )  + 'roo-ed-selection';
53121                  
53122                  // since we removed it earlier... its not there..
53123                 tn = 'BLOCK.' + db.getAttribute('data-block');
53124                 
53125                 //this.editorcore.selectNode(db);
53126                 if (typeof(this.toolbars[tn]) == 'undefined') {
53127                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
53128                 }
53129                 this.toolbars[tn].selectedNode = db;
53130                 left_label = block.friendly_name;
53131                 ans = this.editorcore.getAllAncestors();
53132             }
53133             
53134                 
53135             
53136         }
53137         
53138         
53139         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
53140             return; // no change?
53141         }
53142         
53143         
53144           
53145         this.tb.el.hide();
53146         ///console.log("show: " + tn);
53147         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
53148         
53149         this.tb.el.show();
53150         // update name
53151         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
53152         
53153         
53154         // update attributes
53155         if (block && this.tb.fields) {
53156              
53157             this.tb.fields.each(function(e) {
53158                 e.setValue(block[e.name]);
53159             });
53160             
53161             
53162         } else  if (this.tb.fields && this.tb.selectedNode) {
53163             this.tb.fields.each( function(e) {
53164                 if (e.stylename) {
53165                     e.setValue(this.tb.selectedNode.style[e.stylename]);
53166                     return;
53167                 } 
53168                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
53169             }, this);
53170             this.updateToolbarStyles(this.tb.selectedNode);  
53171         }
53172         
53173         
53174        
53175         Roo.menu.MenuMgr.hideAll();
53176
53177         
53178         
53179     
53180         // update the footer
53181         //
53182         this.updateFooter(ans);
53183              
53184     },
53185     
53186     updateToolbarStyles : function(sel)
53187     {
53188         var hasStyles = false;
53189         for(var i in this.styles) {
53190             hasStyles = true;
53191             break;
53192         }
53193         
53194         // update styles
53195         if (hasStyles && this.tb.hasStyles) { 
53196             var st = this.tb.fields.item(0);
53197             
53198             st.store.removeAll();
53199             var cn = sel.className.split(/\s+/);
53200             
53201             var avs = [];
53202             if (this.styles['*']) {
53203                 
53204                 Roo.each(this.styles['*'], function(v) {
53205                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53206                 });
53207             }
53208             if (this.styles[tn]) { 
53209                 Roo.each(this.styles[tn], function(v) {
53210                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53211                 });
53212             }
53213             
53214             st.store.loadData(avs);
53215             st.collapse();
53216             st.setValue(cn);
53217         }
53218     },
53219     
53220      
53221     updateFooter : function(ans)
53222     {
53223         var html = '';
53224         if (ans === false) {
53225             this.footDisp.dom.innerHTML = '';
53226             return;
53227         }
53228         
53229         this.footerEls = ans.reverse();
53230         Roo.each(this.footerEls, function(a,i) {
53231             if (!a) { return; }
53232             html += html.length ? ' &gt; '  :  '';
53233             
53234             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
53235             
53236         });
53237        
53238         // 
53239         var sz = this.footDisp.up('td').getSize();
53240         this.footDisp.dom.style.width = (sz.width -10) + 'px';
53241         this.footDisp.dom.style.marginLeft = '5px';
53242         
53243         this.footDisp.dom.style.overflow = 'hidden';
53244         
53245         this.footDisp.dom.innerHTML = html;
53246             
53247         
53248     },
53249    
53250        
53251     // private
53252     onDestroy : function(){
53253         if(this.rendered){
53254             
53255             this.tb.items.each(function(item){
53256                 if(item.menu){
53257                     item.menu.removeAll();
53258                     if(item.menu.el){
53259                         item.menu.el.destroy();
53260                     }
53261                 }
53262                 item.destroy();
53263             });
53264              
53265         }
53266     },
53267     onFirstFocus: function() {
53268         // need to do this for all the toolbars..
53269         this.tb.items.each(function(item){
53270            item.enable();
53271         });
53272     },
53273     buildToolbar: function(tlist, nm, friendly_name, block)
53274     {
53275         var editor = this.editor;
53276         var editorcore = this.editorcore;
53277          // create a new element.
53278         var wdiv = editor.wrap.createChild({
53279                 tag: 'div'
53280             }, editor.wrap.dom.firstChild.nextSibling, true);
53281         
53282        
53283         var tb = new Roo.Toolbar(wdiv);
53284         ///this.tb = tb; // << this sets the active toolbar..
53285         if (tlist === false && block) {
53286             tlist = block.contextMenu(this);
53287         }
53288         
53289         tb.hasStyles = false;
53290         tb.name = nm;
53291         
53292         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
53293         
53294         var styles = Array.from(this.styles);
53295         
53296         
53297         // styles...
53298         if (styles && styles.length) {
53299             tb.hasStyles = true;
53300             // this needs a multi-select checkbox...
53301             tb.addField( new Roo.form.ComboBox({
53302                 store: new Roo.data.SimpleStore({
53303                     id : 'val',
53304                     fields: ['val', 'selected'],
53305                     data : [] 
53306                 }),
53307                 name : '-roo-edit-className',
53308                 attrname : 'className',
53309                 displayField: 'val',
53310                 typeAhead: false,
53311                 mode: 'local',
53312                 editable : false,
53313                 triggerAction: 'all',
53314                 emptyText:'Select Style',
53315                 selectOnFocus:true,
53316                 width: 130,
53317                 listeners : {
53318                     'select': function(c, r, i) {
53319                         // initial support only for on class per el..
53320                         tb.selectedNode.className =  r ? r.get('val') : '';
53321                         editorcore.syncValue();
53322                     }
53323                 }
53324     
53325             }));
53326         }
53327         
53328         var tbc = Roo.form.HtmlEditor.ToolbarContext;
53329         
53330         
53331         for (var i = 0; i < tlist.length; i++) {
53332             
53333             // newer versions will use xtype cfg to create menus.
53334             if (typeof(tlist[i].xtype) != 'undefined') {
53335                 
53336                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
53337                 
53338                 
53339                 continue;
53340             }
53341             
53342             var item = tlist[i];
53343             tb.add(item.title + ":&nbsp;");
53344             
53345             
53346             //optname == used so you can configure the options available..
53347             var opts = item.opts ? item.opts : false;
53348             if (item.optname) { // use the b
53349                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
53350            
53351             }
53352             
53353             if (opts) {
53354                 // opts == pulldown..
53355                 tb.addField( new Roo.form.ComboBox({
53356                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
53357                         id : 'val',
53358                         fields: ['val', 'display'],
53359                         data : opts  
53360                     }),
53361                     name : '-roo-edit-' + tlist[i].name,
53362                     
53363                     attrname : tlist[i].name,
53364                     stylename : item.style ? item.style : false,
53365                     
53366                     displayField: item.displayField ? item.displayField : 'val',
53367                     valueField :  'val',
53368                     typeAhead: false,
53369                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
53370                     editable : false,
53371                     triggerAction: 'all',
53372                     emptyText:'Select',
53373                     selectOnFocus:true,
53374                     width: item.width ? item.width  : 130,
53375                     listeners : {
53376                         'select': function(c, r, i) {
53377                              
53378                             
53379                             if (c.stylename) {
53380                                 tb.selectedNode.style[c.stylename] =  r.get('val');
53381                                 editorcore.syncValue();
53382                                 return;
53383                             }
53384                             if (r === false) {
53385                                 tb.selectedNode.removeAttribute(c.attrname);
53386                                 editorcore.syncValue();
53387                                 return;
53388                             }
53389                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
53390                             editorcore.syncValue();
53391                         }
53392                     }
53393
53394                 }));
53395                 continue;
53396                     
53397                  
53398                 /*
53399                 tb.addField( new Roo.form.TextField({
53400                     name: i,
53401                     width: 100,
53402                     //allowBlank:false,
53403                     value: ''
53404                 }));
53405                 continue;
53406                 */
53407             }
53408             tb.addField( new Roo.form.TextField({
53409                 name: '-roo-edit-' + tlist[i].name,
53410                 attrname : tlist[i].name,
53411                 
53412                 width: item.width,
53413                 //allowBlank:true,
53414                 value: '',
53415                 listeners: {
53416                     'change' : function(f, nv, ov) {
53417                         
53418                          
53419                         tb.selectedNode.setAttribute(f.attrname, nv);
53420                         editorcore.syncValue();
53421                     }
53422                 }
53423             }));
53424              
53425         }
53426         
53427         var _this = this;
53428         var show_delete = !block || block.deleteTitle !== false;
53429         if(nm == 'BODY'){
53430             show_delete = false;
53431             tb.addSeparator();
53432         
53433             tb.addButton( {
53434                 text: 'Stylesheets',
53435
53436                 listeners : {
53437                     click : function ()
53438                     {
53439                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
53440                     }
53441                 }
53442             });
53443         }
53444         
53445         tb.addFill();
53446         if (show_delete) {
53447             tb.addButton({
53448                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
53449         
53450                 listeners : {
53451                     click : function ()
53452                     {
53453                         var sn = tb.selectedNode;
53454                         if (block) {
53455                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
53456                             
53457                         }
53458                         if (!sn) {
53459                             return;
53460                         }
53461                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
53462                         if (sn.hasAttribute('data-block')) {
53463                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
53464                             sn.parentNode.removeChild(sn);
53465                             
53466                         } else if (sn && sn.tagName != 'BODY') {
53467                             // remove and keep parents.
53468                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
53469                             a.replaceTag(sn);
53470                         }
53471                         
53472                         
53473                         var range = editorcore.createRange();
53474             
53475                         range.setStart(stn,0);
53476                         range.setEnd(stn,0); 
53477                         var selection = editorcore.getSelection();
53478                         selection.removeAllRanges();
53479                         selection.addRange(range);
53480                         
53481                         
53482                         //_this.updateToolbar(null, null, pn);
53483                         _this.updateToolbar(null, null, null);
53484                         _this.updateFooter(false);
53485                         
53486                     }
53487                 }
53488                 
53489                         
53490                     
53491                 
53492             });
53493         }    
53494         
53495         tb.el.on('click', function(e){
53496             e.preventDefault(); // what does this do?
53497         });
53498         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
53499         tb.el.hide();
53500         
53501         // dont need to disable them... as they will get hidden
53502         return tb;
53503          
53504         
53505     },
53506     buildFooter : function()
53507     {
53508         
53509         var fel = this.editor.wrap.createChild();
53510         this.footer = new Roo.Toolbar(fel);
53511         // toolbar has scrolly on left / right?
53512         var footDisp= new Roo.Toolbar.Fill();
53513         var _t = this;
53514         this.footer.add(
53515             {
53516                 text : '&lt;',
53517                 xtype: 'Button',
53518                 handler : function() {
53519                     _t.footDisp.scrollTo('left',0,true)
53520                 }
53521             }
53522         );
53523         this.footer.add( footDisp );
53524         this.footer.add( 
53525             {
53526                 text : '&gt;',
53527                 xtype: 'Button',
53528                 handler : function() {
53529                     // no animation..
53530                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
53531                 }
53532             }
53533         );
53534         var fel = Roo.get(footDisp.el);
53535         fel.addClass('x-editor-context');
53536         this.footDispWrap = fel; 
53537         this.footDispWrap.overflow  = 'hidden';
53538         
53539         this.footDisp = fel.createChild();
53540         this.footDispWrap.on('click', this.onContextClick, this)
53541         
53542         
53543     },
53544     // when the footer contect changes
53545     onContextClick : function (ev,dom)
53546     {
53547         ev.preventDefault();
53548         var  cn = dom.className;
53549         //Roo.log(cn);
53550         if (!cn.match(/x-ed-loc-/)) {
53551             return;
53552         }
53553         var n = cn.split('-').pop();
53554         var ans = this.footerEls;
53555         var sel = ans[n];
53556         
53557         this.editorcore.selectNode(sel);
53558         
53559         
53560         this.updateToolbar(null, null, sel);
53561         
53562         
53563     }
53564     
53565     
53566     
53567     
53568     
53569 });
53570
53571
53572
53573
53574
53575 /*
53576  * Based on:
53577  * Ext JS Library 1.1.1
53578  * Copyright(c) 2006-2007, Ext JS, LLC.
53579  *
53580  * Originally Released Under LGPL - original licence link has changed is not relivant.
53581  *
53582  * Fork - LGPL
53583  * <script type="text/javascript">
53584  */
53585  
53586 /**
53587  * @class Roo.form.BasicForm
53588  * @extends Roo.util.Observable
53589  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
53590  * @constructor
53591  * @param {String/HTMLElement/Roo.Element} el The form element or its id
53592  * @param {Object} config Configuration options
53593  */
53594 Roo.form.BasicForm = function(el, config){
53595     this.allItems = [];
53596     this.childForms = [];
53597     Roo.apply(this, config);
53598     /*
53599      * The Roo.form.Field items in this form.
53600      * @type MixedCollection
53601      */
53602      
53603      
53604     this.items = new Roo.util.MixedCollection(false, function(o){
53605         return o.id || (o.id = Roo.id());
53606     });
53607     this.addEvents({
53608         /**
53609          * @event beforeaction
53610          * Fires before any action is performed. Return false to cancel the action.
53611          * @param {Form} this
53612          * @param {Action} action The action to be performed
53613          */
53614         beforeaction: true,
53615         /**
53616          * @event actionfailed
53617          * Fires when an action fails.
53618          * @param {Form} this
53619          * @param {Action} action The action that failed
53620          */
53621         actionfailed : true,
53622         /**
53623          * @event actioncomplete
53624          * Fires when an action is completed.
53625          * @param {Form} this
53626          * @param {Action} action The action that completed
53627          */
53628         actioncomplete : true
53629     });
53630     if(el){
53631         this.initEl(el);
53632     }
53633     Roo.form.BasicForm.superclass.constructor.call(this);
53634     
53635     Roo.form.BasicForm.popover.apply();
53636 };
53637
53638 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
53639     /**
53640      * @cfg {String} method
53641      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
53642      */
53643     /**
53644      * @cfg {DataReader} reader
53645      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
53646      * This is optional as there is built-in support for processing JSON.
53647      */
53648     /**
53649      * @cfg {DataReader} errorReader
53650      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
53651      * This is completely optional as there is built-in support for processing JSON.
53652      */
53653     /**
53654      * @cfg {String} url
53655      * The URL to use for form actions if one isn't supplied in the action options.
53656      */
53657     /**
53658      * @cfg {Boolean} fileUpload
53659      * Set to true if this form is a file upload.
53660      */
53661      
53662     /**
53663      * @cfg {Object} baseParams
53664      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
53665      */
53666      /**
53667      
53668     /**
53669      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
53670      */
53671     timeout: 30,
53672
53673     // private
53674     activeAction : null,
53675
53676     /**
53677      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
53678      * or setValues() data instead of when the form was first created.
53679      */
53680     trackResetOnLoad : false,
53681     
53682     
53683     /**
53684      * childForms - used for multi-tab forms
53685      * @type {Array}
53686      */
53687     childForms : false,
53688     
53689     /**
53690      * allItems - full list of fields.
53691      * @type {Array}
53692      */
53693     allItems : false,
53694     
53695     /**
53696      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
53697      * element by passing it or its id or mask the form itself by passing in true.
53698      * @type Mixed
53699      */
53700     waitMsgTarget : false,
53701     
53702     /**
53703      * @type Boolean
53704      */
53705     disableMask : false,
53706     
53707     /**
53708      * @cfg {Boolean} errorMask (true|false) default false
53709      */
53710     errorMask : false,
53711     
53712     /**
53713      * @cfg {Number} maskOffset Default 100
53714      */
53715     maskOffset : 100,
53716
53717     // private
53718     initEl : function(el){
53719         this.el = Roo.get(el);
53720         this.id = this.el.id || Roo.id();
53721         this.el.on('submit', this.onSubmit, this);
53722         this.el.addClass('x-form');
53723     },
53724
53725     // private
53726     onSubmit : function(e){
53727         e.stopEvent();
53728     },
53729
53730     /**
53731      * Returns true if client-side validation on the form is successful.
53732      * @return Boolean
53733      */
53734     isValid : function(){
53735         var valid = true;
53736         var target = false;
53737         this.items.each(function(f){
53738             if(f.validate()){
53739                 return;
53740             }
53741             
53742             valid = false;
53743                 
53744             if(!target && f.el.isVisible(true)){
53745                 target = f;
53746             }
53747         });
53748         
53749         if(this.errorMask && !valid){
53750             Roo.form.BasicForm.popover.mask(this, target);
53751         }
53752         
53753         return valid;
53754     },
53755     /**
53756      * Returns array of invalid form fields.
53757      * @return Array
53758      */
53759     
53760     invalidFields : function()
53761     {
53762         var ret = [];
53763         this.items.each(function(f){
53764             if(f.validate()){
53765                 return;
53766             }
53767             ret.push(f);
53768             
53769         });
53770         
53771         return ret;
53772     },
53773     
53774     
53775     /**
53776      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
53777      * @return Boolean
53778      */
53779     isDirty : function(){
53780         var dirty = false;
53781         this.items.each(function(f){
53782            if(f.isDirty()){
53783                dirty = true;
53784                return false;
53785            }
53786         });
53787         return dirty;
53788     },
53789     
53790     /**
53791      * Returns true if any fields in this form have changed since their original load. (New version)
53792      * @return Boolean
53793      */
53794     
53795     hasChanged : function()
53796     {
53797         var dirty = false;
53798         this.items.each(function(f){
53799            if(f.hasChanged()){
53800                dirty = true;
53801                return false;
53802            }
53803         });
53804         return dirty;
53805         
53806     },
53807     /**
53808      * Resets all hasChanged to 'false' -
53809      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
53810      * So hasChanged storage is only to be used for this purpose
53811      * @return Boolean
53812      */
53813     resetHasChanged : function()
53814     {
53815         this.items.each(function(f){
53816            f.resetHasChanged();
53817         });
53818         
53819     },
53820     
53821     
53822     /**
53823      * Performs a predefined action (submit or load) or custom actions you define on this form.
53824      * @param {String} actionName The name of the action type
53825      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
53826      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
53827      * accept other config options):
53828      * <pre>
53829 Property          Type             Description
53830 ----------------  ---------------  ----------------------------------------------------------------------------------
53831 url               String           The url for the action (defaults to the form's url)
53832 method            String           The form method to use (defaults to the form's method, or POST if not defined)
53833 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
53834 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
53835                                    validate the form on the client (defaults to false)
53836      * </pre>
53837      * @return {BasicForm} this
53838      */
53839     doAction : function(action, options){
53840         if(typeof action == 'string'){
53841             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
53842         }
53843         if(this.fireEvent('beforeaction', this, action) !== false){
53844             this.beforeAction(action);
53845             action.run.defer(100, action);
53846         }
53847         return this;
53848     },
53849
53850     /**
53851      * Shortcut to do a submit action.
53852      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53853      * @return {BasicForm} this
53854      */
53855     submit : function(options){
53856         this.doAction('submit', options);
53857         return this;
53858     },
53859
53860     /**
53861      * Shortcut to do a load action.
53862      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53863      * @return {BasicForm} this
53864      */
53865     load : function(options){
53866         this.doAction('load', options);
53867         return this;
53868     },
53869
53870     /**
53871      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
53872      * @param {Record} record The record to edit
53873      * @return {BasicForm} this
53874      */
53875     updateRecord : function(record){
53876         record.beginEdit();
53877         var fs = record.fields;
53878         fs.each(function(f){
53879             var field = this.findField(f.name);
53880             if(field){
53881                 record.set(f.name, field.getValue());
53882             }
53883         }, this);
53884         record.endEdit();
53885         return this;
53886     },
53887
53888     /**
53889      * Loads an Roo.data.Record into this form.
53890      * @param {Record} record The record to load
53891      * @return {BasicForm} this
53892      */
53893     loadRecord : function(record){
53894         this.setValues(record.data);
53895         return this;
53896     },
53897
53898     // private
53899     beforeAction : function(action){
53900         var o = action.options;
53901         
53902         if(!this.disableMask) {
53903             if(this.waitMsgTarget === true){
53904                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
53905             }else if(this.waitMsgTarget){
53906                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
53907                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
53908             }else {
53909                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
53910             }
53911         }
53912         
53913          
53914     },
53915
53916     // private
53917     afterAction : function(action, success){
53918         this.activeAction = null;
53919         var o = action.options;
53920         
53921         if(!this.disableMask) {
53922             if(this.waitMsgTarget === true){
53923                 this.el.unmask();
53924             }else if(this.waitMsgTarget){
53925                 this.waitMsgTarget.unmask();
53926             }else{
53927                 Roo.MessageBox.updateProgress(1);
53928                 Roo.MessageBox.hide();
53929             }
53930         }
53931         
53932         if(success){
53933             if(o.reset){
53934                 this.reset();
53935             }
53936             Roo.callback(o.success, o.scope, [this, action]);
53937             this.fireEvent('actioncomplete', this, action);
53938             
53939         }else{
53940             
53941             // failure condition..
53942             // we have a scenario where updates need confirming.
53943             // eg. if a locking scenario exists..
53944             // we look for { errors : { needs_confirm : true }} in the response.
53945             if (
53946                 (typeof(action.result) != 'undefined')  &&
53947                 (typeof(action.result.errors) != 'undefined')  &&
53948                 (typeof(action.result.errors.needs_confirm) != 'undefined')
53949            ){
53950                 var _t = this;
53951                 Roo.MessageBox.confirm(
53952                     "Change requires confirmation",
53953                     action.result.errorMsg,
53954                     function(r) {
53955                         if (r != 'yes') {
53956                             return;
53957                         }
53958                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
53959                     }
53960                     
53961                 );
53962                 
53963                 
53964                 
53965                 return;
53966             }
53967             
53968             Roo.callback(o.failure, o.scope, [this, action]);
53969             // show an error message if no failed handler is set..
53970             if (!this.hasListener('actionfailed')) {
53971                 Roo.MessageBox.alert("Error",
53972                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
53973                         action.result.errorMsg :
53974                         "Saving Failed, please check your entries or try again"
53975                 );
53976             }
53977             
53978             this.fireEvent('actionfailed', this, action);
53979         }
53980         
53981     },
53982
53983     /**
53984      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
53985      * @param {String} id The value to search for
53986      * @return Field
53987      */
53988     findField : function(id){
53989         var field = this.items.get(id);
53990         if(!field){
53991             this.items.each(function(f){
53992                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
53993                     field = f;
53994                     return false;
53995                 }
53996             });
53997         }
53998         return field || null;
53999     },
54000
54001     /**
54002      * Add a secondary form to this one, 
54003      * Used to provide tabbed forms. One form is primary, with hidden values 
54004      * which mirror the elements from the other forms.
54005      * 
54006      * @param {Roo.form.Form} form to add.
54007      * 
54008      */
54009     addForm : function(form)
54010     {
54011        
54012         if (this.childForms.indexOf(form) > -1) {
54013             // already added..
54014             return;
54015         }
54016         this.childForms.push(form);
54017         var n = '';
54018         Roo.each(form.allItems, function (fe) {
54019             
54020             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
54021             if (this.findField(n)) { // already added..
54022                 return;
54023             }
54024             var add = new Roo.form.Hidden({
54025                 name : n
54026             });
54027             add.render(this.el);
54028             
54029             this.add( add );
54030         }, this);
54031         
54032     },
54033     /**
54034      * Mark fields in this form invalid in bulk.
54035      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
54036      * @return {BasicForm} this
54037      */
54038     markInvalid : function(errors){
54039         if(errors instanceof Array){
54040             for(var i = 0, len = errors.length; i < len; i++){
54041                 var fieldError = errors[i];
54042                 var f = this.findField(fieldError.id);
54043                 if(f){
54044                     f.markInvalid(fieldError.msg);
54045                 }
54046             }
54047         }else{
54048             var field, id;
54049             for(id in errors){
54050                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
54051                     field.markInvalid(errors[id]);
54052                 }
54053             }
54054         }
54055         Roo.each(this.childForms || [], function (f) {
54056             f.markInvalid(errors);
54057         });
54058         
54059         return this;
54060     },
54061
54062     /**
54063      * Set values for fields in this form in bulk.
54064      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
54065      * @return {BasicForm} this
54066      */
54067     setValues : function(values){
54068         if(values instanceof Array){ // array of objects
54069             for(var i = 0, len = values.length; i < len; i++){
54070                 var v = values[i];
54071                 var f = this.findField(v.id);
54072                 if(f){
54073                     f.setValue(v.value);
54074                     if(this.trackResetOnLoad){
54075                         f.originalValue = f.getValue();
54076                     }
54077                 }
54078             }
54079         }else{ // object hash
54080             var field, id;
54081             for(id in values){
54082                 if(typeof values[id] != 'function' && (field = this.findField(id))){
54083                     
54084                     if (field.setFromData && 
54085                         field.valueField && 
54086                         field.displayField &&
54087                         // combos' with local stores can 
54088                         // be queried via setValue()
54089                         // to set their value..
54090                         (field.store && !field.store.isLocal)
54091                         ) {
54092                         // it's a combo
54093                         var sd = { };
54094                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
54095                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
54096                         field.setFromData(sd);
54097                         
54098                     } else {
54099                         field.setValue(values[id]);
54100                     }
54101                     
54102                     
54103                     if(this.trackResetOnLoad){
54104                         field.originalValue = field.getValue();
54105                     }
54106                 }
54107             }
54108         }
54109         this.resetHasChanged();
54110         
54111         
54112         Roo.each(this.childForms || [], function (f) {
54113             f.setValues(values);
54114             f.resetHasChanged();
54115         });
54116                 
54117         return this;
54118     },
54119  
54120     /**
54121      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
54122      * they are returned as an array.
54123      * @param {Boolean} asString
54124      * @return {Object}
54125      */
54126     getValues : function(asString)
54127     {
54128         if (this.childForms) {
54129             // copy values from the child forms
54130             Roo.each(this.childForms, function (f) {
54131                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
54132             }, this);
54133         }
54134         
54135         // use formdata
54136         if (typeof(FormData) != 'undefined' && asString !== true) {
54137             // this relies on a 'recent' version of chrome apparently...
54138             try {
54139                 var fd = (new FormData(this.el.dom)).entries();
54140                 var ret = {};
54141                 var ent = fd.next();
54142                 while (!ent.done) {
54143                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
54144                     ent = fd.next();
54145                 };
54146                 return ret;
54147             } catch(e) {
54148                 
54149             }
54150             
54151         }
54152         
54153         
54154         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
54155         if(asString === true){
54156             return fs;
54157         }
54158         return Roo.urlDecode(fs);
54159     },
54160     
54161     /**
54162      * Returns the fields in this form as an object with key/value pairs. 
54163      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
54164      * Normally this will not return readOnly data 
54165      * @param {Boolean} with_readonly return readonly field data.
54166      * @return {Object}
54167      */
54168     getFieldValues : function(with_readonly)
54169     {
54170         if (this.childForms) {
54171             // copy values from the child forms
54172             // should this call getFieldValues - probably not as we do not currently copy
54173             // hidden fields when we generate..
54174             Roo.each(this.childForms, function (f) {
54175                 this.setValues(f.getFieldValues());
54176             }, this);
54177         }
54178         
54179         var ret = {};
54180         this.items.each(function(f){
54181             
54182             if (f.readOnly && with_readonly !== true) {
54183                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
54184                         // if a subform contains a copy of them.
54185                         // if you have subforms with the same editable data, you will need to copy the data back
54186                         // and forth.
54187             }
54188             
54189             if (!f.getName()) {
54190                 return;
54191             }
54192             var v = f.getValue();
54193             if (f.inputType =='radio') {
54194                 if (typeof(ret[f.getName()]) == 'undefined') {
54195                     ret[f.getName()] = ''; // empty..
54196                 }
54197                 
54198                 if (!f.el.dom.checked) {
54199                     return;
54200                     
54201                 }
54202                 v = f.el.dom.value;
54203                 
54204             }
54205             
54206             // not sure if this supported any more..
54207             if ((typeof(v) == 'object') && f.getRawValue) {
54208                 v = f.getRawValue() ; // dates..
54209             }
54210             // combo boxes where name != hiddenName...
54211             if (f.name != f.getName()) {
54212                 ret[f.name] = f.getRawValue();
54213             }
54214             ret[f.getName()] = v;
54215         });
54216         
54217         return ret;
54218     },
54219
54220     /**
54221      * Clears all invalid messages in this form.
54222      * @return {BasicForm} this
54223      */
54224     clearInvalid : function(){
54225         this.items.each(function(f){
54226            f.clearInvalid();
54227         });
54228         
54229         Roo.each(this.childForms || [], function (f) {
54230             f.clearInvalid();
54231         });
54232         
54233         
54234         return this;
54235     },
54236
54237     /**
54238      * Resets this form.
54239      * @return {BasicForm} this
54240      */
54241     reset : function(){
54242         this.items.each(function(f){
54243             f.reset();
54244         });
54245         
54246         Roo.each(this.childForms || [], function (f) {
54247             f.reset();
54248         });
54249         this.resetHasChanged();
54250         
54251         return this;
54252     },
54253
54254     /**
54255      * Add Roo.form components to this form.
54256      * @param {Field} field1
54257      * @param {Field} field2 (optional)
54258      * @param {Field} etc (optional)
54259      * @return {BasicForm} this
54260      */
54261     add : function(){
54262         this.items.addAll(Array.prototype.slice.call(arguments, 0));
54263         return this;
54264     },
54265
54266
54267     /**
54268      * Removes a field from the items collection (does NOT remove its markup).
54269      * @param {Field} field
54270      * @return {BasicForm} this
54271      */
54272     remove : function(field){
54273         this.items.remove(field);
54274         return this;
54275     },
54276
54277     /**
54278      * Looks at the fields in this form, checks them for an id attribute,
54279      * and calls applyTo on the existing dom element with that id.
54280      * @return {BasicForm} this
54281      */
54282     render : function(){
54283         this.items.each(function(f){
54284             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
54285                 f.applyTo(f.id);
54286             }
54287         });
54288         return this;
54289     },
54290
54291     /**
54292      * Calls {@link Ext#apply} for all fields in this form with the passed object.
54293      * @param {Object} values
54294      * @return {BasicForm} this
54295      */
54296     applyToFields : function(o){
54297         this.items.each(function(f){
54298            Roo.apply(f, o);
54299         });
54300         return this;
54301     },
54302
54303     /**
54304      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
54305      * @param {Object} values
54306      * @return {BasicForm} this
54307      */
54308     applyIfToFields : function(o){
54309         this.items.each(function(f){
54310            Roo.applyIf(f, o);
54311         });
54312         return this;
54313     }
54314 });
54315
54316 // back compat
54317 Roo.BasicForm = Roo.form.BasicForm;
54318
54319 Roo.apply(Roo.form.BasicForm, {
54320     
54321     popover : {
54322         
54323         padding : 5,
54324         
54325         isApplied : false,
54326         
54327         isMasked : false,
54328         
54329         form : false,
54330         
54331         target : false,
54332         
54333         intervalID : false,
54334         
54335         maskEl : false,
54336         
54337         apply : function()
54338         {
54339             if(this.isApplied){
54340                 return;
54341             }
54342             
54343             this.maskEl = {
54344                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
54345                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
54346                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
54347                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
54348             };
54349             
54350             this.maskEl.top.enableDisplayMode("block");
54351             this.maskEl.left.enableDisplayMode("block");
54352             this.maskEl.bottom.enableDisplayMode("block");
54353             this.maskEl.right.enableDisplayMode("block");
54354             
54355             Roo.get(document.body).on('click', function(){
54356                 this.unmask();
54357             }, this);
54358             
54359             Roo.get(document.body).on('touchstart', function(){
54360                 this.unmask();
54361             }, this);
54362             
54363             this.isApplied = true
54364         },
54365         
54366         mask : function(form, target)
54367         {
54368             this.form = form;
54369             
54370             this.target = target;
54371             
54372             if(!this.form.errorMask || !target.el){
54373                 return;
54374             }
54375             
54376             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
54377             
54378             var ot = this.target.el.calcOffsetsTo(scrollable);
54379             
54380             var scrollTo = ot[1] - this.form.maskOffset;
54381             
54382             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
54383             
54384             scrollable.scrollTo('top', scrollTo);
54385             
54386             var el = this.target.wrap || this.target.el;
54387             
54388             var box = el.getBox();
54389             
54390             this.maskEl.top.setStyle('position', 'absolute');
54391             this.maskEl.top.setStyle('z-index', 10000);
54392             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
54393             this.maskEl.top.setLeft(0);
54394             this.maskEl.top.setTop(0);
54395             this.maskEl.top.show();
54396             
54397             this.maskEl.left.setStyle('position', 'absolute');
54398             this.maskEl.left.setStyle('z-index', 10000);
54399             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
54400             this.maskEl.left.setLeft(0);
54401             this.maskEl.left.setTop(box.y - this.padding);
54402             this.maskEl.left.show();
54403
54404             this.maskEl.bottom.setStyle('position', 'absolute');
54405             this.maskEl.bottom.setStyle('z-index', 10000);
54406             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
54407             this.maskEl.bottom.setLeft(0);
54408             this.maskEl.bottom.setTop(box.bottom + this.padding);
54409             this.maskEl.bottom.show();
54410
54411             this.maskEl.right.setStyle('position', 'absolute');
54412             this.maskEl.right.setStyle('z-index', 10000);
54413             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
54414             this.maskEl.right.setLeft(box.right + this.padding);
54415             this.maskEl.right.setTop(box.y - this.padding);
54416             this.maskEl.right.show();
54417
54418             this.intervalID = window.setInterval(function() {
54419                 Roo.form.BasicForm.popover.unmask();
54420             }, 10000);
54421
54422             window.onwheel = function(){ return false;};
54423             
54424             (function(){ this.isMasked = true; }).defer(500, this);
54425             
54426         },
54427         
54428         unmask : function()
54429         {
54430             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
54431                 return;
54432             }
54433             
54434             this.maskEl.top.setStyle('position', 'absolute');
54435             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
54436             this.maskEl.top.hide();
54437
54438             this.maskEl.left.setStyle('position', 'absolute');
54439             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
54440             this.maskEl.left.hide();
54441
54442             this.maskEl.bottom.setStyle('position', 'absolute');
54443             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
54444             this.maskEl.bottom.hide();
54445
54446             this.maskEl.right.setStyle('position', 'absolute');
54447             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
54448             this.maskEl.right.hide();
54449             
54450             window.onwheel = function(){ return true;};
54451             
54452             if(this.intervalID){
54453                 window.clearInterval(this.intervalID);
54454                 this.intervalID = false;
54455             }
54456             
54457             this.isMasked = false;
54458             
54459         }
54460         
54461     }
54462     
54463 });/*
54464  * Based on:
54465  * Ext JS Library 1.1.1
54466  * Copyright(c) 2006-2007, Ext JS, LLC.
54467  *
54468  * Originally Released Under LGPL - original licence link has changed is not relivant.
54469  *
54470  * Fork - LGPL
54471  * <script type="text/javascript">
54472  */
54473
54474 /**
54475  * @class Roo.form.Form
54476  * @extends Roo.form.BasicForm
54477  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
54478  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
54479  * @constructor
54480  * @param {Object} config Configuration options
54481  */
54482 Roo.form.Form = function(config){
54483     var xitems =  [];
54484     if (config.items) {
54485         xitems = config.items;
54486         delete config.items;
54487     }
54488    
54489     
54490     Roo.form.Form.superclass.constructor.call(this, null, config);
54491     this.url = this.url || this.action;
54492     if(!this.root){
54493         this.root = new Roo.form.Layout(Roo.applyIf({
54494             id: Roo.id()
54495         }, config));
54496     }
54497     this.active = this.root;
54498     /**
54499      * Array of all the buttons that have been added to this form via {@link addButton}
54500      * @type Array
54501      */
54502     this.buttons = [];
54503     this.allItems = [];
54504     this.addEvents({
54505         /**
54506          * @event clientvalidation
54507          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
54508          * @param {Form} this
54509          * @param {Boolean} valid true if the form has passed client-side validation
54510          */
54511         clientvalidation: true,
54512         /**
54513          * @event rendered
54514          * Fires when the form is rendered
54515          * @param {Roo.form.Form} form
54516          */
54517         rendered : true
54518     });
54519     
54520     if (this.progressUrl) {
54521             // push a hidden field onto the list of fields..
54522             this.addxtype( {
54523                     xns: Roo.form, 
54524                     xtype : 'Hidden', 
54525                     name : 'UPLOAD_IDENTIFIER' 
54526             });
54527         }
54528         
54529     
54530     Roo.each(xitems, this.addxtype, this);
54531     
54532 };
54533
54534 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
54535      /**
54536      * @cfg {Roo.Button} buttons[] buttons at bottom of form
54537      */
54538     
54539     /**
54540      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
54541      */
54542     /**
54543      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
54544      */
54545     /**
54546      * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
54547      */
54548     buttonAlign:'center',
54549
54550     /**
54551      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
54552      */
54553     minButtonWidth:75,
54554
54555     /**
54556      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
54557      * This property cascades to child containers if not set.
54558      */
54559     labelAlign:'left',
54560
54561     /**
54562      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
54563      * fires a looping event with that state. This is required to bind buttons to the valid
54564      * state using the config value formBind:true on the button.
54565      */
54566     monitorValid : false,
54567
54568     /**
54569      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
54570      */
54571     monitorPoll : 200,
54572     
54573     /**
54574      * @cfg {String} progressUrl - Url to return progress data 
54575      */
54576     
54577     progressUrl : false,
54578     /**
54579      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
54580      * sending a formdata with extra parameters - eg uploaded elements.
54581      */
54582     
54583     formData : false,
54584     
54585     /**
54586      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
54587      * fields are added and the column is closed. If no fields are passed the column remains open
54588      * until end() is called.
54589      * @param {Object} config The config to pass to the column
54590      * @param {Field} field1 (optional)
54591      * @param {Field} field2 (optional)
54592      * @param {Field} etc (optional)
54593      * @return Column The column container object
54594      */
54595     column : function(c){
54596         var col = new Roo.form.Column(c);
54597         this.start(col);
54598         if(arguments.length > 1){ // duplicate code required because of Opera
54599             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54600             this.end();
54601         }
54602         return col;
54603     },
54604
54605     /**
54606      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
54607      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
54608      * until end() is called.
54609      * @param {Object} config The config to pass to the fieldset
54610      * @param {Field} field1 (optional)
54611      * @param {Field} field2 (optional)
54612      * @param {Field} etc (optional)
54613      * @return FieldSet The fieldset container object
54614      */
54615     fieldset : function(c){
54616         var fs = new Roo.form.FieldSet(c);
54617         this.start(fs);
54618         if(arguments.length > 1){ // duplicate code required because of Opera
54619             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54620             this.end();
54621         }
54622         return fs;
54623     },
54624
54625     /**
54626      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
54627      * fields are added and the container is closed. If no fields are passed the container remains open
54628      * until end() is called.
54629      * @param {Object} config The config to pass to the Layout
54630      * @param {Field} field1 (optional)
54631      * @param {Field} field2 (optional)
54632      * @param {Field} etc (optional)
54633      * @return Layout The container object
54634      */
54635     container : function(c){
54636         var l = new Roo.form.Layout(c);
54637         this.start(l);
54638         if(arguments.length > 1){ // duplicate code required because of Opera
54639             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54640             this.end();
54641         }
54642         return l;
54643     },
54644
54645     /**
54646      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
54647      * @param {Object} container A Roo.form.Layout or subclass of Layout
54648      * @return {Form} this
54649      */
54650     start : function(c){
54651         // cascade label info
54652         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
54653         this.active.stack.push(c);
54654         c.ownerCt = this.active;
54655         this.active = c;
54656         return this;
54657     },
54658
54659     /**
54660      * Closes the current open container
54661      * @return {Form} this
54662      */
54663     end : function(){
54664         if(this.active == this.root){
54665             return this;
54666         }
54667         this.active = this.active.ownerCt;
54668         return this;
54669     },
54670
54671     /**
54672      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
54673      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
54674      * as the label of the field.
54675      * @param {Field} field1
54676      * @param {Field} field2 (optional)
54677      * @param {Field} etc. (optional)
54678      * @return {Form} this
54679      */
54680     add : function(){
54681         this.active.stack.push.apply(this.active.stack, arguments);
54682         this.allItems.push.apply(this.allItems,arguments);
54683         var r = [];
54684         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
54685             if(a[i].isFormField){
54686                 r.push(a[i]);
54687             }
54688         }
54689         if(r.length > 0){
54690             Roo.form.Form.superclass.add.apply(this, r);
54691         }
54692         return this;
54693     },
54694     
54695
54696     
54697     
54698     
54699      /**
54700      * Find any element that has been added to a form, using it's ID or name
54701      * This can include framesets, columns etc. along with regular fields..
54702      * @param {String} id - id or name to find.
54703      
54704      * @return {Element} e - or false if nothing found.
54705      */
54706     findbyId : function(id)
54707     {
54708         var ret = false;
54709         if (!id) {
54710             return ret;
54711         }
54712         Roo.each(this.allItems, function(f){
54713             if (f.id == id || f.name == id ){
54714                 ret = f;
54715                 return false;
54716             }
54717         });
54718         return ret;
54719     },
54720
54721     
54722     
54723     /**
54724      * Render this form into the passed container. This should only be called once!
54725      * @param {String/HTMLElement/Element} container The element this component should be rendered into
54726      * @return {Form} this
54727      */
54728     render : function(ct)
54729     {
54730         
54731         
54732         
54733         ct = Roo.get(ct);
54734         var o = this.autoCreate || {
54735             tag: 'form',
54736             method : this.method || 'POST',
54737             id : this.id || Roo.id()
54738         };
54739         this.initEl(ct.createChild(o));
54740
54741         this.root.render(this.el);
54742         
54743        
54744              
54745         this.items.each(function(f){
54746             f.render('x-form-el-'+f.id);
54747         });
54748
54749         if(this.buttons.length > 0){
54750             // tables are required to maintain order and for correct IE layout
54751             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
54752                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
54753                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
54754             }}, null, true);
54755             var tr = tb.getElementsByTagName('tr')[0];
54756             for(var i = 0, len = this.buttons.length; i < len; i++) {
54757                 var b = this.buttons[i];
54758                 var td = document.createElement('td');
54759                 td.className = 'x-form-btn-td';
54760                 b.render(tr.appendChild(td));
54761             }
54762         }
54763         if(this.monitorValid){ // initialize after render
54764             this.startMonitoring();
54765         }
54766         this.fireEvent('rendered', this);
54767         return this;
54768     },
54769
54770     /**
54771      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
54772      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
54773      * object or a valid Roo.DomHelper element config
54774      * @param {Function} handler The function called when the button is clicked
54775      * @param {Object} scope (optional) The scope of the handler function
54776      * @return {Roo.Button}
54777      */
54778     addButton : function(config, handler, scope){
54779         var bc = {
54780             handler: handler,
54781             scope: scope,
54782             minWidth: this.minButtonWidth,
54783             hideParent:true
54784         };
54785         if(typeof config == "string"){
54786             bc.text = config;
54787         }else{
54788             Roo.apply(bc, config);
54789         }
54790         var btn = new Roo.Button(null, bc);
54791         this.buttons.push(btn);
54792         return btn;
54793     },
54794
54795      /**
54796      * Adds a series of form elements (using the xtype property as the factory method.
54797      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
54798      * @param {Object} config 
54799      */
54800     
54801     addxtype : function()
54802     {
54803         var ar = Array.prototype.slice.call(arguments, 0);
54804         var ret = false;
54805         for(var i = 0; i < ar.length; i++) {
54806             if (!ar[i]) {
54807                 continue; // skip -- if this happends something invalid got sent, we 
54808                 // should ignore it, as basically that interface element will not show up
54809                 // and that should be pretty obvious!!
54810             }
54811             
54812             if (Roo.form[ar[i].xtype]) {
54813                 ar[i].form = this;
54814                 var fe = Roo.factory(ar[i], Roo.form);
54815                 if (!ret) {
54816                     ret = fe;
54817                 }
54818                 fe.form = this;
54819                 if (fe.store) {
54820                     fe.store.form = this;
54821                 }
54822                 if (fe.isLayout) {  
54823                          
54824                     this.start(fe);
54825                     this.allItems.push(fe);
54826                     if (fe.items && fe.addxtype) {
54827                         fe.addxtype.apply(fe, fe.items);
54828                         delete fe.items;
54829                     }
54830                      this.end();
54831                     continue;
54832                 }
54833                 
54834                 
54835                  
54836                 this.add(fe);
54837               //  console.log('adding ' + ar[i].xtype);
54838             }
54839             if (ar[i].xtype == 'Button') {  
54840                 //console.log('adding button');
54841                 //console.log(ar[i]);
54842                 this.addButton(ar[i]);
54843                 this.allItems.push(fe);
54844                 continue;
54845             }
54846             
54847             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
54848                 alert('end is not supported on xtype any more, use items');
54849             //    this.end();
54850             //    //console.log('adding end');
54851             }
54852             
54853         }
54854         return ret;
54855     },
54856     
54857     /**
54858      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
54859      * option "monitorValid"
54860      */
54861     startMonitoring : function(){
54862         if(!this.bound){
54863             this.bound = true;
54864             Roo.TaskMgr.start({
54865                 run : this.bindHandler,
54866                 interval : this.monitorPoll || 200,
54867                 scope: this
54868             });
54869         }
54870     },
54871
54872     /**
54873      * Stops monitoring of the valid state of this form
54874      */
54875     stopMonitoring : function(){
54876         this.bound = false;
54877     },
54878
54879     // private
54880     bindHandler : function(){
54881         if(!this.bound){
54882             return false; // stops binding
54883         }
54884         var valid = true;
54885         this.items.each(function(f){
54886             if(!f.isValid(true)){
54887                 valid = false;
54888                 return false;
54889             }
54890         });
54891         for(var i = 0, len = this.buttons.length; i < len; i++){
54892             var btn = this.buttons[i];
54893             if(btn.formBind === true && btn.disabled === valid){
54894                 btn.setDisabled(!valid);
54895             }
54896         }
54897         this.fireEvent('clientvalidation', this, valid);
54898     }
54899     
54900     
54901     
54902     
54903     
54904     
54905     
54906     
54907 });
54908
54909
54910 // back compat
54911 Roo.Form = Roo.form.Form;
54912 /*
54913  * Based on:
54914  * Ext JS Library 1.1.1
54915  * Copyright(c) 2006-2007, Ext JS, LLC.
54916  *
54917  * Originally Released Under LGPL - original licence link has changed is not relivant.
54918  *
54919  * Fork - LGPL
54920  * <script type="text/javascript">
54921  */
54922
54923 // as we use this in bootstrap.
54924 Roo.namespace('Roo.form');
54925  /**
54926  * @class Roo.form.Action
54927  * Internal Class used to handle form actions
54928  * @constructor
54929  * @param {Roo.form.BasicForm} el The form element or its id
54930  * @param {Object} config Configuration options
54931  */
54932
54933  
54934  
54935 // define the action interface
54936 Roo.form.Action = function(form, options){
54937     this.form = form;
54938     this.options = options || {};
54939 };
54940 /**
54941  * Client Validation Failed
54942  * @const 
54943  */
54944 Roo.form.Action.CLIENT_INVALID = 'client';
54945 /**
54946  * Server Validation Failed
54947  * @const 
54948  */
54949 Roo.form.Action.SERVER_INVALID = 'server';
54950  /**
54951  * Connect to Server Failed
54952  * @const 
54953  */
54954 Roo.form.Action.CONNECT_FAILURE = 'connect';
54955 /**
54956  * Reading Data from Server Failed
54957  * @const 
54958  */
54959 Roo.form.Action.LOAD_FAILURE = 'load';
54960
54961 Roo.form.Action.prototype = {
54962     type : 'default',
54963     failureType : undefined,
54964     response : undefined,
54965     result : undefined,
54966
54967     // interface method
54968     run : function(options){
54969
54970     },
54971
54972     // interface method
54973     success : function(response){
54974
54975     },
54976
54977     // interface method
54978     handleResponse : function(response){
54979
54980     },
54981
54982     // default connection failure
54983     failure : function(response){
54984         
54985         this.response = response;
54986         this.failureType = Roo.form.Action.CONNECT_FAILURE;
54987         this.form.afterAction(this, false);
54988     },
54989
54990     processResponse : function(response){
54991         this.response = response;
54992         if(!response.responseText){
54993             return true;
54994         }
54995         this.result = this.handleResponse(response);
54996         return this.result;
54997     },
54998
54999     // utility functions used internally
55000     getUrl : function(appendParams){
55001         var url = this.options.url || this.form.url || this.form.el.dom.action;
55002         if(appendParams){
55003             var p = this.getParams();
55004             if(p){
55005                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
55006             }
55007         }
55008         return url;
55009     },
55010
55011     getMethod : function(){
55012         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
55013     },
55014
55015     getParams : function(){
55016         var bp = this.form.baseParams;
55017         var p = this.options.params;
55018         if(p){
55019             if(typeof p == "object"){
55020                 p = Roo.urlEncode(Roo.applyIf(p, bp));
55021             }else if(typeof p == 'string' && bp){
55022                 p += '&' + Roo.urlEncode(bp);
55023             }
55024         }else if(bp){
55025             p = Roo.urlEncode(bp);
55026         }
55027         return p;
55028     },
55029
55030     createCallback : function(){
55031         return {
55032             success: this.success,
55033             failure: this.failure,
55034             scope: this,
55035             timeout: (this.form.timeout*1000),
55036             upload: this.form.fileUpload ? this.success : undefined
55037         };
55038     }
55039 };
55040
55041 Roo.form.Action.Submit = function(form, options){
55042     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
55043 };
55044
55045 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
55046     type : 'submit',
55047
55048     haveProgress : false,
55049     uploadComplete : false,
55050     
55051     // uploadProgress indicator.
55052     uploadProgress : function()
55053     {
55054         if (!this.form.progressUrl) {
55055             return;
55056         }
55057         
55058         if (!this.haveProgress) {
55059             Roo.MessageBox.progress("Uploading", "Uploading");
55060         }
55061         if (this.uploadComplete) {
55062            Roo.MessageBox.hide();
55063            return;
55064         }
55065         
55066         this.haveProgress = true;
55067    
55068         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
55069         
55070         var c = new Roo.data.Connection();
55071         c.request({
55072             url : this.form.progressUrl,
55073             params: {
55074                 id : uid
55075             },
55076             method: 'GET',
55077             success : function(req){
55078                //console.log(data);
55079                 var rdata = false;
55080                 var edata;
55081                 try  {
55082                    rdata = Roo.decode(req.responseText)
55083                 } catch (e) {
55084                     Roo.log("Invalid data from server..");
55085                     Roo.log(edata);
55086                     return;
55087                 }
55088                 if (!rdata || !rdata.success) {
55089                     Roo.log(rdata);
55090                     Roo.MessageBox.alert(Roo.encode(rdata));
55091                     return;
55092                 }
55093                 var data = rdata.data;
55094                 
55095                 if (this.uploadComplete) {
55096                    Roo.MessageBox.hide();
55097                    return;
55098                 }
55099                    
55100                 if (data){
55101                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
55102                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
55103                     );
55104                 }
55105                 this.uploadProgress.defer(2000,this);
55106             },
55107        
55108             failure: function(data) {
55109                 Roo.log('progress url failed ');
55110                 Roo.log(data);
55111             },
55112             scope : this
55113         });
55114            
55115     },
55116     
55117     
55118     run : function()
55119     {
55120         // run get Values on the form, so it syncs any secondary forms.
55121         this.form.getValues();
55122         
55123         var o = this.options;
55124         var method = this.getMethod();
55125         var isPost = method == 'POST';
55126         if(o.clientValidation === false || this.form.isValid()){
55127             
55128             if (this.form.progressUrl) {
55129                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
55130                     (new Date() * 1) + '' + Math.random());
55131                     
55132             } 
55133             
55134             
55135             Roo.Ajax.request(Roo.apply(this.createCallback(), {
55136                 form:this.form.el.dom,
55137                 url:this.getUrl(!isPost),
55138                 method: method,
55139                 params:isPost ? this.getParams() : null,
55140                 isUpload: this.form.fileUpload,
55141                 formData : this.form.formData
55142             }));
55143             
55144             this.uploadProgress();
55145
55146         }else if (o.clientValidation !== false){ // client validation failed
55147             this.failureType = Roo.form.Action.CLIENT_INVALID;
55148             this.form.afterAction(this, false);
55149         }
55150     },
55151
55152     success : function(response)
55153     {
55154         this.uploadComplete= true;
55155         if (this.haveProgress) {
55156             Roo.MessageBox.hide();
55157         }
55158         
55159         
55160         var result = this.processResponse(response);
55161         if(result === true || result.success){
55162             this.form.afterAction(this, true);
55163             return;
55164         }
55165         if(result.errors){
55166             this.form.markInvalid(result.errors);
55167             this.failureType = Roo.form.Action.SERVER_INVALID;
55168         }
55169         this.form.afterAction(this, false);
55170     },
55171     failure : function(response)
55172     {
55173         this.uploadComplete= true;
55174         if (this.haveProgress) {
55175             Roo.MessageBox.hide();
55176         }
55177         
55178         this.response = response;
55179         this.failureType = Roo.form.Action.CONNECT_FAILURE;
55180         this.form.afterAction(this, false);
55181     },
55182     
55183     handleResponse : function(response){
55184         if(this.form.errorReader){
55185             var rs = this.form.errorReader.read(response);
55186             var errors = [];
55187             if(rs.records){
55188                 for(var i = 0, len = rs.records.length; i < len; i++) {
55189                     var r = rs.records[i];
55190                     errors[i] = r.data;
55191                 }
55192             }
55193             if(errors.length < 1){
55194                 errors = null;
55195             }
55196             return {
55197                 success : rs.success,
55198                 errors : errors
55199             };
55200         }
55201         var ret = false;
55202         try {
55203             ret = Roo.decode(response.responseText);
55204         } catch (e) {
55205             ret = {
55206                 success: false,
55207                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
55208                 errors : []
55209             };
55210         }
55211         return ret;
55212         
55213     }
55214 });
55215
55216
55217 Roo.form.Action.Load = function(form, options){
55218     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
55219     this.reader = this.form.reader;
55220 };
55221
55222 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
55223     type : 'load',
55224
55225     run : function(){
55226         
55227         Roo.Ajax.request(Roo.apply(
55228                 this.createCallback(), {
55229                     method:this.getMethod(),
55230                     url:this.getUrl(false),
55231                     params:this.getParams()
55232         }));
55233     },
55234
55235     success : function(response){
55236         
55237         var result = this.processResponse(response);
55238         if(result === true || !result.success || !result.data){
55239             this.failureType = Roo.form.Action.LOAD_FAILURE;
55240             this.form.afterAction(this, false);
55241             return;
55242         }
55243         this.form.clearInvalid();
55244         this.form.setValues(result.data);
55245         this.form.afterAction(this, true);
55246     },
55247
55248     handleResponse : function(response){
55249         if(this.form.reader){
55250             var rs = this.form.reader.read(response);
55251             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
55252             return {
55253                 success : rs.success,
55254                 data : data
55255             };
55256         }
55257         return Roo.decode(response.responseText);
55258     }
55259 });
55260
55261 Roo.form.Action.ACTION_TYPES = {
55262     'load' : Roo.form.Action.Load,
55263     'submit' : Roo.form.Action.Submit
55264 };/*
55265  * Based on:
55266  * Ext JS Library 1.1.1
55267  * Copyright(c) 2006-2007, Ext JS, LLC.
55268  *
55269  * Originally Released Under LGPL - original licence link has changed is not relivant.
55270  *
55271  * Fork - LGPL
55272  * <script type="text/javascript">
55273  */
55274  
55275 /**
55276  * @class Roo.form.Layout
55277  * @extends Roo.Component
55278  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55279  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
55280  * @constructor
55281  * @param {Object} config Configuration options
55282  */
55283 Roo.form.Layout = function(config){
55284     var xitems = [];
55285     if (config.items) {
55286         xitems = config.items;
55287         delete config.items;
55288     }
55289     Roo.form.Layout.superclass.constructor.call(this, config);
55290     this.stack = [];
55291     Roo.each(xitems, this.addxtype, this);
55292      
55293 };
55294
55295 Roo.extend(Roo.form.Layout, Roo.Component, {
55296     /**
55297      * @cfg {String/Object} autoCreate
55298      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
55299      */
55300     /**
55301      * @cfg {String/Object/Function} style
55302      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
55303      * a function which returns such a specification.
55304      */
55305     /**
55306      * @cfg {String} labelAlign (left|top|right)
55307      * Valid values are "left," "top" and "right" (defaults to "left")
55308      */
55309     /**
55310      * @cfg {Number} labelWidth
55311      * Fixed width in pixels of all field labels (defaults to undefined)
55312      */
55313     /**
55314      * @cfg {Boolean} clear
55315      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
55316      */
55317     clear : true,
55318     /**
55319      * @cfg {String} labelSeparator
55320      * The separator to use after field labels (defaults to ':')
55321      */
55322     labelSeparator : ':',
55323     /**
55324      * @cfg {Boolean} hideLabels
55325      * True to suppress the display of field labels in this layout (defaults to false)
55326      */
55327     hideLabels : false,
55328
55329     // private
55330     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
55331     
55332     isLayout : true,
55333     
55334     // private
55335     onRender : function(ct, position){
55336         if(this.el){ // from markup
55337             this.el = Roo.get(this.el);
55338         }else {  // generate
55339             var cfg = this.getAutoCreate();
55340             this.el = ct.createChild(cfg, position);
55341         }
55342         if(this.style){
55343             this.el.applyStyles(this.style);
55344         }
55345         if(this.labelAlign){
55346             this.el.addClass('x-form-label-'+this.labelAlign);
55347         }
55348         if(this.hideLabels){
55349             this.labelStyle = "display:none";
55350             this.elementStyle = "padding-left:0;";
55351         }else{
55352             if(typeof this.labelWidth == 'number'){
55353                 this.labelStyle = "width:"+this.labelWidth+"px;";
55354                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
55355             }
55356             if(this.labelAlign == 'top'){
55357                 this.labelStyle = "width:auto;";
55358                 this.elementStyle = "padding-left:0;";
55359             }
55360         }
55361         var stack = this.stack;
55362         var slen = stack.length;
55363         if(slen > 0){
55364             if(!this.fieldTpl){
55365                 var t = new Roo.Template(
55366                     '<div class="x-form-item {5}">',
55367                         '<label for="{0}" style="{2}">{1}{4}</label>',
55368                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55369                         '</div>',
55370                     '</div><div class="x-form-clear-left"></div>'
55371                 );
55372                 t.disableFormats = true;
55373                 t.compile();
55374                 Roo.form.Layout.prototype.fieldTpl = t;
55375             }
55376             for(var i = 0; i < slen; i++) {
55377                 if(stack[i].isFormField){
55378                     this.renderField(stack[i]);
55379                 }else{
55380                     this.renderComponent(stack[i]);
55381                 }
55382             }
55383         }
55384         if(this.clear){
55385             this.el.createChild({cls:'x-form-clear'});
55386         }
55387     },
55388
55389     // private
55390     renderField : function(f){
55391         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
55392                f.id, //0
55393                f.fieldLabel, //1
55394                f.labelStyle||this.labelStyle||'', //2
55395                this.elementStyle||'', //3
55396                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
55397                f.itemCls||this.itemCls||''  //5
55398        ], true).getPrevSibling());
55399     },
55400
55401     // private
55402     renderComponent : function(c){
55403         c.render(c.isLayout ? this.el : this.el.createChild());    
55404     },
55405     /**
55406      * Adds a object form elements (using the xtype property as the factory method.)
55407      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
55408      * @param {Object} config 
55409      */
55410     addxtype : function(o)
55411     {
55412         // create the lement.
55413         o.form = this.form;
55414         var fe = Roo.factory(o, Roo.form);
55415         this.form.allItems.push(fe);
55416         this.stack.push(fe);
55417         
55418         if (fe.isFormField) {
55419             this.form.items.add(fe);
55420         }
55421          
55422         return fe;
55423     }
55424 });
55425
55426
55427 /**
55428  * @class Roo.form.Column
55429  * @extends Roo.form.Layout
55430  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55431  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
55432  * @constructor
55433  * @param {Object} config Configuration options
55434  */
55435 Roo.form.Column = function(config){
55436     Roo.form.Column.superclass.constructor.call(this, config);
55437 };
55438
55439 Roo.extend(Roo.form.Column, Roo.form.Layout, {
55440     /**
55441      * @cfg {Number/String} width
55442      * The fixed width of the column in pixels or CSS value (defaults to "auto")
55443      */
55444     /**
55445      * @cfg {String/Object} autoCreate
55446      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
55447      */
55448
55449     // private
55450     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
55451
55452     // private
55453     onRender : function(ct, position){
55454         Roo.form.Column.superclass.onRender.call(this, ct, position);
55455         if(this.width){
55456             this.el.setWidth(this.width);
55457         }
55458     }
55459 });
55460
55461 /**
55462  * @class Roo.form.Row
55463  * @extends Roo.form.Layout
55464  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55465  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
55466  * @constructor
55467  * @param {Object} config Configuration options
55468  */
55469
55470  
55471 Roo.form.Row = function(config){
55472     Roo.form.Row.superclass.constructor.call(this, config);
55473 };
55474  
55475 Roo.extend(Roo.form.Row, Roo.form.Layout, {
55476       /**
55477      * @cfg {Number/String} width
55478      * The fixed width of the column in pixels or CSS value (defaults to "auto")
55479      */
55480     /**
55481      * @cfg {Number/String} height
55482      * The fixed height of the column in pixels or CSS value (defaults to "auto")
55483      */
55484     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
55485     
55486     padWidth : 20,
55487     // private
55488     onRender : function(ct, position){
55489         //console.log('row render');
55490         if(!this.rowTpl){
55491             var t = new Roo.Template(
55492                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
55493                     '<label for="{0}" style="{2}">{1}{4}</label>',
55494                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55495                     '</div>',
55496                 '</div>'
55497             );
55498             t.disableFormats = true;
55499             t.compile();
55500             Roo.form.Layout.prototype.rowTpl = t;
55501         }
55502         this.fieldTpl = this.rowTpl;
55503         
55504         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
55505         var labelWidth = 100;
55506         
55507         if ((this.labelAlign != 'top')) {
55508             if (typeof this.labelWidth == 'number') {
55509                 labelWidth = this.labelWidth
55510             }
55511             this.padWidth =  20 + labelWidth;
55512             
55513         }
55514         
55515         Roo.form.Column.superclass.onRender.call(this, ct, position);
55516         if(this.width){
55517             this.el.setWidth(this.width);
55518         }
55519         if(this.height){
55520             this.el.setHeight(this.height);
55521         }
55522     },
55523     
55524     // private
55525     renderField : function(f){
55526         f.fieldEl = this.fieldTpl.append(this.el, [
55527                f.id, f.fieldLabel,
55528                f.labelStyle||this.labelStyle||'',
55529                this.elementStyle||'',
55530                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
55531                f.itemCls||this.itemCls||'',
55532                f.width ? f.width + this.padWidth : 160 + this.padWidth
55533        ],true);
55534     }
55535 });
55536  
55537
55538 /**
55539  * @class Roo.form.FieldSet
55540  * @extends Roo.form.Layout
55541  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
55542  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
55543  * @constructor
55544  * @param {Object} config Configuration options
55545  */
55546 Roo.form.FieldSet = function(config){
55547     Roo.form.FieldSet.superclass.constructor.call(this, config);
55548 };
55549
55550 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
55551     /**
55552      * @cfg {String} legend
55553      * The text to display as the legend for the FieldSet (defaults to '')
55554      */
55555     /**
55556      * @cfg {String/Object} autoCreate
55557      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
55558      */
55559
55560     // private
55561     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
55562
55563     // private
55564     onRender : function(ct, position){
55565         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
55566         if(this.legend){
55567             this.setLegend(this.legend);
55568         }
55569     },
55570
55571     // private
55572     setLegend : function(text){
55573         if(this.rendered){
55574             this.el.child('legend').update(text);
55575         }
55576     }
55577 });/*
55578  * Based on:
55579  * Ext JS Library 1.1.1
55580  * Copyright(c) 2006-2007, Ext JS, LLC.
55581  *
55582  * Originally Released Under LGPL - original licence link has changed is not relivant.
55583  *
55584  * Fork - LGPL
55585  * <script type="text/javascript">
55586  */
55587 /**
55588  * @class Roo.form.VTypes
55589  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
55590  * @static
55591  */
55592 Roo.form.VTypes = function(){
55593     // closure these in so they are only created once.
55594     var alpha = /^[a-zA-Z_]+$/;
55595     var alphanum = /^[a-zA-Z0-9_]+$/;
55596     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
55597     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
55598
55599     // All these messages and functions are configurable
55600     return {
55601         /**
55602          * The function used to validate email addresses
55603          * @param {String} value The email address
55604          */
55605         'email' : function(v){
55606             return email.test(v);
55607         },
55608         /**
55609          * The error text to display when the email validation function returns false
55610          * @type String
55611          */
55612         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
55613         /**
55614          * The keystroke filter mask to be applied on email input
55615          * @type RegExp
55616          */
55617         'emailMask' : /[a-z0-9_\.\-@]/i,
55618
55619         /**
55620          * The function used to validate URLs
55621          * @param {String} value The URL
55622          */
55623         'url' : function(v){
55624             return url.test(v);
55625         },
55626         /**
55627          * The error text to display when the url validation function returns false
55628          * @type String
55629          */
55630         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
55631         
55632         /**
55633          * The function used to validate alpha values
55634          * @param {String} value The value
55635          */
55636         'alpha' : function(v){
55637             return alpha.test(v);
55638         },
55639         /**
55640          * The error text to display when the alpha validation function returns false
55641          * @type String
55642          */
55643         'alphaText' : 'This field should only contain letters and _',
55644         /**
55645          * The keystroke filter mask to be applied on alpha input
55646          * @type RegExp
55647          */
55648         'alphaMask' : /[a-z_]/i,
55649
55650         /**
55651          * The function used to validate alphanumeric values
55652          * @param {String} value The value
55653          */
55654         'alphanum' : function(v){
55655             return alphanum.test(v);
55656         },
55657         /**
55658          * The error text to display when the alphanumeric validation function returns false
55659          * @type String
55660          */
55661         'alphanumText' : 'This field should only contain letters, numbers and _',
55662         /**
55663          * The keystroke filter mask to be applied on alphanumeric input
55664          * @type RegExp
55665          */
55666         'alphanumMask' : /[a-z0-9_]/i
55667     };
55668 }();//<script type="text/javascript">
55669
55670 /**
55671  * @class Roo.form.FCKeditor
55672  * @extends Roo.form.TextArea
55673  * Wrapper around the FCKEditor http://www.fckeditor.net
55674  * @constructor
55675  * Creates a new FCKeditor
55676  * @param {Object} config Configuration options
55677  */
55678 Roo.form.FCKeditor = function(config){
55679     Roo.form.FCKeditor.superclass.constructor.call(this, config);
55680     this.addEvents({
55681          /**
55682          * @event editorinit
55683          * Fired when the editor is initialized - you can add extra handlers here..
55684          * @param {FCKeditor} this
55685          * @param {Object} the FCK object.
55686          */
55687         editorinit : true
55688     });
55689     
55690     
55691 };
55692 Roo.form.FCKeditor.editors = { };
55693 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
55694 {
55695     //defaultAutoCreate : {
55696     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
55697     //},
55698     // private
55699     /**
55700      * @cfg {Object} fck options - see fck manual for details.
55701      */
55702     fckconfig : false,
55703     
55704     /**
55705      * @cfg {Object} fck toolbar set (Basic or Default)
55706      */
55707     toolbarSet : 'Basic',
55708     /**
55709      * @cfg {Object} fck BasePath
55710      */ 
55711     basePath : '/fckeditor/',
55712     
55713     
55714     frame : false,
55715     
55716     value : '',
55717     
55718    
55719     onRender : function(ct, position)
55720     {
55721         if(!this.el){
55722             this.defaultAutoCreate = {
55723                 tag: "textarea",
55724                 style:"width:300px;height:60px;",
55725                 autocomplete: "new-password"
55726             };
55727         }
55728         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
55729         /*
55730         if(this.grow){
55731             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
55732             if(this.preventScrollbars){
55733                 this.el.setStyle("overflow", "hidden");
55734             }
55735             this.el.setHeight(this.growMin);
55736         }
55737         */
55738         //console.log('onrender' + this.getId() );
55739         Roo.form.FCKeditor.editors[this.getId()] = this;
55740          
55741
55742         this.replaceTextarea() ;
55743         
55744     },
55745     
55746     getEditor : function() {
55747         return this.fckEditor;
55748     },
55749     /**
55750      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
55751      * @param {Mixed} value The value to set
55752      */
55753     
55754     
55755     setValue : function(value)
55756     {
55757         //console.log('setValue: ' + value);
55758         
55759         if(typeof(value) == 'undefined') { // not sure why this is happending...
55760             return;
55761         }
55762         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55763         
55764         //if(!this.el || !this.getEditor()) {
55765         //    this.value = value;
55766             //this.setValue.defer(100,this,[value]);    
55767         //    return;
55768         //} 
55769         
55770         if(!this.getEditor()) {
55771             return;
55772         }
55773         
55774         this.getEditor().SetData(value);
55775         
55776         //
55777
55778     },
55779
55780     /**
55781      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
55782      * @return {Mixed} value The field value
55783      */
55784     getValue : function()
55785     {
55786         
55787         if (this.frame && this.frame.dom.style.display == 'none') {
55788             return Roo.form.FCKeditor.superclass.getValue.call(this);
55789         }
55790         
55791         if(!this.el || !this.getEditor()) {
55792            
55793            // this.getValue.defer(100,this); 
55794             return this.value;
55795         }
55796        
55797         
55798         var value=this.getEditor().GetData();
55799         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55800         return Roo.form.FCKeditor.superclass.getValue.call(this);
55801         
55802
55803     },
55804
55805     /**
55806      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
55807      * @return {Mixed} value The field value
55808      */
55809     getRawValue : function()
55810     {
55811         if (this.frame && this.frame.dom.style.display == 'none') {
55812             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55813         }
55814         
55815         if(!this.el || !this.getEditor()) {
55816             //this.getRawValue.defer(100,this); 
55817             return this.value;
55818             return;
55819         }
55820         
55821         
55822         
55823         var value=this.getEditor().GetData();
55824         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
55825         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55826          
55827     },
55828     
55829     setSize : function(w,h) {
55830         
55831         
55832         
55833         //if (this.frame && this.frame.dom.style.display == 'none') {
55834         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55835         //    return;
55836         //}
55837         //if(!this.el || !this.getEditor()) {
55838         //    this.setSize.defer(100,this, [w,h]); 
55839         //    return;
55840         //}
55841         
55842         
55843         
55844         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55845         
55846         this.frame.dom.setAttribute('width', w);
55847         this.frame.dom.setAttribute('height', h);
55848         this.frame.setSize(w,h);
55849         
55850     },
55851     
55852     toggleSourceEdit : function(value) {
55853         
55854       
55855          
55856         this.el.dom.style.display = value ? '' : 'none';
55857         this.frame.dom.style.display = value ?  'none' : '';
55858         
55859     },
55860     
55861     
55862     focus: function(tag)
55863     {
55864         if (this.frame.dom.style.display == 'none') {
55865             return Roo.form.FCKeditor.superclass.focus.call(this);
55866         }
55867         if(!this.el || !this.getEditor()) {
55868             this.focus.defer(100,this, [tag]); 
55869             return;
55870         }
55871         
55872         
55873         
55874         
55875         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
55876         this.getEditor().Focus();
55877         if (tgs.length) {
55878             if (!this.getEditor().Selection.GetSelection()) {
55879                 this.focus.defer(100,this, [tag]); 
55880                 return;
55881             }
55882             
55883             
55884             var r = this.getEditor().EditorDocument.createRange();
55885             r.setStart(tgs[0],0);
55886             r.setEnd(tgs[0],0);
55887             this.getEditor().Selection.GetSelection().removeAllRanges();
55888             this.getEditor().Selection.GetSelection().addRange(r);
55889             this.getEditor().Focus();
55890         }
55891         
55892     },
55893     
55894     
55895     
55896     replaceTextarea : function()
55897     {
55898         if ( document.getElementById( this.getId() + '___Frame' ) ) {
55899             return ;
55900         }
55901         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
55902         //{
55903             // We must check the elements firstly using the Id and then the name.
55904         var oTextarea = document.getElementById( this.getId() );
55905         
55906         var colElementsByName = document.getElementsByName( this.getId() ) ;
55907          
55908         oTextarea.style.display = 'none' ;
55909
55910         if ( oTextarea.tabIndex ) {            
55911             this.TabIndex = oTextarea.tabIndex ;
55912         }
55913         
55914         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
55915         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
55916         this.frame = Roo.get(this.getId() + '___Frame')
55917     },
55918     
55919     _getConfigHtml : function()
55920     {
55921         var sConfig = '' ;
55922
55923         for ( var o in this.fckconfig ) {
55924             sConfig += sConfig.length > 0  ? '&amp;' : '';
55925             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
55926         }
55927
55928         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
55929     },
55930     
55931     
55932     _getIFrameHtml : function()
55933     {
55934         var sFile = 'fckeditor.html' ;
55935         /* no idea what this is about..
55936         try
55937         {
55938             if ( (/fcksource=true/i).test( window.top.location.search ) )
55939                 sFile = 'fckeditor.original.html' ;
55940         }
55941         catch (e) { 
55942         */
55943
55944         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
55945         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
55946         
55947         
55948         var html = '<iframe id="' + this.getId() +
55949             '___Frame" src="' + sLink +
55950             '" width="' + this.width +
55951             '" height="' + this.height + '"' +
55952             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
55953             ' frameborder="0" scrolling="no"></iframe>' ;
55954
55955         return html ;
55956     },
55957     
55958     _insertHtmlBefore : function( html, element )
55959     {
55960         if ( element.insertAdjacentHTML )       {
55961             // IE
55962             element.insertAdjacentHTML( 'beforeBegin', html ) ;
55963         } else { // Gecko
55964             var oRange = document.createRange() ;
55965             oRange.setStartBefore( element ) ;
55966             var oFragment = oRange.createContextualFragment( html );
55967             element.parentNode.insertBefore( oFragment, element ) ;
55968         }
55969     }
55970     
55971     
55972   
55973     
55974     
55975     
55976     
55977
55978 });
55979
55980 //Roo.reg('fckeditor', Roo.form.FCKeditor);
55981
55982 function FCKeditor_OnComplete(editorInstance){
55983     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
55984     f.fckEditor = editorInstance;
55985     //console.log("loaded");
55986     f.fireEvent('editorinit', f, editorInstance);
55987
55988   
55989
55990  
55991
55992
55993
55994
55995
55996
55997
55998
55999
56000
56001
56002
56003
56004
56005
56006 //<script type="text/javascript">
56007 /**
56008  * @class Roo.form.GridField
56009  * @extends Roo.form.Field
56010  * Embed a grid (or editable grid into a form)
56011  * STATUS ALPHA
56012  * 
56013  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
56014  * it needs 
56015  * xgrid.store = Roo.data.Store
56016  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
56017  * xgrid.store.reader = Roo.data.JsonReader 
56018  * 
56019  * 
56020  * @constructor
56021  * Creates a new GridField
56022  * @param {Object} config Configuration options
56023  */
56024 Roo.form.GridField = function(config){
56025     Roo.form.GridField.superclass.constructor.call(this, config);
56026      
56027 };
56028
56029 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
56030     /**
56031      * @cfg {Number} width  - used to restrict width of grid..
56032      */
56033     width : 100,
56034     /**
56035      * @cfg {Number} height - used to restrict height of grid..
56036      */
56037     height : 50,
56038      /**
56039      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
56040          * 
56041          *}
56042      */
56043     xgrid : false, 
56044     /**
56045      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56046      * {tag: "input", type: "checkbox", autocomplete: "off"})
56047      */
56048    // defaultAutoCreate : { tag: 'div' },
56049     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
56050     /**
56051      * @cfg {String} addTitle Text to include for adding a title.
56052      */
56053     addTitle : false,
56054     //
56055     onResize : function(){
56056         Roo.form.Field.superclass.onResize.apply(this, arguments);
56057     },
56058
56059     initEvents : function(){
56060         // Roo.form.Checkbox.superclass.initEvents.call(this);
56061         // has no events...
56062        
56063     },
56064
56065
56066     getResizeEl : function(){
56067         return this.wrap;
56068     },
56069
56070     getPositionEl : function(){
56071         return this.wrap;
56072     },
56073
56074     // private
56075     onRender : function(ct, position){
56076         
56077         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
56078         var style = this.style;
56079         delete this.style;
56080         
56081         Roo.form.GridField.superclass.onRender.call(this, ct, position);
56082         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
56083         this.viewEl = this.wrap.createChild({ tag: 'div' });
56084         if (style) {
56085             this.viewEl.applyStyles(style);
56086         }
56087         if (this.width) {
56088             this.viewEl.setWidth(this.width);
56089         }
56090         if (this.height) {
56091             this.viewEl.setHeight(this.height);
56092         }
56093         //if(this.inputValue !== undefined){
56094         //this.setValue(this.value);
56095         
56096         
56097         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
56098         
56099         
56100         this.grid.render();
56101         this.grid.getDataSource().on('remove', this.refreshValue, this);
56102         this.grid.getDataSource().on('update', this.refreshValue, this);
56103         this.grid.on('afteredit', this.refreshValue, this);
56104  
56105     },
56106      
56107     
56108     /**
56109      * Sets the value of the item. 
56110      * @param {String} either an object  or a string..
56111      */
56112     setValue : function(v){
56113         //this.value = v;
56114         v = v || []; // empty set..
56115         // this does not seem smart - it really only affects memoryproxy grids..
56116         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
56117             var ds = this.grid.getDataSource();
56118             // assumes a json reader..
56119             var data = {}
56120             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
56121             ds.loadData( data);
56122         }
56123         // clear selection so it does not get stale.
56124         if (this.grid.sm) { 
56125             this.grid.sm.clearSelections();
56126         }
56127         
56128         Roo.form.GridField.superclass.setValue.call(this, v);
56129         this.refreshValue();
56130         // should load data in the grid really....
56131     },
56132     
56133     // private
56134     refreshValue: function() {
56135          var val = [];
56136         this.grid.getDataSource().each(function(r) {
56137             val.push(r.data);
56138         });
56139         this.el.dom.value = Roo.encode(val);
56140     }
56141     
56142      
56143     
56144     
56145 });/*
56146  * Based on:
56147  * Ext JS Library 1.1.1
56148  * Copyright(c) 2006-2007, Ext JS, LLC.
56149  *
56150  * Originally Released Under LGPL - original licence link has changed is not relivant.
56151  *
56152  * Fork - LGPL
56153  * <script type="text/javascript">
56154  */
56155 /**
56156  * @class Roo.form.DisplayField
56157  * @extends Roo.form.Field
56158  * A generic Field to display non-editable data.
56159  * @cfg {Boolean} closable (true|false) default false
56160  * @constructor
56161  * Creates a new Display Field item.
56162  * @param {Object} config Configuration options
56163  */
56164 Roo.form.DisplayField = function(config){
56165     Roo.form.DisplayField.superclass.constructor.call(this, config);
56166     
56167     this.addEvents({
56168         /**
56169          * @event close
56170          * Fires after the click the close btn
56171              * @param {Roo.form.DisplayField} this
56172              */
56173         close : true
56174     });
56175 };
56176
56177 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
56178     inputType:      'hidden',
56179     allowBlank:     true,
56180     readOnly:         true,
56181     
56182  
56183     /**
56184      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56185      */
56186     focusClass : undefined,
56187     /**
56188      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56189      */
56190     fieldClass: 'x-form-field',
56191     
56192      /**
56193      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
56194      */
56195     valueRenderer: undefined,
56196     
56197     width: 100,
56198     /**
56199      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56200      * {tag: "input", type: "checkbox", autocomplete: "off"})
56201      */
56202      
56203  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
56204  
56205     closable : false,
56206     
56207     onResize : function(){
56208         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
56209         
56210     },
56211
56212     initEvents : function(){
56213         // Roo.form.Checkbox.superclass.initEvents.call(this);
56214         // has no events...
56215         
56216         if(this.closable){
56217             this.closeEl.on('click', this.onClose, this);
56218         }
56219        
56220     },
56221
56222
56223     getResizeEl : function(){
56224         return this.wrap;
56225     },
56226
56227     getPositionEl : function(){
56228         return this.wrap;
56229     },
56230
56231     // private
56232     onRender : function(ct, position){
56233         
56234         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
56235         //if(this.inputValue !== undefined){
56236         this.wrap = this.el.wrap();
56237         
56238         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
56239         
56240         if(this.closable){
56241             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
56242         }
56243         
56244         if (this.bodyStyle) {
56245             this.viewEl.applyStyles(this.bodyStyle);
56246         }
56247         //this.viewEl.setStyle('padding', '2px');
56248         
56249         this.setValue(this.value);
56250         
56251     },
56252 /*
56253     // private
56254     initValue : Roo.emptyFn,
56255
56256   */
56257
56258         // private
56259     onClick : function(){
56260         
56261     },
56262
56263     /**
56264      * Sets the checked state of the checkbox.
56265      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
56266      */
56267     setValue : function(v){
56268         this.value = v;
56269         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
56270         // this might be called before we have a dom element..
56271         if (!this.viewEl) {
56272             return;
56273         }
56274         this.viewEl.dom.innerHTML = html;
56275         Roo.form.DisplayField.superclass.setValue.call(this, v);
56276
56277     },
56278     
56279     onClose : function(e)
56280     {
56281         e.preventDefault();
56282         
56283         this.fireEvent('close', this);
56284     }
56285 });/*
56286  * 
56287  * Licence- LGPL
56288  * 
56289  */
56290
56291 /**
56292  * @class Roo.form.DayPicker
56293  * @extends Roo.form.Field
56294  * A Day picker show [M] [T] [W] ....
56295  * @constructor
56296  * Creates a new Day Picker
56297  * @param {Object} config Configuration options
56298  */
56299 Roo.form.DayPicker= function(config){
56300     Roo.form.DayPicker.superclass.constructor.call(this, config);
56301      
56302 };
56303
56304 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
56305     /**
56306      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56307      */
56308     focusClass : undefined,
56309     /**
56310      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56311      */
56312     fieldClass: "x-form-field",
56313    
56314     /**
56315      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56316      * {tag: "input", type: "checkbox", autocomplete: "off"})
56317      */
56318     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
56319     
56320    
56321     actionMode : 'viewEl', 
56322     //
56323     // private
56324  
56325     inputType : 'hidden',
56326     
56327      
56328     inputElement: false, // real input element?
56329     basedOn: false, // ????
56330     
56331     isFormField: true, // not sure where this is needed!!!!
56332
56333     onResize : function(){
56334         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
56335         if(!this.boxLabel){
56336             this.el.alignTo(this.wrap, 'c-c');
56337         }
56338     },
56339
56340     initEvents : function(){
56341         Roo.form.Checkbox.superclass.initEvents.call(this);
56342         this.el.on("click", this.onClick,  this);
56343         this.el.on("change", this.onClick,  this);
56344     },
56345
56346
56347     getResizeEl : function(){
56348         return this.wrap;
56349     },
56350
56351     getPositionEl : function(){
56352         return this.wrap;
56353     },
56354
56355     
56356     // private
56357     onRender : function(ct, position){
56358         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
56359        
56360         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
56361         
56362         var r1 = '<table><tr>';
56363         var r2 = '<tr class="x-form-daypick-icons">';
56364         for (var i=0; i < 7; i++) {
56365             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
56366             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
56367         }
56368         
56369         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
56370         viewEl.select('img').on('click', this.onClick, this);
56371         this.viewEl = viewEl;   
56372         
56373         
56374         // this will not work on Chrome!!!
56375         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
56376         this.el.on('propertychange', this.setFromHidden,  this);  //ie
56377         
56378         
56379           
56380
56381     },
56382
56383     // private
56384     initValue : Roo.emptyFn,
56385
56386     /**
56387      * Returns the checked state of the checkbox.
56388      * @return {Boolean} True if checked, else false
56389      */
56390     getValue : function(){
56391         return this.el.dom.value;
56392         
56393     },
56394
56395         // private
56396     onClick : function(e){ 
56397         //this.setChecked(!this.checked);
56398         Roo.get(e.target).toggleClass('x-menu-item-checked');
56399         this.refreshValue();
56400         //if(this.el.dom.checked != this.checked){
56401         //    this.setValue(this.el.dom.checked);
56402        // }
56403     },
56404     
56405     // private
56406     refreshValue : function()
56407     {
56408         var val = '';
56409         this.viewEl.select('img',true).each(function(e,i,n)  {
56410             val += e.is(".x-menu-item-checked") ? String(n) : '';
56411         });
56412         this.setValue(val, true);
56413     },
56414
56415     /**
56416      * Sets the checked state of the checkbox.
56417      * On is always based on a string comparison between inputValue and the param.
56418      * @param {Boolean/String} value - the value to set 
56419      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
56420      */
56421     setValue : function(v,suppressEvent){
56422         if (!this.el.dom) {
56423             return;
56424         }
56425         var old = this.el.dom.value ;
56426         this.el.dom.value = v;
56427         if (suppressEvent) {
56428             return ;
56429         }
56430          
56431         // update display..
56432         this.viewEl.select('img',true).each(function(e,i,n)  {
56433             
56434             var on = e.is(".x-menu-item-checked");
56435             var newv = v.indexOf(String(n)) > -1;
56436             if (on != newv) {
56437                 e.toggleClass('x-menu-item-checked');
56438             }
56439             
56440         });
56441         
56442         
56443         this.fireEvent('change', this, v, old);
56444         
56445         
56446     },
56447    
56448     // handle setting of hidden value by some other method!!?!?
56449     setFromHidden: function()
56450     {
56451         if(!this.el){
56452             return;
56453         }
56454         //console.log("SET FROM HIDDEN");
56455         //alert('setFrom hidden');
56456         this.setValue(this.el.dom.value);
56457     },
56458     
56459     onDestroy : function()
56460     {
56461         if(this.viewEl){
56462             Roo.get(this.viewEl).remove();
56463         }
56464          
56465         Roo.form.DayPicker.superclass.onDestroy.call(this);
56466     }
56467
56468 });/*
56469  * RooJS Library 1.1.1
56470  * Copyright(c) 2008-2011  Alan Knowles
56471  *
56472  * License - LGPL
56473  */
56474  
56475
56476 /**
56477  * @class Roo.form.ComboCheck
56478  * @extends Roo.form.ComboBox
56479  * A combobox for multiple select items.
56480  *
56481  * FIXME - could do with a reset button..
56482  * 
56483  * @constructor
56484  * Create a new ComboCheck
56485  * @param {Object} config Configuration options
56486  */
56487 Roo.form.ComboCheck = function(config){
56488     Roo.form.ComboCheck.superclass.constructor.call(this, config);
56489     // should verify some data...
56490     // like
56491     // hiddenName = required..
56492     // displayField = required
56493     // valudField == required
56494     var req= [ 'hiddenName', 'displayField', 'valueField' ];
56495     var _t = this;
56496     Roo.each(req, function(e) {
56497         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
56498             throw "Roo.form.ComboCheck : missing value for: " + e;
56499         }
56500     });
56501     
56502     
56503 };
56504
56505 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
56506      
56507      
56508     editable : false,
56509      
56510     selectedClass: 'x-menu-item-checked', 
56511     
56512     // private
56513     onRender : function(ct, position){
56514         var _t = this;
56515         
56516         
56517         
56518         if(!this.tpl){
56519             var cls = 'x-combo-list';
56520
56521             
56522             this.tpl =  new Roo.Template({
56523                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
56524                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
56525                    '<span>{' + this.displayField + '}</span>' +
56526                     '</div>' 
56527                 
56528             });
56529         }
56530  
56531         
56532         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
56533         this.view.singleSelect = false;
56534         this.view.multiSelect = true;
56535         this.view.toggleSelect = true;
56536         this.pageTb.add(new Roo.Toolbar.Fill(), {
56537             
56538             text: 'Done',
56539             handler: function()
56540             {
56541                 _t.collapse();
56542             }
56543         });
56544     },
56545     
56546     onViewOver : function(e, t){
56547         // do nothing...
56548         return;
56549         
56550     },
56551     
56552     onViewClick : function(doFocus,index){
56553         return;
56554         
56555     },
56556     select: function () {
56557         //Roo.log("SELECT CALLED");
56558     },
56559      
56560     selectByValue : function(xv, scrollIntoView){
56561         var ar = this.getValueArray();
56562         var sels = [];
56563         
56564         Roo.each(ar, function(v) {
56565             if(v === undefined || v === null){
56566                 return;
56567             }
56568             var r = this.findRecord(this.valueField, v);
56569             if(r){
56570                 sels.push(this.store.indexOf(r))
56571                 
56572             }
56573         },this);
56574         this.view.select(sels);
56575         return false;
56576     },
56577     
56578     
56579     
56580     onSelect : function(record, index){
56581        // Roo.log("onselect Called");
56582        // this is only called by the clear button now..
56583         this.view.clearSelections();
56584         this.setValue('[]');
56585         if (this.value != this.valueBefore) {
56586             this.fireEvent('change', this, this.value, this.valueBefore);
56587             this.valueBefore = this.value;
56588         }
56589     },
56590     getValueArray : function()
56591     {
56592         var ar = [] ;
56593         
56594         try {
56595             //Roo.log(this.value);
56596             if (typeof(this.value) == 'undefined') {
56597                 return [];
56598             }
56599             var ar = Roo.decode(this.value);
56600             return  ar instanceof Array ? ar : []; //?? valid?
56601             
56602         } catch(e) {
56603             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
56604             return [];
56605         }
56606          
56607     },
56608     expand : function ()
56609     {
56610         
56611         Roo.form.ComboCheck.superclass.expand.call(this);
56612         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
56613         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
56614         
56615
56616     },
56617     
56618     collapse : function(){
56619         Roo.form.ComboCheck.superclass.collapse.call(this);
56620         var sl = this.view.getSelectedIndexes();
56621         var st = this.store;
56622         var nv = [];
56623         var tv = [];
56624         var r;
56625         Roo.each(sl, function(i) {
56626             r = st.getAt(i);
56627             nv.push(r.get(this.valueField));
56628         },this);
56629         this.setValue(Roo.encode(nv));
56630         if (this.value != this.valueBefore) {
56631
56632             this.fireEvent('change', this, this.value, this.valueBefore);
56633             this.valueBefore = this.value;
56634         }
56635         
56636     },
56637     
56638     setValue : function(v){
56639         // Roo.log(v);
56640         this.value = v;
56641         
56642         var vals = this.getValueArray();
56643         var tv = [];
56644         Roo.each(vals, function(k) {
56645             var r = this.findRecord(this.valueField, k);
56646             if(r){
56647                 tv.push(r.data[this.displayField]);
56648             }else if(this.valueNotFoundText !== undefined){
56649                 tv.push( this.valueNotFoundText );
56650             }
56651         },this);
56652        // Roo.log(tv);
56653         
56654         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
56655         this.hiddenField.value = v;
56656         this.value = v;
56657     }
56658     
56659 });/*
56660  * Based on:
56661  * Ext JS Library 1.1.1
56662  * Copyright(c) 2006-2007, Ext JS, LLC.
56663  *
56664  * Originally Released Under LGPL - original licence link has changed is not relivant.
56665  *
56666  * Fork - LGPL
56667  * <script type="text/javascript">
56668  */
56669  
56670 /**
56671  * @class Roo.form.Signature
56672  * @extends Roo.form.Field
56673  * Signature field.  
56674  * @constructor
56675  * 
56676  * @param {Object} config Configuration options
56677  */
56678
56679 Roo.form.Signature = function(config){
56680     Roo.form.Signature.superclass.constructor.call(this, config);
56681     
56682     this.addEvents({// not in used??
56683          /**
56684          * @event confirm
56685          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
56686              * @param {Roo.form.Signature} combo This combo box
56687              */
56688         'confirm' : true,
56689         /**
56690          * @event reset
56691          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
56692              * @param {Roo.form.ComboBox} combo This combo box
56693              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
56694              */
56695         'reset' : true
56696     });
56697 };
56698
56699 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
56700     /**
56701      * @cfg {Object} labels Label to use when rendering a form.
56702      * defaults to 
56703      * labels : { 
56704      *      clear : "Clear",
56705      *      confirm : "Confirm"
56706      *  }
56707      */
56708     labels : { 
56709         clear : "Clear",
56710         confirm : "Confirm"
56711     },
56712     /**
56713      * @cfg {Number} width The signature panel width (defaults to 300)
56714      */
56715     width: 300,
56716     /**
56717      * @cfg {Number} height The signature panel height (defaults to 100)
56718      */
56719     height : 100,
56720     /**
56721      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
56722      */
56723     allowBlank : false,
56724     
56725     //private
56726     // {Object} signPanel The signature SVG panel element (defaults to {})
56727     signPanel : {},
56728     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
56729     isMouseDown : false,
56730     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
56731     isConfirmed : false,
56732     // {String} signatureTmp SVG mapping string (defaults to empty string)
56733     signatureTmp : '',
56734     
56735     
56736     defaultAutoCreate : { // modified by initCompnoent..
56737         tag: "input",
56738         type:"hidden"
56739     },
56740
56741     // private
56742     onRender : function(ct, position){
56743         
56744         Roo.form.Signature.superclass.onRender.call(this, ct, position);
56745         
56746         this.wrap = this.el.wrap({
56747             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
56748         });
56749         
56750         this.createToolbar(this);
56751         this.signPanel = this.wrap.createChild({
56752                 tag: 'div',
56753                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
56754             }, this.el
56755         );
56756             
56757         this.svgID = Roo.id();
56758         this.svgEl = this.signPanel.createChild({
56759               xmlns : 'http://www.w3.org/2000/svg',
56760               tag : 'svg',
56761               id : this.svgID + "-svg",
56762               width: this.width,
56763               height: this.height,
56764               viewBox: '0 0 '+this.width+' '+this.height,
56765               cn : [
56766                 {
56767                     tag: "rect",
56768                     id: this.svgID + "-svg-r",
56769                     width: this.width,
56770                     height: this.height,
56771                     fill: "#ffa"
56772                 },
56773                 {
56774                     tag: "line",
56775                     id: this.svgID + "-svg-l",
56776                     x1: "0", // start
56777                     y1: (this.height*0.8), // start set the line in 80% of height
56778                     x2: this.width, // end
56779                     y2: (this.height*0.8), // end set the line in 80% of height
56780                     'stroke': "#666",
56781                     'stroke-width': "1",
56782                     'stroke-dasharray': "3",
56783                     'shape-rendering': "crispEdges",
56784                     'pointer-events': "none"
56785                 },
56786                 {
56787                     tag: "path",
56788                     id: this.svgID + "-svg-p",
56789                     'stroke': "navy",
56790                     'stroke-width': "3",
56791                     'fill': "none",
56792                     'pointer-events': 'none'
56793                 }
56794               ]
56795         });
56796         this.createSVG();
56797         this.svgBox = this.svgEl.dom.getScreenCTM();
56798     },
56799     createSVG : function(){ 
56800         var svg = this.signPanel;
56801         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
56802         var t = this;
56803
56804         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
56805         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
56806         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
56807         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
56808         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
56809         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
56810         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
56811         
56812     },
56813     isTouchEvent : function(e){
56814         return e.type.match(/^touch/);
56815     },
56816     getCoords : function (e) {
56817         var pt    = this.svgEl.dom.createSVGPoint();
56818         pt.x = e.clientX; 
56819         pt.y = e.clientY;
56820         if (this.isTouchEvent(e)) {
56821             pt.x =  e.targetTouches[0].clientX;
56822             pt.y = e.targetTouches[0].clientY;
56823         }
56824         var a = this.svgEl.dom.getScreenCTM();
56825         var b = a.inverse();
56826         var mx = pt.matrixTransform(b);
56827         return mx.x + ',' + mx.y;
56828     },
56829     //mouse event headler 
56830     down : function (e) {
56831         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
56832         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
56833         
56834         this.isMouseDown = true;
56835         
56836         e.preventDefault();
56837     },
56838     move : function (e) {
56839         if (this.isMouseDown) {
56840             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
56841             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
56842         }
56843         
56844         e.preventDefault();
56845     },
56846     up : function (e) {
56847         this.isMouseDown = false;
56848         var sp = this.signatureTmp.split(' ');
56849         
56850         if(sp.length > 1){
56851             if(!sp[sp.length-2].match(/^L/)){
56852                 sp.pop();
56853                 sp.pop();
56854                 sp.push("");
56855                 this.signatureTmp = sp.join(" ");
56856             }
56857         }
56858         if(this.getValue() != this.signatureTmp){
56859             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56860             this.isConfirmed = false;
56861         }
56862         e.preventDefault();
56863     },
56864     
56865     /**
56866      * Protected method that will not generally be called directly. It
56867      * is called when the editor creates its toolbar. Override this method if you need to
56868      * add custom toolbar buttons.
56869      * @param {HtmlEditor} editor
56870      */
56871     createToolbar : function(editor){
56872          function btn(id, toggle, handler){
56873             var xid = fid + '-'+ id ;
56874             return {
56875                 id : xid,
56876                 cmd : id,
56877                 cls : 'x-btn-icon x-edit-'+id,
56878                 enableToggle:toggle !== false,
56879                 scope: editor, // was editor...
56880                 handler:handler||editor.relayBtnCmd,
56881                 clickEvent:'mousedown',
56882                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
56883                 tabIndex:-1
56884             };
56885         }
56886         
56887         
56888         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
56889         this.tb = tb;
56890         this.tb.add(
56891            {
56892                 cls : ' x-signature-btn x-signature-'+id,
56893                 scope: editor, // was editor...
56894                 handler: this.reset,
56895                 clickEvent:'mousedown',
56896                 text: this.labels.clear
56897             },
56898             {
56899                  xtype : 'Fill',
56900                  xns: Roo.Toolbar
56901             }, 
56902             {
56903                 cls : '  x-signature-btn x-signature-'+id,
56904                 scope: editor, // was editor...
56905                 handler: this.confirmHandler,
56906                 clickEvent:'mousedown',
56907                 text: this.labels.confirm
56908             }
56909         );
56910     
56911     },
56912     //public
56913     /**
56914      * when user is clicked confirm then show this image.....
56915      * 
56916      * @return {String} Image Data URI
56917      */
56918     getImageDataURI : function(){
56919         var svg = this.svgEl.dom.parentNode.innerHTML;
56920         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
56921         return src; 
56922     },
56923     /**
56924      * 
56925      * @return {Boolean} this.isConfirmed
56926      */
56927     getConfirmed : function(){
56928         return this.isConfirmed;
56929     },
56930     /**
56931      * 
56932      * @return {Number} this.width
56933      */
56934     getWidth : function(){
56935         return this.width;
56936     },
56937     /**
56938      * 
56939      * @return {Number} this.height
56940      */
56941     getHeight : function(){
56942         return this.height;
56943     },
56944     // private
56945     getSignature : function(){
56946         return this.signatureTmp;
56947     },
56948     // private
56949     reset : function(){
56950         this.signatureTmp = '';
56951         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56952         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
56953         this.isConfirmed = false;
56954         Roo.form.Signature.superclass.reset.call(this);
56955     },
56956     setSignature : function(s){
56957         this.signatureTmp = s;
56958         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56959         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
56960         this.setValue(s);
56961         this.isConfirmed = false;
56962         Roo.form.Signature.superclass.reset.call(this);
56963     }, 
56964     test : function(){
56965 //        Roo.log(this.signPanel.dom.contentWindow.up())
56966     },
56967     //private
56968     setConfirmed : function(){
56969         
56970         
56971         
56972 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
56973     },
56974     // private
56975     confirmHandler : function(){
56976         if(!this.getSignature()){
56977             return;
56978         }
56979         
56980         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
56981         this.setValue(this.getSignature());
56982         this.isConfirmed = true;
56983         
56984         this.fireEvent('confirm', this);
56985     },
56986     // private
56987     // Subclasses should provide the validation implementation by overriding this
56988     validateValue : function(value){
56989         if(this.allowBlank){
56990             return true;
56991         }
56992         
56993         if(this.isConfirmed){
56994             return true;
56995         }
56996         return false;
56997     }
56998 });/*
56999  * Based on:
57000  * Ext JS Library 1.1.1
57001  * Copyright(c) 2006-2007, Ext JS, LLC.
57002  *
57003  * Originally Released Under LGPL - original licence link has changed is not relivant.
57004  *
57005  * Fork - LGPL
57006  * <script type="text/javascript">
57007  */
57008  
57009
57010 /**
57011  * @class Roo.form.ComboBox
57012  * @extends Roo.form.TriggerField
57013  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
57014  * @constructor
57015  * Create a new ComboBox.
57016  * @param {Object} config Configuration options
57017  */
57018 Roo.form.Select = function(config){
57019     Roo.form.Select.superclass.constructor.call(this, config);
57020      
57021 };
57022
57023 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
57024     /**
57025      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
57026      */
57027     /**
57028      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
57029      * rendering into an Roo.Editor, defaults to false)
57030      */
57031     /**
57032      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
57033      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
57034      */
57035     /**
57036      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
57037      */
57038     /**
57039      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
57040      * the dropdown list (defaults to undefined, with no header element)
57041      */
57042
57043      /**
57044      * @cfg {String/Roo.Template} tpl The template to use to render the output
57045      */
57046      
57047     // private
57048     defaultAutoCreate : {tag: "select"  },
57049     /**
57050      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
57051      */
57052     listWidth: undefined,
57053     /**
57054      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
57055      * mode = 'remote' or 'text' if mode = 'local')
57056      */
57057     displayField: undefined,
57058     /**
57059      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
57060      * mode = 'remote' or 'value' if mode = 'local'). 
57061      * Note: use of a valueField requires the user make a selection
57062      * in order for a value to be mapped.
57063      */
57064     valueField: undefined,
57065     
57066     
57067     /**
57068      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
57069      * field's data value (defaults to the underlying DOM element's name)
57070      */
57071     hiddenName: undefined,
57072     /**
57073      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
57074      */
57075     listClass: '',
57076     /**
57077      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
57078      */
57079     selectedClass: 'x-combo-selected',
57080     /**
57081      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
57082      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
57083      * which displays a downward arrow icon).
57084      */
57085     triggerClass : 'x-form-arrow-trigger',
57086     /**
57087      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
57088      */
57089     shadow:'sides',
57090     /**
57091      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
57092      * anchor positions (defaults to 'tl-bl')
57093      */
57094     listAlign: 'tl-bl?',
57095     /**
57096      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
57097      */
57098     maxHeight: 300,
57099     /**
57100      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
57101      * query specified by the allQuery config option (defaults to 'query')
57102      */
57103     triggerAction: 'query',
57104     /**
57105      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
57106      * (defaults to 4, does not apply if editable = false)
57107      */
57108     minChars : 4,
57109     /**
57110      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
57111      * delay (typeAheadDelay) if it matches a known value (defaults to false)
57112      */
57113     typeAhead: false,
57114     /**
57115      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
57116      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
57117      */
57118     queryDelay: 500,
57119     /**
57120      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
57121      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
57122      */
57123     pageSize: 0,
57124     /**
57125      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
57126      * when editable = true (defaults to false)
57127      */
57128     selectOnFocus:false,
57129     /**
57130      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
57131      */
57132     queryParam: 'query',
57133     /**
57134      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
57135      * when mode = 'remote' (defaults to 'Loading...')
57136      */
57137     loadingText: 'Loading...',
57138     /**
57139      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
57140      */
57141     resizable: false,
57142     /**
57143      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
57144      */
57145     handleHeight : 8,
57146     /**
57147      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
57148      * traditional select (defaults to true)
57149      */
57150     editable: true,
57151     /**
57152      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
57153      */
57154     allQuery: '',
57155     /**
57156      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
57157      */
57158     mode: 'remote',
57159     /**
57160      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
57161      * listWidth has a higher value)
57162      */
57163     minListWidth : 70,
57164     /**
57165      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
57166      * allow the user to set arbitrary text into the field (defaults to false)
57167      */
57168     forceSelection:false,
57169     /**
57170      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
57171      * if typeAhead = true (defaults to 250)
57172      */
57173     typeAheadDelay : 250,
57174     /**
57175      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
57176      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
57177      */
57178     valueNotFoundText : undefined,
57179     
57180     /**
57181      * @cfg {String} defaultValue The value displayed after loading the store.
57182      */
57183     defaultValue: '',
57184     
57185     /**
57186      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
57187      */
57188     blockFocus : false,
57189     
57190     /**
57191      * @cfg {Boolean} disableClear Disable showing of clear button.
57192      */
57193     disableClear : false,
57194     /**
57195      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
57196      */
57197     alwaysQuery : false,
57198     
57199     //private
57200     addicon : false,
57201     editicon: false,
57202     
57203     // element that contains real text value.. (when hidden is used..)
57204      
57205     // private
57206     onRender : function(ct, position){
57207         Roo.form.Field.prototype.onRender.call(this, ct, position);
57208         
57209         if(this.store){
57210             this.store.on('beforeload', this.onBeforeLoad, this);
57211             this.store.on('load', this.onLoad, this);
57212             this.store.on('loadexception', this.onLoadException, this);
57213             this.store.load({});
57214         }
57215         
57216         
57217         
57218     },
57219
57220     // private
57221     initEvents : function(){
57222         //Roo.form.ComboBox.superclass.initEvents.call(this);
57223  
57224     },
57225
57226     onDestroy : function(){
57227        
57228         if(this.store){
57229             this.store.un('beforeload', this.onBeforeLoad, this);
57230             this.store.un('load', this.onLoad, this);
57231             this.store.un('loadexception', this.onLoadException, this);
57232         }
57233         //Roo.form.ComboBox.superclass.onDestroy.call(this);
57234     },
57235
57236     // private
57237     fireKey : function(e){
57238         if(e.isNavKeyPress() && !this.list.isVisible()){
57239             this.fireEvent("specialkey", this, e);
57240         }
57241     },
57242
57243     // private
57244     onResize: function(w, h){
57245         
57246         return; 
57247     
57248         
57249     },
57250
57251     /**
57252      * Allow or prevent the user from directly editing the field text.  If false is passed,
57253      * the user will only be able to select from the items defined in the dropdown list.  This method
57254      * is the runtime equivalent of setting the 'editable' config option at config time.
57255      * @param {Boolean} value True to allow the user to directly edit the field text
57256      */
57257     setEditable : function(value){
57258          
57259     },
57260
57261     // private
57262     onBeforeLoad : function(){
57263         
57264         Roo.log("Select before load");
57265         return;
57266     
57267         this.innerList.update(this.loadingText ?
57268                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
57269         //this.restrictHeight();
57270         this.selectedIndex = -1;
57271     },
57272
57273     // private
57274     onLoad : function(){
57275
57276     
57277         var dom = this.el.dom;
57278         dom.innerHTML = '';
57279          var od = dom.ownerDocument;
57280          
57281         if (this.emptyText) {
57282             var op = od.createElement('option');
57283             op.setAttribute('value', '');
57284             op.innerHTML = String.format('{0}', this.emptyText);
57285             dom.appendChild(op);
57286         }
57287         if(this.store.getCount() > 0){
57288            
57289             var vf = this.valueField;
57290             var df = this.displayField;
57291             this.store.data.each(function(r) {
57292                 // which colmsn to use... testing - cdoe / title..
57293                 var op = od.createElement('option');
57294                 op.setAttribute('value', r.data[vf]);
57295                 op.innerHTML = String.format('{0}', r.data[df]);
57296                 dom.appendChild(op);
57297             });
57298             if (typeof(this.defaultValue != 'undefined')) {
57299                 this.setValue(this.defaultValue);
57300             }
57301             
57302              
57303         }else{
57304             //this.onEmptyResults();
57305         }
57306         //this.el.focus();
57307     },
57308     // private
57309     onLoadException : function()
57310     {
57311         dom.innerHTML = '';
57312             
57313         Roo.log("Select on load exception");
57314         return;
57315     
57316         this.collapse();
57317         Roo.log(this.store.reader.jsonData);
57318         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57319             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57320         }
57321         
57322         
57323     },
57324     // private
57325     onTypeAhead : function(){
57326          
57327     },
57328
57329     // private
57330     onSelect : function(record, index){
57331         Roo.log('on select?');
57332         return;
57333         if(this.fireEvent('beforeselect', this, record, index) !== false){
57334             this.setFromData(index > -1 ? record.data : false);
57335             this.collapse();
57336             this.fireEvent('select', this, record, index);
57337         }
57338     },
57339
57340     /**
57341      * Returns the currently selected field value or empty string if no value is set.
57342      * @return {String} value The selected value
57343      */
57344     getValue : function(){
57345         var dom = this.el.dom;
57346         this.value = dom.options[dom.selectedIndex].value;
57347         return this.value;
57348         
57349     },
57350
57351     /**
57352      * Clears any text/value currently set in the field
57353      */
57354     clearValue : function(){
57355         this.value = '';
57356         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
57357         
57358     },
57359
57360     /**
57361      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
57362      * will be displayed in the field.  If the value does not match the data value of an existing item,
57363      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
57364      * Otherwise the field will be blank (although the value will still be set).
57365      * @param {String} value The value to match
57366      */
57367     setValue : function(v){
57368         var d = this.el.dom;
57369         for (var i =0; i < d.options.length;i++) {
57370             if (v == d.options[i].value) {
57371                 d.selectedIndex = i;
57372                 this.value = v;
57373                 return;
57374             }
57375         }
57376         this.clearValue();
57377     },
57378     /**
57379      * @property {Object} the last set data for the element
57380      */
57381     
57382     lastData : false,
57383     /**
57384      * Sets the value of the field based on a object which is related to the record format for the store.
57385      * @param {Object} value the value to set as. or false on reset?
57386      */
57387     setFromData : function(o){
57388         Roo.log('setfrom data?');
57389          
57390         
57391         
57392     },
57393     // private
57394     reset : function(){
57395         this.clearValue();
57396     },
57397     // private
57398     findRecord : function(prop, value){
57399         
57400         return false;
57401     
57402         var record;
57403         if(this.store.getCount() > 0){
57404             this.store.each(function(r){
57405                 if(r.data[prop] == value){
57406                     record = r;
57407                     return false;
57408                 }
57409                 return true;
57410             });
57411         }
57412         return record;
57413     },
57414     
57415     getName: function()
57416     {
57417         // returns hidden if it's set..
57418         if (!this.rendered) {return ''};
57419         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
57420         
57421     },
57422      
57423
57424     
57425
57426     // private
57427     onEmptyResults : function(){
57428         Roo.log('empty results');
57429         //this.collapse();
57430     },
57431
57432     /**
57433      * Returns true if the dropdown list is expanded, else false.
57434      */
57435     isExpanded : function(){
57436         return false;
57437     },
57438
57439     /**
57440      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
57441      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57442      * @param {String} value The data value of the item to select
57443      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57444      * selected item if it is not currently in view (defaults to true)
57445      * @return {Boolean} True if the value matched an item in the list, else false
57446      */
57447     selectByValue : function(v, scrollIntoView){
57448         Roo.log('select By Value');
57449         return false;
57450     
57451         if(v !== undefined && v !== null){
57452             var r = this.findRecord(this.valueField || this.displayField, v);
57453             if(r){
57454                 this.select(this.store.indexOf(r), scrollIntoView);
57455                 return true;
57456             }
57457         }
57458         return false;
57459     },
57460
57461     /**
57462      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
57463      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57464      * @param {Number} index The zero-based index of the list item to select
57465      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57466      * selected item if it is not currently in view (defaults to true)
57467      */
57468     select : function(index, scrollIntoView){
57469         Roo.log('select ');
57470         return  ;
57471         
57472         this.selectedIndex = index;
57473         this.view.select(index);
57474         if(scrollIntoView !== false){
57475             var el = this.view.getNode(index);
57476             if(el){
57477                 this.innerList.scrollChildIntoView(el, false);
57478             }
57479         }
57480     },
57481
57482       
57483
57484     // private
57485     validateBlur : function(){
57486         
57487         return;
57488         
57489     },
57490
57491     // private
57492     initQuery : function(){
57493         this.doQuery(this.getRawValue());
57494     },
57495
57496     // private
57497     doForce : function(){
57498         if(this.el.dom.value.length > 0){
57499             this.el.dom.value =
57500                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
57501              
57502         }
57503     },
57504
57505     /**
57506      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
57507      * query allowing the query action to be canceled if needed.
57508      * @param {String} query The SQL query to execute
57509      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
57510      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
57511      * saved in the current store (defaults to false)
57512      */
57513     doQuery : function(q, forceAll){
57514         
57515         Roo.log('doQuery?');
57516         if(q === undefined || q === null){
57517             q = '';
57518         }
57519         var qe = {
57520             query: q,
57521             forceAll: forceAll,
57522             combo: this,
57523             cancel:false
57524         };
57525         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
57526             return false;
57527         }
57528         q = qe.query;
57529         forceAll = qe.forceAll;
57530         if(forceAll === true || (q.length >= this.minChars)){
57531             if(this.lastQuery != q || this.alwaysQuery){
57532                 this.lastQuery = q;
57533                 if(this.mode == 'local'){
57534                     this.selectedIndex = -1;
57535                     if(forceAll){
57536                         this.store.clearFilter();
57537                     }else{
57538                         this.store.filter(this.displayField, q);
57539                     }
57540                     this.onLoad();
57541                 }else{
57542                     this.store.baseParams[this.queryParam] = q;
57543                     this.store.load({
57544                         params: this.getParams(q)
57545                     });
57546                     this.expand();
57547                 }
57548             }else{
57549                 this.selectedIndex = -1;
57550                 this.onLoad();   
57551             }
57552         }
57553     },
57554
57555     // private
57556     getParams : function(q){
57557         var p = {};
57558         //p[this.queryParam] = q;
57559         if(this.pageSize){
57560             p.start = 0;
57561             p.limit = this.pageSize;
57562         }
57563         return p;
57564     },
57565
57566     /**
57567      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
57568      */
57569     collapse : function(){
57570         
57571     },
57572
57573     // private
57574     collapseIf : function(e){
57575         
57576     },
57577
57578     /**
57579      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
57580      */
57581     expand : function(){
57582         
57583     } ,
57584
57585     // private
57586      
57587
57588     /** 
57589     * @cfg {Boolean} grow 
57590     * @hide 
57591     */
57592     /** 
57593     * @cfg {Number} growMin 
57594     * @hide 
57595     */
57596     /** 
57597     * @cfg {Number} growMax 
57598     * @hide 
57599     */
57600     /**
57601      * @hide
57602      * @method autoSize
57603      */
57604     
57605     setWidth : function()
57606     {
57607         
57608     },
57609     getResizeEl : function(){
57610         return this.el;
57611     }
57612 });//<script type="text/javasscript">
57613  
57614
57615 /**
57616  * @class Roo.DDView
57617  * A DnD enabled version of Roo.View.
57618  * @param {Element/String} container The Element in which to create the View.
57619  * @param {String} tpl The template string used to create the markup for each element of the View
57620  * @param {Object} config The configuration properties. These include all the config options of
57621  * {@link Roo.View} plus some specific to this class.<br>
57622  * <p>
57623  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
57624  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
57625  * <p>
57626  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
57627 .x-view-drag-insert-above {
57628         border-top:1px dotted #3366cc;
57629 }
57630 .x-view-drag-insert-below {
57631         border-bottom:1px dotted #3366cc;
57632 }
57633 </code></pre>
57634  * 
57635  */
57636  
57637 Roo.DDView = function(container, tpl, config) {
57638     Roo.DDView.superclass.constructor.apply(this, arguments);
57639     this.getEl().setStyle("outline", "0px none");
57640     this.getEl().unselectable();
57641     if (this.dragGroup) {
57642         this.setDraggable(this.dragGroup.split(","));
57643     }
57644     if (this.dropGroup) {
57645         this.setDroppable(this.dropGroup.split(","));
57646     }
57647     if (this.deletable) {
57648         this.setDeletable();
57649     }
57650     this.isDirtyFlag = false;
57651         this.addEvents({
57652                 "drop" : true
57653         });
57654 };
57655
57656 Roo.extend(Roo.DDView, Roo.View, {
57657 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
57658 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
57659 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
57660 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
57661
57662         isFormField: true,
57663
57664         reset: Roo.emptyFn,
57665         
57666         clearInvalid: Roo.form.Field.prototype.clearInvalid,
57667
57668         validate: function() {
57669                 return true;
57670         },
57671         
57672         destroy: function() {
57673                 this.purgeListeners();
57674                 this.getEl.removeAllListeners();
57675                 this.getEl().remove();
57676                 if (this.dragZone) {
57677                         if (this.dragZone.destroy) {
57678                                 this.dragZone.destroy();
57679                         }
57680                 }
57681                 if (this.dropZone) {
57682                         if (this.dropZone.destroy) {
57683                                 this.dropZone.destroy();
57684                         }
57685                 }
57686         },
57687
57688 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
57689         getName: function() {
57690                 return this.name;
57691         },
57692
57693 /**     Loads the View from a JSON string representing the Records to put into the Store. */
57694         setValue: function(v) {
57695                 if (!this.store) {
57696                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
57697                 }
57698                 var data = {};
57699                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
57700                 this.store.proxy = new Roo.data.MemoryProxy(data);
57701                 this.store.load();
57702         },
57703
57704 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
57705         getValue: function() {
57706                 var result = '(';
57707                 this.store.each(function(rec) {
57708                         result += rec.id + ',';
57709                 });
57710                 return result.substr(0, result.length - 1) + ')';
57711         },
57712         
57713         getIds: function() {
57714                 var i = 0, result = new Array(this.store.getCount());
57715                 this.store.each(function(rec) {
57716                         result[i++] = rec.id;
57717                 });
57718                 return result;
57719         },
57720         
57721         isDirty: function() {
57722                 return this.isDirtyFlag;
57723         },
57724
57725 /**
57726  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
57727  *      whole Element becomes the target, and this causes the drop gesture to append.
57728  */
57729     getTargetFromEvent : function(e) {
57730                 var target = e.getTarget();
57731                 while ((target !== null) && (target.parentNode != this.el.dom)) {
57732                 target = target.parentNode;
57733                 }
57734                 if (!target) {
57735                         target = this.el.dom.lastChild || this.el.dom;
57736                 }
57737                 return target;
57738     },
57739
57740 /**
57741  *      Create the drag data which consists of an object which has the property "ddel" as
57742  *      the drag proxy element. 
57743  */
57744     getDragData : function(e) {
57745         var target = this.findItemFromChild(e.getTarget());
57746                 if(target) {
57747                         this.handleSelection(e);
57748                         var selNodes = this.getSelectedNodes();
57749             var dragData = {
57750                 source: this,
57751                 copy: this.copy || (this.allowCopy && e.ctrlKey),
57752                 nodes: selNodes,
57753                 records: []
57754                         };
57755                         var selectedIndices = this.getSelectedIndexes();
57756                         for (var i = 0; i < selectedIndices.length; i++) {
57757                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
57758                         }
57759                         if (selNodes.length == 1) {
57760                                 dragData.ddel = target.cloneNode(true); // the div element
57761                         } else {
57762                                 var div = document.createElement('div'); // create the multi element drag "ghost"
57763                                 div.className = 'multi-proxy';
57764                                 for (var i = 0, len = selNodes.length; i < len; i++) {
57765                                         div.appendChild(selNodes[i].cloneNode(true));
57766                                 }
57767                                 dragData.ddel = div;
57768                         }
57769             //console.log(dragData)
57770             //console.log(dragData.ddel.innerHTML)
57771                         return dragData;
57772                 }
57773         //console.log('nodragData')
57774                 return false;
57775     },
57776     
57777 /**     Specify to which ddGroup items in this DDView may be dragged. */
57778     setDraggable: function(ddGroup) {
57779         if (ddGroup instanceof Array) {
57780                 Roo.each(ddGroup, this.setDraggable, this);
57781                 return;
57782         }
57783         if (this.dragZone) {
57784                 this.dragZone.addToGroup(ddGroup);
57785         } else {
57786                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
57787                                 containerScroll: true,
57788                                 ddGroup: ddGroup 
57789
57790                         });
57791 //                      Draggability implies selection. DragZone's mousedown selects the element.
57792                         if (!this.multiSelect) { this.singleSelect = true; }
57793
57794 //                      Wire the DragZone's handlers up to methods in *this*
57795                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
57796                 }
57797     },
57798
57799 /**     Specify from which ddGroup this DDView accepts drops. */
57800     setDroppable: function(ddGroup) {
57801         if (ddGroup instanceof Array) {
57802                 Roo.each(ddGroup, this.setDroppable, this);
57803                 return;
57804         }
57805         if (this.dropZone) {
57806                 this.dropZone.addToGroup(ddGroup);
57807         } else {
57808                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
57809                                 containerScroll: true,
57810                                 ddGroup: ddGroup
57811                         });
57812
57813 //                      Wire the DropZone's handlers up to methods in *this*
57814                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
57815                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
57816                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
57817                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
57818                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
57819                 }
57820     },
57821
57822 /**     Decide whether to drop above or below a View node. */
57823     getDropPoint : function(e, n, dd){
57824         if (n == this.el.dom) { return "above"; }
57825                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
57826                 var c = t + (b - t) / 2;
57827                 var y = Roo.lib.Event.getPageY(e);
57828                 if(y <= c) {
57829                         return "above";
57830                 }else{
57831                         return "below";
57832                 }
57833     },
57834
57835     onNodeEnter : function(n, dd, e, data){
57836                 return false;
57837     },
57838     
57839     onNodeOver : function(n, dd, e, data){
57840                 var pt = this.getDropPoint(e, n, dd);
57841                 // set the insert point style on the target node
57842                 var dragElClass = this.dropNotAllowed;
57843                 if (pt) {
57844                         var targetElClass;
57845                         if (pt == "above"){
57846                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
57847                                 targetElClass = "x-view-drag-insert-above";
57848                         } else {
57849                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
57850                                 targetElClass = "x-view-drag-insert-below";
57851                         }
57852                         if (this.lastInsertClass != targetElClass){
57853                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
57854                                 this.lastInsertClass = targetElClass;
57855                         }
57856                 }
57857                 return dragElClass;
57858         },
57859
57860     onNodeOut : function(n, dd, e, data){
57861                 this.removeDropIndicators(n);
57862     },
57863
57864     onNodeDrop : function(n, dd, e, data){
57865         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
57866                 return false;
57867         }
57868         var pt = this.getDropPoint(e, n, dd);
57869                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
57870                 if (pt == "below") { insertAt++; }
57871                 for (var i = 0; i < data.records.length; i++) {
57872                         var r = data.records[i];
57873                         var dup = this.store.getById(r.id);
57874                         if (dup && (dd != this.dragZone)) {
57875                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
57876                         } else {
57877                                 if (data.copy) {
57878                                         this.store.insert(insertAt++, r.copy());
57879                                 } else {
57880                                         data.source.isDirtyFlag = true;
57881                                         r.store.remove(r);
57882                                         this.store.insert(insertAt++, r);
57883                                 }
57884                                 this.isDirtyFlag = true;
57885                         }
57886                 }
57887                 this.dragZone.cachedTarget = null;
57888                 return true;
57889     },
57890
57891     removeDropIndicators : function(n){
57892                 if(n){
57893                         Roo.fly(n).removeClass([
57894                                 "x-view-drag-insert-above",
57895                                 "x-view-drag-insert-below"]);
57896                         this.lastInsertClass = "_noclass";
57897                 }
57898     },
57899
57900 /**
57901  *      Utility method. Add a delete option to the DDView's context menu.
57902  *      @param {String} imageUrl The URL of the "delete" icon image.
57903  */
57904         setDeletable: function(imageUrl) {
57905                 if (!this.singleSelect && !this.multiSelect) {
57906                         this.singleSelect = true;
57907                 }
57908                 var c = this.getContextMenu();
57909                 this.contextMenu.on("itemclick", function(item) {
57910                         switch (item.id) {
57911                                 case "delete":
57912                                         this.remove(this.getSelectedIndexes());
57913                                         break;
57914                         }
57915                 }, this);
57916                 this.contextMenu.add({
57917                         icon: imageUrl,
57918                         id: "delete",
57919                         text: 'Delete'
57920                 });
57921         },
57922         
57923 /**     Return the context menu for this DDView. */
57924         getContextMenu: function() {
57925                 if (!this.contextMenu) {
57926 //                      Create the View's context menu
57927                         this.contextMenu = new Roo.menu.Menu({
57928                                 id: this.id + "-contextmenu"
57929                         });
57930                         this.el.on("contextmenu", this.showContextMenu, this);
57931                 }
57932                 return this.contextMenu;
57933         },
57934         
57935         disableContextMenu: function() {
57936                 if (this.contextMenu) {
57937                         this.el.un("contextmenu", this.showContextMenu, this);
57938                 }
57939         },
57940
57941         showContextMenu: function(e, item) {
57942         item = this.findItemFromChild(e.getTarget());
57943                 if (item) {
57944                         e.stopEvent();
57945                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
57946                         this.contextMenu.showAt(e.getXY());
57947             }
57948     },
57949
57950 /**
57951  *      Remove {@link Roo.data.Record}s at the specified indices.
57952  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
57953  */
57954     remove: function(selectedIndices) {
57955                 selectedIndices = [].concat(selectedIndices);
57956                 for (var i = 0; i < selectedIndices.length; i++) {
57957                         var rec = this.store.getAt(selectedIndices[i]);
57958                         this.store.remove(rec);
57959                 }
57960     },
57961
57962 /**
57963  *      Double click fires the event, but also, if this is draggable, and there is only one other
57964  *      related DropZone, it transfers the selected node.
57965  */
57966     onDblClick : function(e){
57967         var item = this.findItemFromChild(e.getTarget());
57968         if(item){
57969             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
57970                 return false;
57971             }
57972             if (this.dragGroup) {
57973                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
57974                     while (targets.indexOf(this.dropZone) > -1) {
57975                             targets.remove(this.dropZone);
57976                                 }
57977                     if (targets.length == 1) {
57978                                         this.dragZone.cachedTarget = null;
57979                         var el = Roo.get(targets[0].getEl());
57980                         var box = el.getBox(true);
57981                         targets[0].onNodeDrop(el.dom, {
57982                                 target: el.dom,
57983                                 xy: [box.x, box.y + box.height - 1]
57984                         }, null, this.getDragData(e));
57985                     }
57986                 }
57987         }
57988     },
57989     
57990     handleSelection: function(e) {
57991                 this.dragZone.cachedTarget = null;
57992         var item = this.findItemFromChild(e.getTarget());
57993         if (!item) {
57994                 this.clearSelections(true);
57995                 return;
57996         }
57997                 if (item && (this.multiSelect || this.singleSelect)){
57998                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
57999                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
58000                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
58001                                 this.unselect(item);
58002                         } else {
58003                                 this.select(item, this.multiSelect && e.ctrlKey);
58004                                 this.lastSelection = item;
58005                         }
58006                 }
58007     },
58008
58009     onItemClick : function(item, index, e){
58010                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
58011                         return false;
58012                 }
58013                 return true;
58014     },
58015
58016     unselect : function(nodeInfo, suppressEvent){
58017                 var node = this.getNode(nodeInfo);
58018                 if(node && this.isSelected(node)){
58019                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
58020                                 Roo.fly(node).removeClass(this.selectedClass);
58021                                 this.selections.remove(node);
58022                                 if(!suppressEvent){
58023                                         this.fireEvent("selectionchange", this, this.selections);
58024                                 }
58025                         }
58026                 }
58027     }
58028 });
58029 /*
58030  * Based on:
58031  * Ext JS Library 1.1.1
58032  * Copyright(c) 2006-2007, Ext JS, LLC.
58033  *
58034  * Originally Released Under LGPL - original licence link has changed is not relivant.
58035  *
58036  * Fork - LGPL
58037  * <script type="text/javascript">
58038  */
58039  
58040 /**
58041  * @class Roo.LayoutManager
58042  * @extends Roo.util.Observable
58043  * Base class for layout managers.
58044  */
58045 Roo.LayoutManager = function(container, config){
58046     Roo.LayoutManager.superclass.constructor.call(this);
58047     this.el = Roo.get(container);
58048     // ie scrollbar fix
58049     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
58050         document.body.scroll = "no";
58051     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
58052         this.el.position('relative');
58053     }
58054     this.id = this.el.id;
58055     this.el.addClass("x-layout-container");
58056     /** false to disable window resize monitoring @type Boolean */
58057     this.monitorWindowResize = true;
58058     this.regions = {};
58059     this.addEvents({
58060         /**
58061          * @event layout
58062          * Fires when a layout is performed. 
58063          * @param {Roo.LayoutManager} this
58064          */
58065         "layout" : true,
58066         /**
58067          * @event regionresized
58068          * Fires when the user resizes a region. 
58069          * @param {Roo.LayoutRegion} region The resized region
58070          * @param {Number} newSize The new size (width for east/west, height for north/south)
58071          */
58072         "regionresized" : true,
58073         /**
58074          * @event regioncollapsed
58075          * Fires when a region is collapsed. 
58076          * @param {Roo.LayoutRegion} region The collapsed region
58077          */
58078         "regioncollapsed" : true,
58079         /**
58080          * @event regionexpanded
58081          * Fires when a region is expanded.  
58082          * @param {Roo.LayoutRegion} region The expanded region
58083          */
58084         "regionexpanded" : true
58085     });
58086     this.updating = false;
58087     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58088 };
58089
58090 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
58091     /**
58092      * Returns true if this layout is currently being updated
58093      * @return {Boolean}
58094      */
58095     isUpdating : function(){
58096         return this.updating; 
58097     },
58098     
58099     /**
58100      * Suspend the LayoutManager from doing auto-layouts while
58101      * making multiple add or remove calls
58102      */
58103     beginUpdate : function(){
58104         this.updating = true;    
58105     },
58106     
58107     /**
58108      * Restore auto-layouts and optionally disable the manager from performing a layout
58109      * @param {Boolean} noLayout true to disable a layout update 
58110      */
58111     endUpdate : function(noLayout){
58112         this.updating = false;
58113         if(!noLayout){
58114             this.layout();
58115         }    
58116     },
58117     
58118     layout: function(){
58119         
58120     },
58121     
58122     onRegionResized : function(region, newSize){
58123         this.fireEvent("regionresized", region, newSize);
58124         this.layout();
58125     },
58126     
58127     onRegionCollapsed : function(region){
58128         this.fireEvent("regioncollapsed", region);
58129     },
58130     
58131     onRegionExpanded : function(region){
58132         this.fireEvent("regionexpanded", region);
58133     },
58134         
58135     /**
58136      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
58137      * performs box-model adjustments.
58138      * @return {Object} The size as an object {width: (the width), height: (the height)}
58139      */
58140     getViewSize : function(){
58141         var size;
58142         if(this.el.dom != document.body){
58143             size = this.el.getSize();
58144         }else{
58145             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
58146         }
58147         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
58148         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
58149         return size;
58150     },
58151     
58152     /**
58153      * Returns the Element this layout is bound to.
58154      * @return {Roo.Element}
58155      */
58156     getEl : function(){
58157         return this.el;
58158     },
58159     
58160     /**
58161      * Returns the specified region.
58162      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
58163      * @return {Roo.LayoutRegion}
58164      */
58165     getRegion : function(target){
58166         return this.regions[target.toLowerCase()];
58167     },
58168     
58169     onWindowResize : function(){
58170         if(this.monitorWindowResize){
58171             this.layout();
58172         }
58173     }
58174 });/*
58175  * Based on:
58176  * Ext JS Library 1.1.1
58177  * Copyright(c) 2006-2007, Ext JS, LLC.
58178  *
58179  * Originally Released Under LGPL - original licence link has changed is not relivant.
58180  *
58181  * Fork - LGPL
58182  * <script type="text/javascript">
58183  */
58184 /**
58185  * @class Roo.BorderLayout
58186  * @extends Roo.LayoutManager
58187  * @children Roo.ContentPanel
58188  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
58189  * please see: <br><br>
58190  * <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>
58191  * <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>
58192  * Example:
58193  <pre><code>
58194  var layout = new Roo.BorderLayout(document.body, {
58195     north: {
58196         initialSize: 25,
58197         titlebar: false
58198     },
58199     west: {
58200         split:true,
58201         initialSize: 200,
58202         minSize: 175,
58203         maxSize: 400,
58204         titlebar: true,
58205         collapsible: true
58206     },
58207     east: {
58208         split:true,
58209         initialSize: 202,
58210         minSize: 175,
58211         maxSize: 400,
58212         titlebar: true,
58213         collapsible: true
58214     },
58215     south: {
58216         split:true,
58217         initialSize: 100,
58218         minSize: 100,
58219         maxSize: 200,
58220         titlebar: true,
58221         collapsible: true
58222     },
58223     center: {
58224         titlebar: true,
58225         autoScroll:true,
58226         resizeTabs: true,
58227         minTabWidth: 50,
58228         preferredTabWidth: 150
58229     }
58230 });
58231
58232 // shorthand
58233 var CP = Roo.ContentPanel;
58234
58235 layout.beginUpdate();
58236 layout.add("north", new CP("north", "North"));
58237 layout.add("south", new CP("south", {title: "South", closable: true}));
58238 layout.add("west", new CP("west", {title: "West"}));
58239 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
58240 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
58241 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
58242 layout.getRegion("center").showPanel("center1");
58243 layout.endUpdate();
58244 </code></pre>
58245
58246 <b>The container the layout is rendered into can be either the body element or any other element.
58247 If it is not the body element, the container needs to either be an absolute positioned element,
58248 or you will need to add "position:relative" to the css of the container.  You will also need to specify
58249 the container size if it is not the body element.</b>
58250
58251 * @constructor
58252 * Create a new BorderLayout
58253 * @param {String/HTMLElement/Element} container The container this layout is bound to
58254 * @param {Object} config Configuration options
58255  */
58256 Roo.BorderLayout = function(container, config){
58257     config = config || {};
58258     Roo.BorderLayout.superclass.constructor.call(this, container, config);
58259     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
58260     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
58261         var target = this.factory.validRegions[i];
58262         if(config[target]){
58263             this.addRegion(target, config[target]);
58264         }
58265     }
58266 };
58267
58268 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
58269         
58270         /**
58271          * @cfg {Roo.LayoutRegion} east
58272          */
58273         /**
58274          * @cfg {Roo.LayoutRegion} west
58275          */
58276         /**
58277          * @cfg {Roo.LayoutRegion} north
58278          */
58279         /**
58280          * @cfg {Roo.LayoutRegion} south
58281          */
58282         /**
58283          * @cfg {Roo.LayoutRegion} center
58284          */
58285     /**
58286      * Creates and adds a new region if it doesn't already exist.
58287      * @param {String} target The target region key (north, south, east, west or center).
58288      * @param {Object} config The regions config object
58289      * @return {BorderLayoutRegion} The new region
58290      */
58291     addRegion : function(target, config){
58292         if(!this.regions[target]){
58293             var r = this.factory.create(target, this, config);
58294             this.bindRegion(target, r);
58295         }
58296         return this.regions[target];
58297     },
58298
58299     // private (kinda)
58300     bindRegion : function(name, r){
58301         this.regions[name] = r;
58302         r.on("visibilitychange", this.layout, this);
58303         r.on("paneladded", this.layout, this);
58304         r.on("panelremoved", this.layout, this);
58305         r.on("invalidated", this.layout, this);
58306         r.on("resized", this.onRegionResized, this);
58307         r.on("collapsed", this.onRegionCollapsed, this);
58308         r.on("expanded", this.onRegionExpanded, this);
58309     },
58310
58311     /**
58312      * Performs a layout update.
58313      */
58314     layout : function(){
58315         if(this.updating) {
58316             return;
58317         }
58318         var size = this.getViewSize();
58319         var w = size.width;
58320         var h = size.height;
58321         var centerW = w;
58322         var centerH = h;
58323         var centerY = 0;
58324         var centerX = 0;
58325         //var x = 0, y = 0;
58326
58327         var rs = this.regions;
58328         var north = rs["north"];
58329         var south = rs["south"]; 
58330         var west = rs["west"];
58331         var east = rs["east"];
58332         var center = rs["center"];
58333         //if(this.hideOnLayout){ // not supported anymore
58334             //c.el.setStyle("display", "none");
58335         //}
58336         if(north && north.isVisible()){
58337             var b = north.getBox();
58338             var m = north.getMargins();
58339             b.width = w - (m.left+m.right);
58340             b.x = m.left;
58341             b.y = m.top;
58342             centerY = b.height + b.y + m.bottom;
58343             centerH -= centerY;
58344             north.updateBox(this.safeBox(b));
58345         }
58346         if(south && south.isVisible()){
58347             var b = south.getBox();
58348             var m = south.getMargins();
58349             b.width = w - (m.left+m.right);
58350             b.x = m.left;
58351             var totalHeight = (b.height + m.top + m.bottom);
58352             b.y = h - totalHeight + m.top;
58353             centerH -= totalHeight;
58354             south.updateBox(this.safeBox(b));
58355         }
58356         if(west && west.isVisible()){
58357             var b = west.getBox();
58358             var m = west.getMargins();
58359             b.height = centerH - (m.top+m.bottom);
58360             b.x = m.left;
58361             b.y = centerY + m.top;
58362             var totalWidth = (b.width + m.left + m.right);
58363             centerX += totalWidth;
58364             centerW -= totalWidth;
58365             west.updateBox(this.safeBox(b));
58366         }
58367         if(east && east.isVisible()){
58368             var b = east.getBox();
58369             var m = east.getMargins();
58370             b.height = centerH - (m.top+m.bottom);
58371             var totalWidth = (b.width + m.left + m.right);
58372             b.x = w - totalWidth + m.left;
58373             b.y = centerY + m.top;
58374             centerW -= totalWidth;
58375             east.updateBox(this.safeBox(b));
58376         }
58377         if(center){
58378             var m = center.getMargins();
58379             var centerBox = {
58380                 x: centerX + m.left,
58381                 y: centerY + m.top,
58382                 width: centerW - (m.left+m.right),
58383                 height: centerH - (m.top+m.bottom)
58384             };
58385             //if(this.hideOnLayout){
58386                 //center.el.setStyle("display", "block");
58387             //}
58388             center.updateBox(this.safeBox(centerBox));
58389         }
58390         this.el.repaint();
58391         this.fireEvent("layout", this);
58392     },
58393
58394     // private
58395     safeBox : function(box){
58396         box.width = Math.max(0, box.width);
58397         box.height = Math.max(0, box.height);
58398         return box;
58399     },
58400
58401     /**
58402      * Adds a ContentPanel (or subclass) to this layout.
58403      * @param {String} target The target region key (north, south, east, west or center).
58404      * @param {Roo.ContentPanel} panel The panel to add
58405      * @return {Roo.ContentPanel} The added panel
58406      */
58407     add : function(target, panel){
58408          
58409         target = target.toLowerCase();
58410         return this.regions[target].add(panel);
58411     },
58412
58413     /**
58414      * Remove a ContentPanel (or subclass) to this layout.
58415      * @param {String} target The target region key (north, south, east, west or center).
58416      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
58417      * @return {Roo.ContentPanel} The removed panel
58418      */
58419     remove : function(target, panel){
58420         target = target.toLowerCase();
58421         return this.regions[target].remove(panel);
58422     },
58423
58424     /**
58425      * Searches all regions for a panel with the specified id
58426      * @param {String} panelId
58427      * @return {Roo.ContentPanel} The panel or null if it wasn't found
58428      */
58429     findPanel : function(panelId){
58430         var rs = this.regions;
58431         for(var target in rs){
58432             if(typeof rs[target] != "function"){
58433                 var p = rs[target].getPanel(panelId);
58434                 if(p){
58435                     return p;
58436                 }
58437             }
58438         }
58439         return null;
58440     },
58441
58442     /**
58443      * Searches all regions for a panel with the specified id and activates (shows) it.
58444      * @param {String/ContentPanel} panelId The panels id or the panel itself
58445      * @return {Roo.ContentPanel} The shown panel or null
58446      */
58447     showPanel : function(panelId) {
58448       var rs = this.regions;
58449       for(var target in rs){
58450          var r = rs[target];
58451          if(typeof r != "function"){
58452             if(r.hasPanel(panelId)){
58453                return r.showPanel(panelId);
58454             }
58455          }
58456       }
58457       return null;
58458    },
58459
58460    /**
58461      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
58462      * @param {Roo.state.Provider} provider (optional) An alternate state provider
58463      */
58464     restoreState : function(provider){
58465         if(!provider){
58466             provider = Roo.state.Manager;
58467         }
58468         var sm = new Roo.LayoutStateManager();
58469         sm.init(this, provider);
58470     },
58471
58472     /**
58473      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
58474      * object should contain properties for each region to add ContentPanels to, and each property's value should be
58475      * a valid ContentPanel config object.  Example:
58476      * <pre><code>
58477 // Create the main layout
58478 var layout = new Roo.BorderLayout('main-ct', {
58479     west: {
58480         split:true,
58481         minSize: 175,
58482         titlebar: true
58483     },
58484     center: {
58485         title:'Components'
58486     }
58487 }, 'main-ct');
58488
58489 // Create and add multiple ContentPanels at once via configs
58490 layout.batchAdd({
58491    west: {
58492        id: 'source-files',
58493        autoCreate:true,
58494        title:'Ext Source Files',
58495        autoScroll:true,
58496        fitToFrame:true
58497    },
58498    center : {
58499        el: cview,
58500        autoScroll:true,
58501        fitToFrame:true,
58502        toolbar: tb,
58503        resizeEl:'cbody'
58504    }
58505 });
58506 </code></pre>
58507      * @param {Object} regions An object containing ContentPanel configs by region name
58508      */
58509     batchAdd : function(regions){
58510         this.beginUpdate();
58511         for(var rname in regions){
58512             var lr = this.regions[rname];
58513             if(lr){
58514                 this.addTypedPanels(lr, regions[rname]);
58515             }
58516         }
58517         this.endUpdate();
58518     },
58519
58520     // private
58521     addTypedPanels : function(lr, ps){
58522         if(typeof ps == 'string'){
58523             lr.add(new Roo.ContentPanel(ps));
58524         }
58525         else if(ps instanceof Array){
58526             for(var i =0, len = ps.length; i < len; i++){
58527                 this.addTypedPanels(lr, ps[i]);
58528             }
58529         }
58530         else if(!ps.events){ // raw config?
58531             var el = ps.el;
58532             delete ps.el; // prevent conflict
58533             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
58534         }
58535         else {  // panel object assumed!
58536             lr.add(ps);
58537         }
58538     },
58539     /**
58540      * Adds a xtype elements to the layout.
58541      * <pre><code>
58542
58543 layout.addxtype({
58544        xtype : 'ContentPanel',
58545        region: 'west',
58546        items: [ .... ]
58547    }
58548 );
58549
58550 layout.addxtype({
58551         xtype : 'NestedLayoutPanel',
58552         region: 'west',
58553         layout: {
58554            center: { },
58555            west: { }   
58556         },
58557         items : [ ... list of content panels or nested layout panels.. ]
58558    }
58559 );
58560 </code></pre>
58561      * @param {Object} cfg Xtype definition of item to add.
58562      */
58563     addxtype : function(cfg)
58564     {
58565         // basically accepts a pannel...
58566         // can accept a layout region..!?!?
58567         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
58568         
58569         if (!cfg.xtype.match(/Panel$/)) {
58570             return false;
58571         }
58572         var ret = false;
58573         
58574         if (typeof(cfg.region) == 'undefined') {
58575             Roo.log("Failed to add Panel, region was not set");
58576             Roo.log(cfg);
58577             return false;
58578         }
58579         var region = cfg.region;
58580         delete cfg.region;
58581         
58582           
58583         var xitems = [];
58584         if (cfg.items) {
58585             xitems = cfg.items;
58586             delete cfg.items;
58587         }
58588         var nb = false;
58589         
58590         switch(cfg.xtype) 
58591         {
58592             case 'ContentPanel':  // ContentPanel (el, cfg)
58593             case 'ScrollPanel':  // ContentPanel (el, cfg)
58594             case 'ViewPanel': 
58595                 if(cfg.autoCreate) {
58596                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58597                 } else {
58598                     var el = this.el.createChild();
58599                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
58600                 }
58601                 
58602                 this.add(region, ret);
58603                 break;
58604             
58605             
58606             case 'TreePanel': // our new panel!
58607                 cfg.el = this.el.createChild();
58608                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58609                 this.add(region, ret);
58610                 break;
58611             
58612             case 'NestedLayoutPanel': 
58613                 // create a new Layout (which is  a Border Layout...
58614                 var el = this.el.createChild();
58615                 var clayout = cfg.layout;
58616                 delete cfg.layout;
58617                 clayout.items   = clayout.items  || [];
58618                 // replace this exitems with the clayout ones..
58619                 xitems = clayout.items;
58620                  
58621                 
58622                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
58623                     cfg.background = false;
58624                 }
58625                 var layout = new Roo.BorderLayout(el, clayout);
58626                 
58627                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
58628                 //console.log('adding nested layout panel '  + cfg.toSource());
58629                 this.add(region, ret);
58630                 nb = {}; /// find first...
58631                 break;
58632                 
58633             case 'GridPanel': 
58634             
58635                 // needs grid and region
58636                 
58637                 //var el = this.getRegion(region).el.createChild();
58638                 var el = this.el.createChild();
58639                 // create the grid first...
58640                 
58641                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
58642                 delete cfg.grid;
58643                 if (region == 'center' && this.active ) {
58644                     cfg.background = false;
58645                 }
58646                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
58647                 
58648                 this.add(region, ret);
58649                 if (cfg.background) {
58650                     ret.on('activate', function(gp) {
58651                         if (!gp.grid.rendered) {
58652                             gp.grid.render();
58653                         }
58654                     });
58655                 } else {
58656                     grid.render();
58657                 }
58658                 break;
58659            
58660            
58661            
58662                 
58663                 
58664                 
58665             default:
58666                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
58667                     
58668                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58669                     this.add(region, ret);
58670                 } else {
58671                 
58672                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
58673                     return null;
58674                 }
58675                 
58676              // GridPanel (grid, cfg)
58677             
58678         }
58679         this.beginUpdate();
58680         // add children..
58681         var region = '';
58682         var abn = {};
58683         Roo.each(xitems, function(i)  {
58684             region = nb && i.region ? i.region : false;
58685             
58686             var add = ret.addxtype(i);
58687            
58688             if (region) {
58689                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
58690                 if (!i.background) {
58691                     abn[region] = nb[region] ;
58692                 }
58693             }
58694             
58695         });
58696         this.endUpdate();
58697
58698         // make the last non-background panel active..
58699         //if (nb) { Roo.log(abn); }
58700         if (nb) {
58701             
58702             for(var r in abn) {
58703                 region = this.getRegion(r);
58704                 if (region) {
58705                     // tried using nb[r], but it does not work..
58706                      
58707                     region.showPanel(abn[r]);
58708                    
58709                 }
58710             }
58711         }
58712         return ret;
58713         
58714     }
58715 });
58716
58717 /**
58718  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
58719  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
58720  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
58721  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
58722  * <pre><code>
58723 // shorthand
58724 var CP = Roo.ContentPanel;
58725
58726 var layout = Roo.BorderLayout.create({
58727     north: {
58728         initialSize: 25,
58729         titlebar: false,
58730         panels: [new CP("north", "North")]
58731     },
58732     west: {
58733         split:true,
58734         initialSize: 200,
58735         minSize: 175,
58736         maxSize: 400,
58737         titlebar: true,
58738         collapsible: true,
58739         panels: [new CP("west", {title: "West"})]
58740     },
58741     east: {
58742         split:true,
58743         initialSize: 202,
58744         minSize: 175,
58745         maxSize: 400,
58746         titlebar: true,
58747         collapsible: true,
58748         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
58749     },
58750     south: {
58751         split:true,
58752         initialSize: 100,
58753         minSize: 100,
58754         maxSize: 200,
58755         titlebar: true,
58756         collapsible: true,
58757         panels: [new CP("south", {title: "South", closable: true})]
58758     },
58759     center: {
58760         titlebar: true,
58761         autoScroll:true,
58762         resizeTabs: true,
58763         minTabWidth: 50,
58764         preferredTabWidth: 150,
58765         panels: [
58766             new CP("center1", {title: "Close Me", closable: true}),
58767             new CP("center2", {title: "Center Panel", closable: false})
58768         ]
58769     }
58770 }, document.body);
58771
58772 layout.getRegion("center").showPanel("center1");
58773 </code></pre>
58774  * @param config
58775  * @param targetEl
58776  */
58777 Roo.BorderLayout.create = function(config, targetEl){
58778     var layout = new Roo.BorderLayout(targetEl || document.body, config);
58779     layout.beginUpdate();
58780     var regions = Roo.BorderLayout.RegionFactory.validRegions;
58781     for(var j = 0, jlen = regions.length; j < jlen; j++){
58782         var lr = regions[j];
58783         if(layout.regions[lr] && config[lr].panels){
58784             var r = layout.regions[lr];
58785             var ps = config[lr].panels;
58786             layout.addTypedPanels(r, ps);
58787         }
58788     }
58789     layout.endUpdate();
58790     return layout;
58791 };
58792
58793 // private
58794 Roo.BorderLayout.RegionFactory = {
58795     // private
58796     validRegions : ["north","south","east","west","center"],
58797
58798     // private
58799     create : function(target, mgr, config){
58800         target = target.toLowerCase();
58801         if(config.lightweight || config.basic){
58802             return new Roo.BasicLayoutRegion(mgr, config, target);
58803         }
58804         switch(target){
58805             case "north":
58806                 return new Roo.NorthLayoutRegion(mgr, config);
58807             case "south":
58808                 return new Roo.SouthLayoutRegion(mgr, config);
58809             case "east":
58810                 return new Roo.EastLayoutRegion(mgr, config);
58811             case "west":
58812                 return new Roo.WestLayoutRegion(mgr, config);
58813             case "center":
58814                 return new Roo.CenterLayoutRegion(mgr, config);
58815         }
58816         throw 'Layout region "'+target+'" not supported.';
58817     }
58818 };/*
58819  * Based on:
58820  * Ext JS Library 1.1.1
58821  * Copyright(c) 2006-2007, Ext JS, LLC.
58822  *
58823  * Originally Released Under LGPL - original licence link has changed is not relivant.
58824  *
58825  * Fork - LGPL
58826  * <script type="text/javascript">
58827  */
58828  
58829 /**
58830  * @class Roo.BasicLayoutRegion
58831  * @extends Roo.util.Observable
58832  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
58833  * and does not have a titlebar, tabs or any other features. All it does is size and position 
58834  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
58835  */
58836 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
58837     this.mgr = mgr;
58838     this.position  = pos;
58839     this.events = {
58840         /**
58841          * @scope Roo.BasicLayoutRegion
58842          */
58843         
58844         /**
58845          * @event beforeremove
58846          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
58847          * @param {Roo.LayoutRegion} this
58848          * @param {Roo.ContentPanel} panel The panel
58849          * @param {Object} e The cancel event object
58850          */
58851         "beforeremove" : true,
58852         /**
58853          * @event invalidated
58854          * Fires when the layout for this region is changed.
58855          * @param {Roo.LayoutRegion} this
58856          */
58857         "invalidated" : true,
58858         /**
58859          * @event visibilitychange
58860          * Fires when this region is shown or hidden 
58861          * @param {Roo.LayoutRegion} this
58862          * @param {Boolean} visibility true or false
58863          */
58864         "visibilitychange" : true,
58865         /**
58866          * @event paneladded
58867          * Fires when a panel is added. 
58868          * @param {Roo.LayoutRegion} this
58869          * @param {Roo.ContentPanel} panel The panel
58870          */
58871         "paneladded" : true,
58872         /**
58873          * @event panelremoved
58874          * Fires when a panel is removed. 
58875          * @param {Roo.LayoutRegion} this
58876          * @param {Roo.ContentPanel} panel The panel
58877          */
58878         "panelremoved" : true,
58879         /**
58880          * @event beforecollapse
58881          * Fires when this region before collapse.
58882          * @param {Roo.LayoutRegion} this
58883          */
58884         "beforecollapse" : true,
58885         /**
58886          * @event collapsed
58887          * Fires when this region is collapsed.
58888          * @param {Roo.LayoutRegion} this
58889          */
58890         "collapsed" : true,
58891         /**
58892          * @event expanded
58893          * Fires when this region is expanded.
58894          * @param {Roo.LayoutRegion} this
58895          */
58896         "expanded" : true,
58897         /**
58898          * @event slideshow
58899          * Fires when this region is slid into view.
58900          * @param {Roo.LayoutRegion} this
58901          */
58902         "slideshow" : true,
58903         /**
58904          * @event slidehide
58905          * Fires when this region slides out of view. 
58906          * @param {Roo.LayoutRegion} this
58907          */
58908         "slidehide" : true,
58909         /**
58910          * @event panelactivated
58911          * Fires when a panel is activated. 
58912          * @param {Roo.LayoutRegion} this
58913          * @param {Roo.ContentPanel} panel The activated panel
58914          */
58915         "panelactivated" : true,
58916         /**
58917          * @event resized
58918          * Fires when the user resizes this region. 
58919          * @param {Roo.LayoutRegion} this
58920          * @param {Number} newSize The new size (width for east/west, height for north/south)
58921          */
58922         "resized" : true
58923     };
58924     /** A collection of panels in this region. @type Roo.util.MixedCollection */
58925     this.panels = new Roo.util.MixedCollection();
58926     this.panels.getKey = this.getPanelId.createDelegate(this);
58927     this.box = null;
58928     this.activePanel = null;
58929     // ensure listeners are added...
58930     
58931     if (config.listeners || config.events) {
58932         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
58933             listeners : config.listeners || {},
58934             events : config.events || {}
58935         });
58936     }
58937     
58938     if(skipConfig !== true){
58939         this.applyConfig(config);
58940     }
58941 };
58942
58943 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
58944     getPanelId : function(p){
58945         return p.getId();
58946     },
58947     
58948     applyConfig : function(config){
58949         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
58950         this.config = config;
58951         
58952     },
58953     
58954     /**
58955      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
58956      * the width, for horizontal (north, south) the height.
58957      * @param {Number} newSize The new width or height
58958      */
58959     resizeTo : function(newSize){
58960         var el = this.el ? this.el :
58961                  (this.activePanel ? this.activePanel.getEl() : null);
58962         if(el){
58963             switch(this.position){
58964                 case "east":
58965                 case "west":
58966                     el.setWidth(newSize);
58967                     this.fireEvent("resized", this, newSize);
58968                 break;
58969                 case "north":
58970                 case "south":
58971                     el.setHeight(newSize);
58972                     this.fireEvent("resized", this, newSize);
58973                 break;                
58974             }
58975         }
58976     },
58977     
58978     getBox : function(){
58979         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
58980     },
58981     
58982     getMargins : function(){
58983         return this.margins;
58984     },
58985     
58986     updateBox : function(box){
58987         this.box = box;
58988         var el = this.activePanel.getEl();
58989         el.dom.style.left = box.x + "px";
58990         el.dom.style.top = box.y + "px";
58991         this.activePanel.setSize(box.width, box.height);
58992     },
58993     
58994     /**
58995      * Returns the container element for this region.
58996      * @return {Roo.Element}
58997      */
58998     getEl : function(){
58999         return this.activePanel;
59000     },
59001     
59002     /**
59003      * Returns true if this region is currently visible.
59004      * @return {Boolean}
59005      */
59006     isVisible : function(){
59007         return this.activePanel ? true : false;
59008     },
59009     
59010     setActivePanel : function(panel){
59011         panel = this.getPanel(panel);
59012         if(this.activePanel && this.activePanel != panel){
59013             this.activePanel.setActiveState(false);
59014             this.activePanel.getEl().setLeftTop(-10000,-10000);
59015         }
59016         this.activePanel = panel;
59017         panel.setActiveState(true);
59018         if(this.box){
59019             panel.setSize(this.box.width, this.box.height);
59020         }
59021         this.fireEvent("panelactivated", this, panel);
59022         this.fireEvent("invalidated");
59023     },
59024     
59025     /**
59026      * Show the specified panel.
59027      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
59028      * @return {Roo.ContentPanel} The shown panel or null
59029      */
59030     showPanel : function(panel){
59031         if(panel = this.getPanel(panel)){
59032             this.setActivePanel(panel);
59033         }
59034         return panel;
59035     },
59036     
59037     /**
59038      * Get the active panel for this region.
59039      * @return {Roo.ContentPanel} The active panel or null
59040      */
59041     getActivePanel : function(){
59042         return this.activePanel;
59043     },
59044     
59045     /**
59046      * Add the passed ContentPanel(s)
59047      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59048      * @return {Roo.ContentPanel} The panel added (if only one was added)
59049      */
59050     add : function(panel){
59051         if(arguments.length > 1){
59052             for(var i = 0, len = arguments.length; i < len; i++) {
59053                 this.add(arguments[i]);
59054             }
59055             return null;
59056         }
59057         if(this.hasPanel(panel)){
59058             this.showPanel(panel);
59059             return panel;
59060         }
59061         var el = panel.getEl();
59062         if(el.dom.parentNode != this.mgr.el.dom){
59063             this.mgr.el.dom.appendChild(el.dom);
59064         }
59065         if(panel.setRegion){
59066             panel.setRegion(this);
59067         }
59068         this.panels.add(panel);
59069         el.setStyle("position", "absolute");
59070         if(!panel.background){
59071             this.setActivePanel(panel);
59072             if(this.config.initialSize && this.panels.getCount()==1){
59073                 this.resizeTo(this.config.initialSize);
59074             }
59075         }
59076         this.fireEvent("paneladded", this, panel);
59077         return panel;
59078     },
59079     
59080     /**
59081      * Returns true if the panel is in this region.
59082      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59083      * @return {Boolean}
59084      */
59085     hasPanel : function(panel){
59086         if(typeof panel == "object"){ // must be panel obj
59087             panel = panel.getId();
59088         }
59089         return this.getPanel(panel) ? true : false;
59090     },
59091     
59092     /**
59093      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59094      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59095      * @param {Boolean} preservePanel Overrides the config preservePanel option
59096      * @return {Roo.ContentPanel} The panel that was removed
59097      */
59098     remove : function(panel, preservePanel){
59099         panel = this.getPanel(panel);
59100         if(!panel){
59101             return null;
59102         }
59103         var e = {};
59104         this.fireEvent("beforeremove", this, panel, e);
59105         if(e.cancel === true){
59106             return null;
59107         }
59108         var panelId = panel.getId();
59109         this.panels.removeKey(panelId);
59110         return panel;
59111     },
59112     
59113     /**
59114      * Returns the panel specified or null if it's not in this region.
59115      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59116      * @return {Roo.ContentPanel}
59117      */
59118     getPanel : function(id){
59119         if(typeof id == "object"){ // must be panel obj
59120             return id;
59121         }
59122         return this.panels.get(id);
59123     },
59124     
59125     /**
59126      * Returns this regions position (north/south/east/west/center).
59127      * @return {String} 
59128      */
59129     getPosition: function(){
59130         return this.position;    
59131     }
59132 });/*
59133  * Based on:
59134  * Ext JS Library 1.1.1
59135  * Copyright(c) 2006-2007, Ext JS, LLC.
59136  *
59137  * Originally Released Under LGPL - original licence link has changed is not relivant.
59138  *
59139  * Fork - LGPL
59140  * <script type="text/javascript">
59141  */
59142  
59143 /**
59144  * @class Roo.LayoutRegion
59145  * @extends Roo.BasicLayoutRegion
59146  * This class represents a region in a layout manager.
59147  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
59148  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
59149  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
59150  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
59151  * @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})
59152  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
59153  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
59154  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
59155  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
59156  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
59157  * @cfg {String}    title           The title for the region (overrides panel titles)
59158  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
59159  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
59160  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
59161  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
59162  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
59163  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
59164  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
59165  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
59166  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
59167  * @cfg {Boolean}   showPin         True to show a pin button
59168  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
59169  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
59170  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
59171  * @cfg {Number}    width           For East/West panels
59172  * @cfg {Number}    height          For North/South panels
59173  * @cfg {Boolean}   split           To show the splitter
59174  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
59175  */
59176 Roo.LayoutRegion = function(mgr, config, pos){
59177     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
59178     var dh = Roo.DomHelper;
59179     /** This region's container element 
59180     * @type Roo.Element */
59181     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
59182     /** This region's title element 
59183     * @type Roo.Element */
59184
59185     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
59186         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
59187         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
59188     ]}, true);
59189     this.titleEl.enableDisplayMode();
59190     /** This region's title text element 
59191     * @type HTMLElement */
59192     this.titleTextEl = this.titleEl.dom.firstChild;
59193     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
59194     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
59195     this.closeBtn.enableDisplayMode();
59196     this.closeBtn.on("click", this.closeClicked, this);
59197     this.closeBtn.hide();
59198
59199     this.createBody(config);
59200     this.visible = true;
59201     this.collapsed = false;
59202
59203     if(config.hideWhenEmpty){
59204         this.hide();
59205         this.on("paneladded", this.validateVisibility, this);
59206         this.on("panelremoved", this.validateVisibility, this);
59207     }
59208     this.applyConfig(config);
59209 };
59210
59211 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
59212
59213     createBody : function(){
59214         /** This region's body element 
59215         * @type Roo.Element */
59216         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
59217     },
59218
59219     applyConfig : function(c){
59220         if(c.collapsible && this.position != "center" && !this.collapsedEl){
59221             var dh = Roo.DomHelper;
59222             if(c.titlebar !== false){
59223                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
59224                 this.collapseBtn.on("click", this.collapse, this);
59225                 this.collapseBtn.enableDisplayMode();
59226
59227                 if(c.showPin === true || this.showPin){
59228                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
59229                     this.stickBtn.enableDisplayMode();
59230                     this.stickBtn.on("click", this.expand, this);
59231                     this.stickBtn.hide();
59232                 }
59233             }
59234             /** This region's collapsed element
59235             * @type Roo.Element */
59236             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
59237                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
59238             ]}, true);
59239             if(c.floatable !== false){
59240                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
59241                this.collapsedEl.on("click", this.collapseClick, this);
59242             }
59243
59244             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
59245                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
59246                    id: "message", unselectable: "on", style:{"float":"left"}});
59247                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
59248              }
59249             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
59250             this.expandBtn.on("click", this.expand, this);
59251         }
59252         if(this.collapseBtn){
59253             this.collapseBtn.setVisible(c.collapsible == true);
59254         }
59255         this.cmargins = c.cmargins || this.cmargins ||
59256                          (this.position == "west" || this.position == "east" ?
59257                              {top: 0, left: 2, right:2, bottom: 0} :
59258                              {top: 2, left: 0, right:0, bottom: 2});
59259         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59260         this.bottomTabs = c.tabPosition != "top";
59261         this.autoScroll = c.autoScroll || false;
59262         if(this.autoScroll){
59263             this.bodyEl.setStyle("overflow", "auto");
59264         }else{
59265             this.bodyEl.setStyle("overflow", "hidden");
59266         }
59267         //if(c.titlebar !== false){
59268             if((!c.titlebar && !c.title) || c.titlebar === false){
59269                 this.titleEl.hide();
59270             }else{
59271                 this.titleEl.show();
59272                 if(c.title){
59273                     this.titleTextEl.innerHTML = c.title;
59274                 }
59275             }
59276         //}
59277         this.duration = c.duration || .30;
59278         this.slideDuration = c.slideDuration || .45;
59279         this.config = c;
59280         if(c.collapsed){
59281             this.collapse(true);
59282         }
59283         if(c.hidden){
59284             this.hide();
59285         }
59286     },
59287     /**
59288      * Returns true if this region is currently visible.
59289      * @return {Boolean}
59290      */
59291     isVisible : function(){
59292         return this.visible;
59293     },
59294
59295     /**
59296      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
59297      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
59298      */
59299     setCollapsedTitle : function(title){
59300         title = title || "&#160;";
59301         if(this.collapsedTitleTextEl){
59302             this.collapsedTitleTextEl.innerHTML = title;
59303         }
59304     },
59305
59306     getBox : function(){
59307         var b;
59308         if(!this.collapsed){
59309             b = this.el.getBox(false, true);
59310         }else{
59311             b = this.collapsedEl.getBox(false, true);
59312         }
59313         return b;
59314     },
59315
59316     getMargins : function(){
59317         return this.collapsed ? this.cmargins : this.margins;
59318     },
59319
59320     highlight : function(){
59321         this.el.addClass("x-layout-panel-dragover");
59322     },
59323
59324     unhighlight : function(){
59325         this.el.removeClass("x-layout-panel-dragover");
59326     },
59327
59328     updateBox : function(box){
59329         this.box = box;
59330         if(!this.collapsed){
59331             this.el.dom.style.left = box.x + "px";
59332             this.el.dom.style.top = box.y + "px";
59333             this.updateBody(box.width, box.height);
59334         }else{
59335             this.collapsedEl.dom.style.left = box.x + "px";
59336             this.collapsedEl.dom.style.top = box.y + "px";
59337             this.collapsedEl.setSize(box.width, box.height);
59338         }
59339         if(this.tabs){
59340             this.tabs.autoSizeTabs();
59341         }
59342     },
59343
59344     updateBody : function(w, h){
59345         if(w !== null){
59346             this.el.setWidth(w);
59347             w -= this.el.getBorderWidth("rl");
59348             if(this.config.adjustments){
59349                 w += this.config.adjustments[0];
59350             }
59351         }
59352         if(h !== null){
59353             this.el.setHeight(h);
59354             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
59355             h -= this.el.getBorderWidth("tb");
59356             if(this.config.adjustments){
59357                 h += this.config.adjustments[1];
59358             }
59359             this.bodyEl.setHeight(h);
59360             if(this.tabs){
59361                 h = this.tabs.syncHeight(h);
59362             }
59363         }
59364         if(this.panelSize){
59365             w = w !== null ? w : this.panelSize.width;
59366             h = h !== null ? h : this.panelSize.height;
59367         }
59368         if(this.activePanel){
59369             var el = this.activePanel.getEl();
59370             w = w !== null ? w : el.getWidth();
59371             h = h !== null ? h : el.getHeight();
59372             this.panelSize = {width: w, height: h};
59373             this.activePanel.setSize(w, h);
59374         }
59375         if(Roo.isIE && this.tabs){
59376             this.tabs.el.repaint();
59377         }
59378     },
59379
59380     /**
59381      * Returns the container element for this region.
59382      * @return {Roo.Element}
59383      */
59384     getEl : function(){
59385         return this.el;
59386     },
59387
59388     /**
59389      * Hides this region.
59390      */
59391     hide : function(){
59392         if(!this.collapsed){
59393             this.el.dom.style.left = "-2000px";
59394             this.el.hide();
59395         }else{
59396             this.collapsedEl.dom.style.left = "-2000px";
59397             this.collapsedEl.hide();
59398         }
59399         this.visible = false;
59400         this.fireEvent("visibilitychange", this, false);
59401     },
59402
59403     /**
59404      * Shows this region if it was previously hidden.
59405      */
59406     show : function(){
59407         if(!this.collapsed){
59408             this.el.show();
59409         }else{
59410             this.collapsedEl.show();
59411         }
59412         this.visible = true;
59413         this.fireEvent("visibilitychange", this, true);
59414     },
59415
59416     closeClicked : function(){
59417         if(this.activePanel){
59418             this.remove(this.activePanel);
59419         }
59420     },
59421
59422     collapseClick : function(e){
59423         if(this.isSlid){
59424            e.stopPropagation();
59425            this.slideIn();
59426         }else{
59427            e.stopPropagation();
59428            this.slideOut();
59429         }
59430     },
59431
59432     /**
59433      * Collapses this region.
59434      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
59435      */
59436     collapse : function(skipAnim, skipCheck){
59437         if(this.collapsed) {
59438             return;
59439         }
59440         
59441         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
59442             
59443             this.collapsed = true;
59444             if(this.split){
59445                 this.split.el.hide();
59446             }
59447             if(this.config.animate && skipAnim !== true){
59448                 this.fireEvent("invalidated", this);
59449                 this.animateCollapse();
59450             }else{
59451                 this.el.setLocation(-20000,-20000);
59452                 this.el.hide();
59453                 this.collapsedEl.show();
59454                 this.fireEvent("collapsed", this);
59455                 this.fireEvent("invalidated", this);
59456             }
59457         }
59458         
59459     },
59460
59461     animateCollapse : function(){
59462         // overridden
59463     },
59464
59465     /**
59466      * Expands this region if it was previously collapsed.
59467      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
59468      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
59469      */
59470     expand : function(e, skipAnim){
59471         if(e) {
59472             e.stopPropagation();
59473         }
59474         if(!this.collapsed || this.el.hasActiveFx()) {
59475             return;
59476         }
59477         if(this.isSlid){
59478             this.afterSlideIn();
59479             skipAnim = true;
59480         }
59481         this.collapsed = false;
59482         if(this.config.animate && skipAnim !== true){
59483             this.animateExpand();
59484         }else{
59485             this.el.show();
59486             if(this.split){
59487                 this.split.el.show();
59488             }
59489             this.collapsedEl.setLocation(-2000,-2000);
59490             this.collapsedEl.hide();
59491             this.fireEvent("invalidated", this);
59492             this.fireEvent("expanded", this);
59493         }
59494     },
59495
59496     animateExpand : function(){
59497         // overridden
59498     },
59499
59500     initTabs : function()
59501     {
59502         this.bodyEl.setStyle("overflow", "hidden");
59503         var ts = new Roo.TabPanel(
59504                 this.bodyEl.dom,
59505                 {
59506                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
59507                     disableTooltips: this.config.disableTabTips,
59508                     toolbar : this.config.toolbar
59509                 }
59510         );
59511         if(this.config.hideTabs){
59512             ts.stripWrap.setDisplayed(false);
59513         }
59514         this.tabs = ts;
59515         ts.resizeTabs = this.config.resizeTabs === true;
59516         ts.minTabWidth = this.config.minTabWidth || 40;
59517         ts.maxTabWidth = this.config.maxTabWidth || 250;
59518         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
59519         ts.monitorResize = false;
59520         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59521         ts.bodyEl.addClass('x-layout-tabs-body');
59522         this.panels.each(this.initPanelAsTab, this);
59523     },
59524
59525     initPanelAsTab : function(panel){
59526         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
59527                     this.config.closeOnTab && panel.isClosable());
59528         if(panel.tabTip !== undefined){
59529             ti.setTooltip(panel.tabTip);
59530         }
59531         ti.on("activate", function(){
59532               this.setActivePanel(panel);
59533         }, this);
59534         if(this.config.closeOnTab){
59535             ti.on("beforeclose", function(t, e){
59536                 e.cancel = true;
59537                 this.remove(panel);
59538             }, this);
59539         }
59540         return ti;
59541     },
59542
59543     updatePanelTitle : function(panel, title){
59544         if(this.activePanel == panel){
59545             this.updateTitle(title);
59546         }
59547         if(this.tabs){
59548             var ti = this.tabs.getTab(panel.getEl().id);
59549             ti.setText(title);
59550             if(panel.tabTip !== undefined){
59551                 ti.setTooltip(panel.tabTip);
59552             }
59553         }
59554     },
59555
59556     updateTitle : function(title){
59557         if(this.titleTextEl && !this.config.title){
59558             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
59559         }
59560     },
59561
59562     setActivePanel : function(panel){
59563         panel = this.getPanel(panel);
59564         if(this.activePanel && this.activePanel != panel){
59565             this.activePanel.setActiveState(false);
59566         }
59567         this.activePanel = panel;
59568         panel.setActiveState(true);
59569         if(this.panelSize){
59570             panel.setSize(this.panelSize.width, this.panelSize.height);
59571         }
59572         if(this.closeBtn){
59573             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
59574         }
59575         this.updateTitle(panel.getTitle());
59576         if(this.tabs){
59577             this.fireEvent("invalidated", this);
59578         }
59579         this.fireEvent("panelactivated", this, panel);
59580     },
59581
59582     /**
59583      * Shows the specified panel.
59584      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
59585      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
59586      */
59587     showPanel : function(panel)
59588     {
59589         panel = this.getPanel(panel);
59590         if(panel){
59591             if(this.tabs){
59592                 var tab = this.tabs.getTab(panel.getEl().id);
59593                 if(tab.isHidden()){
59594                     this.tabs.unhideTab(tab.id);
59595                 }
59596                 tab.activate();
59597             }else{
59598                 this.setActivePanel(panel);
59599             }
59600         }
59601         return panel;
59602     },
59603
59604     /**
59605      * Get the active panel for this region.
59606      * @return {Roo.ContentPanel} The active panel or null
59607      */
59608     getActivePanel : function(){
59609         return this.activePanel;
59610     },
59611
59612     validateVisibility : function(){
59613         if(this.panels.getCount() < 1){
59614             this.updateTitle("&#160;");
59615             this.closeBtn.hide();
59616             this.hide();
59617         }else{
59618             if(!this.isVisible()){
59619                 this.show();
59620             }
59621         }
59622     },
59623
59624     /**
59625      * Adds the passed ContentPanel(s) to this region.
59626      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59627      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
59628      */
59629     add : function(panel){
59630         if(arguments.length > 1){
59631             for(var i = 0, len = arguments.length; i < len; i++) {
59632                 this.add(arguments[i]);
59633             }
59634             return null;
59635         }
59636         if(this.hasPanel(panel)){
59637             this.showPanel(panel);
59638             return panel;
59639         }
59640         panel.setRegion(this);
59641         this.panels.add(panel);
59642         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
59643             this.bodyEl.dom.appendChild(panel.getEl().dom);
59644             if(panel.background !== true){
59645                 this.setActivePanel(panel);
59646             }
59647             this.fireEvent("paneladded", this, panel);
59648             return panel;
59649         }
59650         if(!this.tabs){
59651             this.initTabs();
59652         }else{
59653             this.initPanelAsTab(panel);
59654         }
59655         if(panel.background !== true){
59656             this.tabs.activate(panel.getEl().id);
59657         }
59658         this.fireEvent("paneladded", this, panel);
59659         return panel;
59660     },
59661
59662     /**
59663      * Hides the tab for the specified panel.
59664      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59665      */
59666     hidePanel : function(panel){
59667         if(this.tabs && (panel = this.getPanel(panel))){
59668             this.tabs.hideTab(panel.getEl().id);
59669         }
59670     },
59671
59672     /**
59673      * Unhides the tab for a previously hidden panel.
59674      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59675      */
59676     unhidePanel : function(panel){
59677         if(this.tabs && (panel = this.getPanel(panel))){
59678             this.tabs.unhideTab(panel.getEl().id);
59679         }
59680     },
59681
59682     clearPanels : function(){
59683         while(this.panels.getCount() > 0){
59684              this.remove(this.panels.first());
59685         }
59686     },
59687
59688     /**
59689      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59690      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59691      * @param {Boolean} preservePanel Overrides the config preservePanel option
59692      * @return {Roo.ContentPanel} The panel that was removed
59693      */
59694     remove : function(panel, preservePanel){
59695         panel = this.getPanel(panel);
59696         if(!panel){
59697             return null;
59698         }
59699         var e = {};
59700         this.fireEvent("beforeremove", this, panel, e);
59701         if(e.cancel === true){
59702             return null;
59703         }
59704         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
59705         var panelId = panel.getId();
59706         this.panels.removeKey(panelId);
59707         if(preservePanel){
59708             document.body.appendChild(panel.getEl().dom);
59709         }
59710         if(this.tabs){
59711             this.tabs.removeTab(panel.getEl().id);
59712         }else if (!preservePanel){
59713             this.bodyEl.dom.removeChild(panel.getEl().dom);
59714         }
59715         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
59716             var p = this.panels.first();
59717             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
59718             tempEl.appendChild(p.getEl().dom);
59719             this.bodyEl.update("");
59720             this.bodyEl.dom.appendChild(p.getEl().dom);
59721             tempEl = null;
59722             this.updateTitle(p.getTitle());
59723             this.tabs = null;
59724             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59725             this.setActivePanel(p);
59726         }
59727         panel.setRegion(null);
59728         if(this.activePanel == panel){
59729             this.activePanel = null;
59730         }
59731         if(this.config.autoDestroy !== false && preservePanel !== true){
59732             try{panel.destroy();}catch(e){}
59733         }
59734         this.fireEvent("panelremoved", this, panel);
59735         return panel;
59736     },
59737
59738     /**
59739      * Returns the TabPanel component used by this region
59740      * @return {Roo.TabPanel}
59741      */
59742     getTabs : function(){
59743         return this.tabs;
59744     },
59745
59746     createTool : function(parentEl, className){
59747         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
59748             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
59749         btn.addClassOnOver("x-layout-tools-button-over");
59750         return btn;
59751     }
59752 });/*
59753  * Based on:
59754  * Ext JS Library 1.1.1
59755  * Copyright(c) 2006-2007, Ext JS, LLC.
59756  *
59757  * Originally Released Under LGPL - original licence link has changed is not relivant.
59758  *
59759  * Fork - LGPL
59760  * <script type="text/javascript">
59761  */
59762  
59763
59764
59765 /**
59766  * @class Roo.SplitLayoutRegion
59767  * @extends Roo.LayoutRegion
59768  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
59769  */
59770 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
59771     this.cursor = cursor;
59772     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
59773 };
59774
59775 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
59776     splitTip : "Drag to resize.",
59777     collapsibleSplitTip : "Drag to resize. Double click to hide.",
59778     useSplitTips : false,
59779
59780     applyConfig : function(config){
59781         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
59782         if(config.split){
59783             if(!this.split){
59784                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
59785                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
59786                 /** The SplitBar for this region 
59787                 * @type Roo.SplitBar */
59788                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
59789                 this.split.on("moved", this.onSplitMove, this);
59790                 this.split.useShim = config.useShim === true;
59791                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
59792                 if(this.useSplitTips){
59793                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
59794                 }
59795                 if(config.collapsible){
59796                     this.split.el.on("dblclick", this.collapse,  this);
59797                 }
59798             }
59799             if(typeof config.minSize != "undefined"){
59800                 this.split.minSize = config.minSize;
59801             }
59802             if(typeof config.maxSize != "undefined"){
59803                 this.split.maxSize = config.maxSize;
59804             }
59805             if(config.hideWhenEmpty || config.hidden || config.collapsed){
59806                 this.hideSplitter();
59807             }
59808         }
59809     },
59810
59811     getHMaxSize : function(){
59812          var cmax = this.config.maxSize || 10000;
59813          var center = this.mgr.getRegion("center");
59814          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
59815     },
59816
59817     getVMaxSize : function(){
59818          var cmax = this.config.maxSize || 10000;
59819          var center = this.mgr.getRegion("center");
59820          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
59821     },
59822
59823     onSplitMove : function(split, newSize){
59824         this.fireEvent("resized", this, newSize);
59825     },
59826     
59827     /** 
59828      * Returns the {@link Roo.SplitBar} for this region.
59829      * @return {Roo.SplitBar}
59830      */
59831     getSplitBar : function(){
59832         return this.split;
59833     },
59834     
59835     hide : function(){
59836         this.hideSplitter();
59837         Roo.SplitLayoutRegion.superclass.hide.call(this);
59838     },
59839
59840     hideSplitter : function(){
59841         if(this.split){
59842             this.split.el.setLocation(-2000,-2000);
59843             this.split.el.hide();
59844         }
59845     },
59846
59847     show : function(){
59848         if(this.split){
59849             this.split.el.show();
59850         }
59851         Roo.SplitLayoutRegion.superclass.show.call(this);
59852     },
59853     
59854     beforeSlide: function(){
59855         if(Roo.isGecko){// firefox overflow auto bug workaround
59856             this.bodyEl.clip();
59857             if(this.tabs) {
59858                 this.tabs.bodyEl.clip();
59859             }
59860             if(this.activePanel){
59861                 this.activePanel.getEl().clip();
59862                 
59863                 if(this.activePanel.beforeSlide){
59864                     this.activePanel.beforeSlide();
59865                 }
59866             }
59867         }
59868     },
59869     
59870     afterSlide : function(){
59871         if(Roo.isGecko){// firefox overflow auto bug workaround
59872             this.bodyEl.unclip();
59873             if(this.tabs) {
59874                 this.tabs.bodyEl.unclip();
59875             }
59876             if(this.activePanel){
59877                 this.activePanel.getEl().unclip();
59878                 if(this.activePanel.afterSlide){
59879                     this.activePanel.afterSlide();
59880                 }
59881             }
59882         }
59883     },
59884
59885     initAutoHide : function(){
59886         if(this.autoHide !== false){
59887             if(!this.autoHideHd){
59888                 var st = new Roo.util.DelayedTask(this.slideIn, this);
59889                 this.autoHideHd = {
59890                     "mouseout": function(e){
59891                         if(!e.within(this.el, true)){
59892                             st.delay(500);
59893                         }
59894                     },
59895                     "mouseover" : function(e){
59896                         st.cancel();
59897                     },
59898                     scope : this
59899                 };
59900             }
59901             this.el.on(this.autoHideHd);
59902         }
59903     },
59904
59905     clearAutoHide : function(){
59906         if(this.autoHide !== false){
59907             this.el.un("mouseout", this.autoHideHd.mouseout);
59908             this.el.un("mouseover", this.autoHideHd.mouseover);
59909         }
59910     },
59911
59912     clearMonitor : function(){
59913         Roo.get(document).un("click", this.slideInIf, this);
59914     },
59915
59916     // these names are backwards but not changed for compat
59917     slideOut : function(){
59918         if(this.isSlid || this.el.hasActiveFx()){
59919             return;
59920         }
59921         this.isSlid = true;
59922         if(this.collapseBtn){
59923             this.collapseBtn.hide();
59924         }
59925         this.closeBtnState = this.closeBtn.getStyle('display');
59926         this.closeBtn.hide();
59927         if(this.stickBtn){
59928             this.stickBtn.show();
59929         }
59930         this.el.show();
59931         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
59932         this.beforeSlide();
59933         this.el.setStyle("z-index", 10001);
59934         this.el.slideIn(this.getSlideAnchor(), {
59935             callback: function(){
59936                 this.afterSlide();
59937                 this.initAutoHide();
59938                 Roo.get(document).on("click", this.slideInIf, this);
59939                 this.fireEvent("slideshow", this);
59940             },
59941             scope: this,
59942             block: true
59943         });
59944     },
59945
59946     afterSlideIn : function(){
59947         this.clearAutoHide();
59948         this.isSlid = false;
59949         this.clearMonitor();
59950         this.el.setStyle("z-index", "");
59951         if(this.collapseBtn){
59952             this.collapseBtn.show();
59953         }
59954         this.closeBtn.setStyle('display', this.closeBtnState);
59955         if(this.stickBtn){
59956             this.stickBtn.hide();
59957         }
59958         this.fireEvent("slidehide", this);
59959     },
59960
59961     slideIn : function(cb){
59962         if(!this.isSlid || this.el.hasActiveFx()){
59963             Roo.callback(cb);
59964             return;
59965         }
59966         this.isSlid = false;
59967         this.beforeSlide();
59968         this.el.slideOut(this.getSlideAnchor(), {
59969             callback: function(){
59970                 this.el.setLeftTop(-10000, -10000);
59971                 this.afterSlide();
59972                 this.afterSlideIn();
59973                 Roo.callback(cb);
59974             },
59975             scope: this,
59976             block: true
59977         });
59978     },
59979     
59980     slideInIf : function(e){
59981         if(!e.within(this.el)){
59982             this.slideIn();
59983         }
59984     },
59985
59986     animateCollapse : function(){
59987         this.beforeSlide();
59988         this.el.setStyle("z-index", 20000);
59989         var anchor = this.getSlideAnchor();
59990         this.el.slideOut(anchor, {
59991             callback : function(){
59992                 this.el.setStyle("z-index", "");
59993                 this.collapsedEl.slideIn(anchor, {duration:.3});
59994                 this.afterSlide();
59995                 this.el.setLocation(-10000,-10000);
59996                 this.el.hide();
59997                 this.fireEvent("collapsed", this);
59998             },
59999             scope: this,
60000             block: true
60001         });
60002     },
60003
60004     animateExpand : function(){
60005         this.beforeSlide();
60006         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
60007         this.el.setStyle("z-index", 20000);
60008         this.collapsedEl.hide({
60009             duration:.1
60010         });
60011         this.el.slideIn(this.getSlideAnchor(), {
60012             callback : function(){
60013                 this.el.setStyle("z-index", "");
60014                 this.afterSlide();
60015                 if(this.split){
60016                     this.split.el.show();
60017                 }
60018                 this.fireEvent("invalidated", this);
60019                 this.fireEvent("expanded", this);
60020             },
60021             scope: this,
60022             block: true
60023         });
60024     },
60025
60026     anchors : {
60027         "west" : "left",
60028         "east" : "right",
60029         "north" : "top",
60030         "south" : "bottom"
60031     },
60032
60033     sanchors : {
60034         "west" : "l",
60035         "east" : "r",
60036         "north" : "t",
60037         "south" : "b"
60038     },
60039
60040     canchors : {
60041         "west" : "tl-tr",
60042         "east" : "tr-tl",
60043         "north" : "tl-bl",
60044         "south" : "bl-tl"
60045     },
60046
60047     getAnchor : function(){
60048         return this.anchors[this.position];
60049     },
60050
60051     getCollapseAnchor : function(){
60052         return this.canchors[this.position];
60053     },
60054
60055     getSlideAnchor : function(){
60056         return this.sanchors[this.position];
60057     },
60058
60059     getAlignAdj : function(){
60060         var cm = this.cmargins;
60061         switch(this.position){
60062             case "west":
60063                 return [0, 0];
60064             break;
60065             case "east":
60066                 return [0, 0];
60067             break;
60068             case "north":
60069                 return [0, 0];
60070             break;
60071             case "south":
60072                 return [0, 0];
60073             break;
60074         }
60075     },
60076
60077     getExpandAdj : function(){
60078         var c = this.collapsedEl, cm = this.cmargins;
60079         switch(this.position){
60080             case "west":
60081                 return [-(cm.right+c.getWidth()+cm.left), 0];
60082             break;
60083             case "east":
60084                 return [cm.right+c.getWidth()+cm.left, 0];
60085             break;
60086             case "north":
60087                 return [0, -(cm.top+cm.bottom+c.getHeight())];
60088             break;
60089             case "south":
60090                 return [0, cm.top+cm.bottom+c.getHeight()];
60091             break;
60092         }
60093     }
60094 });/*
60095  * Based on:
60096  * Ext JS Library 1.1.1
60097  * Copyright(c) 2006-2007, Ext JS, LLC.
60098  *
60099  * Originally Released Under LGPL - original licence link has changed is not relivant.
60100  *
60101  * Fork - LGPL
60102  * <script type="text/javascript">
60103  */
60104 /*
60105  * These classes are private internal classes
60106  */
60107 Roo.CenterLayoutRegion = function(mgr, config){
60108     Roo.LayoutRegion.call(this, mgr, config, "center");
60109     this.visible = true;
60110     this.minWidth = config.minWidth || 20;
60111     this.minHeight = config.minHeight || 20;
60112 };
60113
60114 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
60115     hide : function(){
60116         // center panel can't be hidden
60117     },
60118     
60119     show : function(){
60120         // center panel can't be hidden
60121     },
60122     
60123     getMinWidth: function(){
60124         return this.minWidth;
60125     },
60126     
60127     getMinHeight: function(){
60128         return this.minHeight;
60129     }
60130 });
60131
60132
60133 Roo.NorthLayoutRegion = function(mgr, config){
60134     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
60135     if(this.split){
60136         this.split.placement = Roo.SplitBar.TOP;
60137         this.split.orientation = Roo.SplitBar.VERTICAL;
60138         this.split.el.addClass("x-layout-split-v");
60139     }
60140     var size = config.initialSize || config.height;
60141     if(typeof size != "undefined"){
60142         this.el.setHeight(size);
60143     }
60144 };
60145 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
60146     orientation: Roo.SplitBar.VERTICAL,
60147     getBox : function(){
60148         if(this.collapsed){
60149             return this.collapsedEl.getBox();
60150         }
60151         var box = this.el.getBox();
60152         if(this.split){
60153             box.height += this.split.el.getHeight();
60154         }
60155         return box;
60156     },
60157     
60158     updateBox : function(box){
60159         if(this.split && !this.collapsed){
60160             box.height -= this.split.el.getHeight();
60161             this.split.el.setLeft(box.x);
60162             this.split.el.setTop(box.y+box.height);
60163             this.split.el.setWidth(box.width);
60164         }
60165         if(this.collapsed){
60166             this.updateBody(box.width, null);
60167         }
60168         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60169     }
60170 });
60171
60172 Roo.SouthLayoutRegion = function(mgr, config){
60173     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
60174     if(this.split){
60175         this.split.placement = Roo.SplitBar.BOTTOM;
60176         this.split.orientation = Roo.SplitBar.VERTICAL;
60177         this.split.el.addClass("x-layout-split-v");
60178     }
60179     var size = config.initialSize || config.height;
60180     if(typeof size != "undefined"){
60181         this.el.setHeight(size);
60182     }
60183 };
60184 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
60185     orientation: Roo.SplitBar.VERTICAL,
60186     getBox : function(){
60187         if(this.collapsed){
60188             return this.collapsedEl.getBox();
60189         }
60190         var box = this.el.getBox();
60191         if(this.split){
60192             var sh = this.split.el.getHeight();
60193             box.height += sh;
60194             box.y -= sh;
60195         }
60196         return box;
60197     },
60198     
60199     updateBox : function(box){
60200         if(this.split && !this.collapsed){
60201             var sh = this.split.el.getHeight();
60202             box.height -= sh;
60203             box.y += sh;
60204             this.split.el.setLeft(box.x);
60205             this.split.el.setTop(box.y-sh);
60206             this.split.el.setWidth(box.width);
60207         }
60208         if(this.collapsed){
60209             this.updateBody(box.width, null);
60210         }
60211         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60212     }
60213 });
60214
60215 Roo.EastLayoutRegion = function(mgr, config){
60216     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
60217     if(this.split){
60218         this.split.placement = Roo.SplitBar.RIGHT;
60219         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60220         this.split.el.addClass("x-layout-split-h");
60221     }
60222     var size = config.initialSize || config.width;
60223     if(typeof size != "undefined"){
60224         this.el.setWidth(size);
60225     }
60226 };
60227 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
60228     orientation: Roo.SplitBar.HORIZONTAL,
60229     getBox : function(){
60230         if(this.collapsed){
60231             return this.collapsedEl.getBox();
60232         }
60233         var box = this.el.getBox();
60234         if(this.split){
60235             var sw = this.split.el.getWidth();
60236             box.width += sw;
60237             box.x -= sw;
60238         }
60239         return box;
60240     },
60241
60242     updateBox : function(box){
60243         if(this.split && !this.collapsed){
60244             var sw = this.split.el.getWidth();
60245             box.width -= sw;
60246             this.split.el.setLeft(box.x);
60247             this.split.el.setTop(box.y);
60248             this.split.el.setHeight(box.height);
60249             box.x += sw;
60250         }
60251         if(this.collapsed){
60252             this.updateBody(null, box.height);
60253         }
60254         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60255     }
60256 });
60257
60258 Roo.WestLayoutRegion = function(mgr, config){
60259     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
60260     if(this.split){
60261         this.split.placement = Roo.SplitBar.LEFT;
60262         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60263         this.split.el.addClass("x-layout-split-h");
60264     }
60265     var size = config.initialSize || config.width;
60266     if(typeof size != "undefined"){
60267         this.el.setWidth(size);
60268     }
60269 };
60270 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
60271     orientation: Roo.SplitBar.HORIZONTAL,
60272     getBox : function(){
60273         if(this.collapsed){
60274             return this.collapsedEl.getBox();
60275         }
60276         var box = this.el.getBox();
60277         if(this.split){
60278             box.width += this.split.el.getWidth();
60279         }
60280         return box;
60281     },
60282     
60283     updateBox : function(box){
60284         if(this.split && !this.collapsed){
60285             var sw = this.split.el.getWidth();
60286             box.width -= sw;
60287             this.split.el.setLeft(box.x+box.width);
60288             this.split.el.setTop(box.y);
60289             this.split.el.setHeight(box.height);
60290         }
60291         if(this.collapsed){
60292             this.updateBody(null, box.height);
60293         }
60294         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60295     }
60296 });
60297 /*
60298  * Based on:
60299  * Ext JS Library 1.1.1
60300  * Copyright(c) 2006-2007, Ext JS, LLC.
60301  *
60302  * Originally Released Under LGPL - original licence link has changed is not relivant.
60303  *
60304  * Fork - LGPL
60305  * <script type="text/javascript">
60306  */
60307  
60308  
60309 /*
60310  * Private internal class for reading and applying state
60311  */
60312 Roo.LayoutStateManager = function(layout){
60313      // default empty state
60314      this.state = {
60315         north: {},
60316         south: {},
60317         east: {},
60318         west: {}       
60319     };
60320 };
60321
60322 Roo.LayoutStateManager.prototype = {
60323     init : function(layout, provider){
60324         this.provider = provider;
60325         var state = provider.get(layout.id+"-layout-state");
60326         if(state){
60327             var wasUpdating = layout.isUpdating();
60328             if(!wasUpdating){
60329                 layout.beginUpdate();
60330             }
60331             for(var key in state){
60332                 if(typeof state[key] != "function"){
60333                     var rstate = state[key];
60334                     var r = layout.getRegion(key);
60335                     if(r && rstate){
60336                         if(rstate.size){
60337                             r.resizeTo(rstate.size);
60338                         }
60339                         if(rstate.collapsed == true){
60340                             r.collapse(true);
60341                         }else{
60342                             r.expand(null, true);
60343                         }
60344                     }
60345                 }
60346             }
60347             if(!wasUpdating){
60348                 layout.endUpdate();
60349             }
60350             this.state = state; 
60351         }
60352         this.layout = layout;
60353         layout.on("regionresized", this.onRegionResized, this);
60354         layout.on("regioncollapsed", this.onRegionCollapsed, this);
60355         layout.on("regionexpanded", this.onRegionExpanded, this);
60356     },
60357     
60358     storeState : function(){
60359         this.provider.set(this.layout.id+"-layout-state", this.state);
60360     },
60361     
60362     onRegionResized : function(region, newSize){
60363         this.state[region.getPosition()].size = newSize;
60364         this.storeState();
60365     },
60366     
60367     onRegionCollapsed : function(region){
60368         this.state[region.getPosition()].collapsed = true;
60369         this.storeState();
60370     },
60371     
60372     onRegionExpanded : function(region){
60373         this.state[region.getPosition()].collapsed = false;
60374         this.storeState();
60375     }
60376 };/*
60377  * Based on:
60378  * Ext JS Library 1.1.1
60379  * Copyright(c) 2006-2007, Ext JS, LLC.
60380  *
60381  * Originally Released Under LGPL - original licence link has changed is not relivant.
60382  *
60383  * Fork - LGPL
60384  * <script type="text/javascript">
60385  */
60386 /**
60387  * @class Roo.ContentPanel
60388  * @extends Roo.util.Observable
60389  * @children Roo.form.Form Roo.JsonView Roo.View
60390  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60391  * A basic ContentPanel element.
60392  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
60393  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
60394  * @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
60395  * @cfg {Boolean}   closable      True if the panel can be closed/removed
60396  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
60397  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
60398  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
60399  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
60400  * @cfg {String} title          The title for this panel
60401  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
60402  * @cfg {String} url            Calls {@link #setUrl} with this value
60403  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
60404  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
60405  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
60406  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
60407  * @cfg {String}    style  Extra style to add to the content panel
60408  * @cfg {Roo.menu.Menu} menu  popup menu
60409
60410  * @constructor
60411  * Create a new ContentPanel.
60412  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
60413  * @param {String/Object} config A string to set only the title or a config object
60414  * @param {String} content (optional) Set the HTML content for this panel
60415  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
60416  */
60417 Roo.ContentPanel = function(el, config, content){
60418     
60419      
60420     /*
60421     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
60422         config = el;
60423         el = Roo.id();
60424     }
60425     if (config && config.parentLayout) { 
60426         el = config.parentLayout.el.createChild(); 
60427     }
60428     */
60429     if(el.autoCreate){ // xtype is available if this is called from factory
60430         config = el;
60431         el = Roo.id();
60432     }
60433     this.el = Roo.get(el);
60434     if(!this.el && config && config.autoCreate){
60435         if(typeof config.autoCreate == "object"){
60436             if(!config.autoCreate.id){
60437                 config.autoCreate.id = config.id||el;
60438             }
60439             this.el = Roo.DomHelper.append(document.body,
60440                         config.autoCreate, true);
60441         }else{
60442             this.el = Roo.DomHelper.append(document.body,
60443                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
60444         }
60445     }
60446     
60447     
60448     this.closable = false;
60449     this.loaded = false;
60450     this.active = false;
60451     if(typeof config == "string"){
60452         this.title = config;
60453     }else{
60454         Roo.apply(this, config);
60455     }
60456     
60457     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
60458         this.wrapEl = this.el.wrap();
60459         this.toolbar.container = this.el.insertSibling(false, 'before');
60460         this.toolbar = new Roo.Toolbar(this.toolbar);
60461     }
60462     
60463     // xtype created footer. - not sure if will work as we normally have to render first..
60464     if (this.footer && !this.footer.el && this.footer.xtype) {
60465         if (!this.wrapEl) {
60466             this.wrapEl = this.el.wrap();
60467         }
60468     
60469         this.footer.container = this.wrapEl.createChild();
60470          
60471         this.footer = Roo.factory(this.footer, Roo);
60472         
60473     }
60474     
60475     if(this.resizeEl){
60476         this.resizeEl = Roo.get(this.resizeEl, true);
60477     }else{
60478         this.resizeEl = this.el;
60479     }
60480     // handle view.xtype
60481     
60482  
60483     
60484     
60485     this.addEvents({
60486         /**
60487          * @event activate
60488          * Fires when this panel is activated. 
60489          * @param {Roo.ContentPanel} this
60490          */
60491         "activate" : true,
60492         /**
60493          * @event deactivate
60494          * Fires when this panel is activated. 
60495          * @param {Roo.ContentPanel} this
60496          */
60497         "deactivate" : true,
60498
60499         /**
60500          * @event resize
60501          * Fires when this panel is resized if fitToFrame is true.
60502          * @param {Roo.ContentPanel} this
60503          * @param {Number} width The width after any component adjustments
60504          * @param {Number} height The height after any component adjustments
60505          */
60506         "resize" : true,
60507         
60508          /**
60509          * @event render
60510          * Fires when this tab is created
60511          * @param {Roo.ContentPanel} this
60512          */
60513         "render" : true
60514          
60515         
60516     });
60517     
60518
60519     
60520     
60521     if(this.autoScroll){
60522         this.resizeEl.setStyle("overflow", "auto");
60523     } else {
60524         // fix randome scrolling
60525         this.el.on('scroll', function() {
60526             Roo.log('fix random scolling');
60527             this.scrollTo('top',0); 
60528         });
60529     }
60530     content = content || this.content;
60531     if(content){
60532         this.setContent(content);
60533     }
60534     if(config && config.url){
60535         this.setUrl(this.url, this.params, this.loadOnce);
60536     }
60537     
60538     
60539     
60540     Roo.ContentPanel.superclass.constructor.call(this);
60541     
60542     if (this.view && typeof(this.view.xtype) != 'undefined') {
60543         this.view.el = this.el.appendChild(document.createElement("div"));
60544         this.view = Roo.factory(this.view); 
60545         this.view.render  &&  this.view.render(false, '');  
60546     }
60547     
60548     
60549     this.fireEvent('render', this);
60550 };
60551
60552 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
60553     tabTip:'',
60554     setRegion : function(region){
60555         this.region = region;
60556         if(region){
60557            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
60558         }else{
60559            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
60560         } 
60561     },
60562     
60563     /**
60564      * Returns the toolbar for this Panel if one was configured. 
60565      * @return {Roo.Toolbar} 
60566      */
60567     getToolbar : function(){
60568         return this.toolbar;
60569     },
60570     
60571     setActiveState : function(active){
60572         this.active = active;
60573         if(!active){
60574             this.fireEvent("deactivate", this);
60575         }else{
60576             this.fireEvent("activate", this);
60577         }
60578     },
60579     /**
60580      * Updates this panel's element
60581      * @param {String} content The new content
60582      * @param {Boolean} loadScripts (optional) true to look for and process scripts
60583     */
60584     setContent : function(content, loadScripts){
60585         this.el.update(content, loadScripts);
60586     },
60587
60588     ignoreResize : function(w, h){
60589         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
60590             return true;
60591         }else{
60592             this.lastSize = {width: w, height: h};
60593             return false;
60594         }
60595     },
60596     /**
60597      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
60598      * @return {Roo.UpdateManager} The UpdateManager
60599      */
60600     getUpdateManager : function(){
60601         return this.el.getUpdateManager();
60602     },
60603      /**
60604      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
60605      * @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:
60606 <pre><code>
60607 panel.load({
60608     url: "your-url.php",
60609     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
60610     callback: yourFunction,
60611     scope: yourObject, //(optional scope)
60612     discardUrl: false,
60613     nocache: false,
60614     text: "Loading...",
60615     timeout: 30,
60616     scripts: false
60617 });
60618 </code></pre>
60619      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
60620      * 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.
60621      * @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}
60622      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
60623      * @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.
60624      * @return {Roo.ContentPanel} this
60625      */
60626     load : function(){
60627         var um = this.el.getUpdateManager();
60628         um.update.apply(um, arguments);
60629         return this;
60630     },
60631
60632
60633     /**
60634      * 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.
60635      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
60636      * @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)
60637      * @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)
60638      * @return {Roo.UpdateManager} The UpdateManager
60639      */
60640     setUrl : function(url, params, loadOnce){
60641         if(this.refreshDelegate){
60642             this.removeListener("activate", this.refreshDelegate);
60643         }
60644         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
60645         this.on("activate", this.refreshDelegate);
60646         return this.el.getUpdateManager();
60647     },
60648     
60649     _handleRefresh : function(url, params, loadOnce){
60650         if(!loadOnce || !this.loaded){
60651             var updater = this.el.getUpdateManager();
60652             updater.update(url, params, this._setLoaded.createDelegate(this));
60653         }
60654     },
60655     
60656     _setLoaded : function(){
60657         this.loaded = true;
60658     }, 
60659     
60660     /**
60661      * Returns this panel's id
60662      * @return {String} 
60663      */
60664     getId : function(){
60665         return this.el.id;
60666     },
60667     
60668     /** 
60669      * Returns this panel's element - used by regiosn to add.
60670      * @return {Roo.Element} 
60671      */
60672     getEl : function(){
60673         return this.wrapEl || this.el;
60674     },
60675     
60676     adjustForComponents : function(width, height)
60677     {
60678         //Roo.log('adjustForComponents ');
60679         if(this.resizeEl != this.el){
60680             width -= this.el.getFrameWidth('lr');
60681             height -= this.el.getFrameWidth('tb');
60682         }
60683         if(this.toolbar){
60684             var te = this.toolbar.getEl();
60685             height -= te.getHeight();
60686             te.setWidth(width);
60687         }
60688         if(this.footer){
60689             var te = this.footer.getEl();
60690             //Roo.log("footer:" + te.getHeight());
60691             
60692             height -= te.getHeight();
60693             te.setWidth(width);
60694         }
60695         
60696         
60697         if(this.adjustments){
60698             width += this.adjustments[0];
60699             height += this.adjustments[1];
60700         }
60701         return {"width": width, "height": height};
60702     },
60703     
60704     setSize : function(width, height){
60705         if(this.fitToFrame && !this.ignoreResize(width, height)){
60706             if(this.fitContainer && this.resizeEl != this.el){
60707                 this.el.setSize(width, height);
60708             }
60709             var size = this.adjustForComponents(width, height);
60710             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
60711             this.fireEvent('resize', this, size.width, size.height);
60712         }
60713     },
60714     
60715     /**
60716      * Returns this panel's title
60717      * @return {String} 
60718      */
60719     getTitle : function(){
60720         return this.title;
60721     },
60722     
60723     /**
60724      * Set this panel's title
60725      * @param {String} title
60726      */
60727     setTitle : function(title){
60728         this.title = title;
60729         if(this.region){
60730             this.region.updatePanelTitle(this, title);
60731         }
60732     },
60733     
60734     /**
60735      * Returns true is this panel was configured to be closable
60736      * @return {Boolean} 
60737      */
60738     isClosable : function(){
60739         return this.closable;
60740     },
60741     
60742     beforeSlide : function(){
60743         this.el.clip();
60744         this.resizeEl.clip();
60745     },
60746     
60747     afterSlide : function(){
60748         this.el.unclip();
60749         this.resizeEl.unclip();
60750     },
60751     
60752     /**
60753      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
60754      *   Will fail silently if the {@link #setUrl} method has not been called.
60755      *   This does not activate the panel, just updates its content.
60756      */
60757     refresh : function(){
60758         if(this.refreshDelegate){
60759            this.loaded = false;
60760            this.refreshDelegate();
60761         }
60762     },
60763     
60764     /**
60765      * Destroys this panel
60766      */
60767     destroy : function(){
60768         this.el.removeAllListeners();
60769         var tempEl = document.createElement("span");
60770         tempEl.appendChild(this.el.dom);
60771         tempEl.innerHTML = "";
60772         this.el.remove();
60773         this.el = null;
60774     },
60775     
60776     /**
60777      * form - if the content panel contains a form - this is a reference to it.
60778      * @type {Roo.form.Form}
60779      */
60780     form : false,
60781     /**
60782      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
60783      *    This contains a reference to it.
60784      * @type {Roo.View}
60785      */
60786     view : false,
60787     
60788       /**
60789      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
60790      * <pre><code>
60791
60792 layout.addxtype({
60793        xtype : 'Form',
60794        items: [ .... ]
60795    }
60796 );
60797
60798 </code></pre>
60799      * @param {Object} cfg Xtype definition of item to add.
60800      */
60801     
60802     addxtype : function(cfg) {
60803         // add form..
60804         if (cfg.xtype.match(/^Form$/)) {
60805             
60806             var el;
60807             //if (this.footer) {
60808             //    el = this.footer.container.insertSibling(false, 'before');
60809             //} else {
60810                 el = this.el.createChild();
60811             //}
60812
60813             this.form = new  Roo.form.Form(cfg);
60814             
60815             
60816             if ( this.form.allItems.length) {
60817                 this.form.render(el.dom);
60818             }
60819             return this.form;
60820         }
60821         // should only have one of theses..
60822         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
60823             // views.. should not be just added - used named prop 'view''
60824             
60825             cfg.el = this.el.appendChild(document.createElement("div"));
60826             // factory?
60827             
60828             var ret = new Roo.factory(cfg);
60829              
60830              ret.render && ret.render(false, ''); // render blank..
60831             this.view = ret;
60832             return ret;
60833         }
60834         return false;
60835     }
60836 });
60837
60838
60839
60840
60841
60842
60843
60844
60845
60846
60847
60848
60849 /**
60850  * @class Roo.GridPanel
60851  * @extends Roo.ContentPanel
60852  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60853  * @constructor
60854  * Create a new GridPanel.
60855  * @cfg {Roo.grid.Grid} grid The grid for this panel
60856  */
60857 Roo.GridPanel = function(grid, config){
60858     
60859     // universal ctor...
60860     if (typeof(grid.grid) != 'undefined') {
60861         config = grid;
60862         grid = config.grid;
60863     }
60864     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
60865         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
60866         
60867     this.wrapper.dom.appendChild(grid.getGridEl().dom);
60868     
60869     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
60870     
60871     if(this.toolbar){
60872         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
60873     }
60874     // xtype created footer. - not sure if will work as we normally have to render first..
60875     if (this.footer && !this.footer.el && this.footer.xtype) {
60876         
60877         this.footer.container = this.grid.getView().getFooterPanel(true);
60878         this.footer.dataSource = this.grid.dataSource;
60879         this.footer = Roo.factory(this.footer, Roo);
60880         
60881     }
60882     
60883     grid.monitorWindowResize = false; // turn off autosizing
60884     grid.autoHeight = false;
60885     grid.autoWidth = false;
60886     this.grid = grid;
60887     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
60888 };
60889
60890 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
60891     getId : function(){
60892         return this.grid.id;
60893     },
60894     
60895     /**
60896      * Returns the grid for this panel
60897      * @return {Roo.grid.Grid} 
60898      */
60899     getGrid : function(){
60900         return this.grid;    
60901     },
60902     
60903     setSize : function(width, height){
60904         if(!this.ignoreResize(width, height)){
60905             var grid = this.grid;
60906             var size = this.adjustForComponents(width, height);
60907             grid.getGridEl().setSize(size.width, size.height);
60908             grid.autoSize();
60909         }
60910     },
60911     
60912     beforeSlide : function(){
60913         this.grid.getView().scroller.clip();
60914     },
60915     
60916     afterSlide : function(){
60917         this.grid.getView().scroller.unclip();
60918     },
60919     
60920     destroy : function(){
60921         this.grid.destroy();
60922         delete this.grid;
60923         Roo.GridPanel.superclass.destroy.call(this); 
60924     }
60925 });
60926
60927
60928 /**
60929  * @class Roo.NestedLayoutPanel
60930  * @extends Roo.ContentPanel
60931  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60932  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
60933  *
60934  * 
60935  * @constructor
60936  * Create a new NestedLayoutPanel.
60937  * 
60938  * 
60939  * @param {Roo.BorderLayout} layout [required] The layout for this panel
60940  * @param {String/Object} config A string to set only the title or a config object
60941  */
60942 Roo.NestedLayoutPanel = function(layout, config)
60943 {
60944     // construct with only one argument..
60945     /* FIXME - implement nicer consturctors
60946     if (layout.layout) {
60947         config = layout;
60948         layout = config.layout;
60949         delete config.layout;
60950     }
60951     if (layout.xtype && !layout.getEl) {
60952         // then layout needs constructing..
60953         layout = Roo.factory(layout, Roo);
60954     }
60955     */
60956     
60957     
60958     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
60959     
60960     layout.monitorWindowResize = false; // turn off autosizing
60961     this.layout = layout;
60962     this.layout.getEl().addClass("x-layout-nested-layout");
60963     
60964     
60965     
60966     
60967 };
60968
60969 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
60970
60971     layout : false,
60972
60973     setSize : function(width, height){
60974         if(!this.ignoreResize(width, height)){
60975             var size = this.adjustForComponents(width, height);
60976             var el = this.layout.getEl();
60977             el.setSize(size.width, size.height);
60978             var touch = el.dom.offsetWidth;
60979             this.layout.layout();
60980             // ie requires a double layout on the first pass
60981             if(Roo.isIE && !this.initialized){
60982                 this.initialized = true;
60983                 this.layout.layout();
60984             }
60985         }
60986     },
60987     
60988     // activate all subpanels if not currently active..
60989     
60990     setActiveState : function(active){
60991         this.active = active;
60992         if(!active){
60993             this.fireEvent("deactivate", this);
60994             return;
60995         }
60996         
60997         this.fireEvent("activate", this);
60998         // not sure if this should happen before or after..
60999         if (!this.layout) {
61000             return; // should not happen..
61001         }
61002         var reg = false;
61003         for (var r in this.layout.regions) {
61004             reg = this.layout.getRegion(r);
61005             if (reg.getActivePanel()) {
61006                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
61007                 reg.setActivePanel(reg.getActivePanel());
61008                 continue;
61009             }
61010             if (!reg.panels.length) {
61011                 continue;
61012             }
61013             reg.showPanel(reg.getPanel(0));
61014         }
61015         
61016         
61017         
61018         
61019     },
61020     
61021     /**
61022      * Returns the nested BorderLayout for this panel
61023      * @return {Roo.BorderLayout}
61024      */
61025     getLayout : function(){
61026         return this.layout;
61027     },
61028     
61029      /**
61030      * Adds a xtype elements to the layout of the nested panel
61031      * <pre><code>
61032
61033 panel.addxtype({
61034        xtype : 'ContentPanel',
61035        region: 'west',
61036        items: [ .... ]
61037    }
61038 );
61039
61040 panel.addxtype({
61041         xtype : 'NestedLayoutPanel',
61042         region: 'west',
61043         layout: {
61044            center: { },
61045            west: { }   
61046         },
61047         items : [ ... list of content panels or nested layout panels.. ]
61048    }
61049 );
61050 </code></pre>
61051      * @param {Object} cfg Xtype definition of item to add.
61052      */
61053     addxtype : function(cfg) {
61054         return this.layout.addxtype(cfg);
61055     
61056     }
61057 });
61058
61059 Roo.ScrollPanel = function(el, config, content){
61060     config = config || {};
61061     config.fitToFrame = true;
61062     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
61063     
61064     this.el.dom.style.overflow = "hidden";
61065     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
61066     this.el.removeClass("x-layout-inactive-content");
61067     this.el.on("mousewheel", this.onWheel, this);
61068
61069     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
61070     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
61071     up.unselectable(); down.unselectable();
61072     up.on("click", this.scrollUp, this);
61073     down.on("click", this.scrollDown, this);
61074     up.addClassOnOver("x-scroller-btn-over");
61075     down.addClassOnOver("x-scroller-btn-over");
61076     up.addClassOnClick("x-scroller-btn-click");
61077     down.addClassOnClick("x-scroller-btn-click");
61078     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
61079
61080     this.resizeEl = this.el;
61081     this.el = wrap; this.up = up; this.down = down;
61082 };
61083
61084 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
61085     increment : 100,
61086     wheelIncrement : 5,
61087     scrollUp : function(){
61088         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
61089     },
61090
61091     scrollDown : function(){
61092         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
61093     },
61094
61095     afterScroll : function(){
61096         var el = this.resizeEl;
61097         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
61098         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61099         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61100     },
61101
61102     setSize : function(){
61103         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
61104         this.afterScroll();
61105     },
61106
61107     onWheel : function(e){
61108         var d = e.getWheelDelta();
61109         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
61110         this.afterScroll();
61111         e.stopEvent();
61112     },
61113
61114     setContent : function(content, loadScripts){
61115         this.resizeEl.update(content, loadScripts);
61116     }
61117
61118 });
61119
61120
61121
61122 /**
61123  * @class Roo.TreePanel
61124  * @extends Roo.ContentPanel
61125  * @parent Roo.BorderLayout Roo.LayoutDialog builder
61126  * Treepanel component
61127  * 
61128  * @constructor
61129  * Create a new TreePanel. - defaults to fit/scoll contents.
61130  * @param {String/Object} config A string to set only the panel's title, or a config object
61131  */
61132 Roo.TreePanel = function(config){
61133     var el = config.el;
61134     var tree = config.tree;
61135     delete config.tree; 
61136     delete config.el; // hopefull!
61137     
61138     // wrapper for IE7 strict & safari scroll issue
61139     
61140     var treeEl = el.createChild();
61141     config.resizeEl = treeEl;
61142     
61143     
61144     
61145     Roo.TreePanel.superclass.constructor.call(this, el, config);
61146  
61147  
61148     this.tree = new Roo.tree.TreePanel(treeEl , tree);
61149     //console.log(tree);
61150     this.on('activate', function()
61151     {
61152         if (this.tree.rendered) {
61153             return;
61154         }
61155         //console.log('render tree');
61156         this.tree.render();
61157     });
61158     // this should not be needed.. - it's actually the 'el' that resizes?
61159     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
61160     
61161     //this.on('resize',  function (cp, w, h) {
61162     //        this.tree.innerCt.setWidth(w);
61163     //        this.tree.innerCt.setHeight(h);
61164     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
61165     //});
61166
61167         
61168     
61169 };
61170
61171 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
61172     fitToFrame : true,
61173     autoScroll : true,
61174     /*
61175      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
61176      */
61177     tree : false
61178
61179 });
61180 /*
61181  * Based on:
61182  * Ext JS Library 1.1.1
61183  * Copyright(c) 2006-2007, Ext JS, LLC.
61184  *
61185  * Originally Released Under LGPL - original licence link has changed is not relivant.
61186  *
61187  * Fork - LGPL
61188  * <script type="text/javascript">
61189  */
61190  
61191
61192 /**
61193  * @class Roo.ReaderLayout
61194  * @extends Roo.BorderLayout
61195  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
61196  * center region containing two nested regions (a top one for a list view and one for item preview below),
61197  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
61198  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
61199  * expedites the setup of the overall layout and regions for this common application style.
61200  * Example:
61201  <pre><code>
61202 var reader = new Roo.ReaderLayout();
61203 var CP = Roo.ContentPanel;  // shortcut for adding
61204
61205 reader.beginUpdate();
61206 reader.add("north", new CP("north", "North"));
61207 reader.add("west", new CP("west", {title: "West"}));
61208 reader.add("east", new CP("east", {title: "East"}));
61209
61210 reader.regions.listView.add(new CP("listView", "List"));
61211 reader.regions.preview.add(new CP("preview", "Preview"));
61212 reader.endUpdate();
61213 </code></pre>
61214 * @constructor
61215 * Create a new ReaderLayout
61216 * @param {Object} config Configuration options
61217 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
61218 * document.body if omitted)
61219 */
61220 Roo.ReaderLayout = function(config, renderTo){
61221     var c = config || {size:{}};
61222     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
61223         north: c.north !== false ? Roo.apply({
61224             split:false,
61225             initialSize: 32,
61226             titlebar: false
61227         }, c.north) : false,
61228         west: c.west !== false ? Roo.apply({
61229             split:true,
61230             initialSize: 200,
61231             minSize: 175,
61232             maxSize: 400,
61233             titlebar: true,
61234             collapsible: true,
61235             animate: true,
61236             margins:{left:5,right:0,bottom:5,top:5},
61237             cmargins:{left:5,right:5,bottom:5,top:5}
61238         }, c.west) : false,
61239         east: c.east !== false ? Roo.apply({
61240             split:true,
61241             initialSize: 200,
61242             minSize: 175,
61243             maxSize: 400,
61244             titlebar: true,
61245             collapsible: true,
61246             animate: true,
61247             margins:{left:0,right:5,bottom:5,top:5},
61248             cmargins:{left:5,right:5,bottom:5,top:5}
61249         }, c.east) : false,
61250         center: Roo.apply({
61251             tabPosition: 'top',
61252             autoScroll:false,
61253             closeOnTab: true,
61254             titlebar:false,
61255             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
61256         }, c.center)
61257     });
61258
61259     this.el.addClass('x-reader');
61260
61261     this.beginUpdate();
61262
61263     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
61264         south: c.preview !== false ? Roo.apply({
61265             split:true,
61266             initialSize: 200,
61267             minSize: 100,
61268             autoScroll:true,
61269             collapsible:true,
61270             titlebar: true,
61271             cmargins:{top:5,left:0, right:0, bottom:0}
61272         }, c.preview) : false,
61273         center: Roo.apply({
61274             autoScroll:false,
61275             titlebar:false,
61276             minHeight:200
61277         }, c.listView)
61278     });
61279     this.add('center', new Roo.NestedLayoutPanel(inner,
61280             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
61281
61282     this.endUpdate();
61283
61284     this.regions.preview = inner.getRegion('south');
61285     this.regions.listView = inner.getRegion('center');
61286 };
61287
61288 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
61289  * Based on:
61290  * Ext JS Library 1.1.1
61291  * Copyright(c) 2006-2007, Ext JS, LLC.
61292  *
61293  * Originally Released Under LGPL - original licence link has changed is not relivant.
61294  *
61295  * Fork - LGPL
61296  * <script type="text/javascript">
61297  */
61298  
61299 /**
61300  * @class Roo.grid.Grid
61301  * @extends Roo.util.Observable
61302  * This class represents the primary interface of a component based grid control.
61303  * <br><br>Usage:<pre><code>
61304  var grid = new Roo.grid.Grid("my-container-id", {
61305      ds: myDataStore,
61306      cm: myColModel,
61307      selModel: mySelectionModel,
61308      autoSizeColumns: true,
61309      monitorWindowResize: false,
61310      trackMouseOver: true
61311  });
61312  // set any options
61313  grid.render();
61314  * </code></pre>
61315  * <b>Common Problems:</b><br/>
61316  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
61317  * element will correct this<br/>
61318  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
61319  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
61320  * are unpredictable.<br/>
61321  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
61322  * grid to calculate dimensions/offsets.<br/>
61323   * @constructor
61324  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61325  * The container MUST have some type of size defined for the grid to fill. The container will be
61326  * automatically set to position relative if it isn't already.
61327  * @param {Object} config A config object that sets properties on this grid.
61328  */
61329 Roo.grid.Grid = function(container, config){
61330         // initialize the container
61331         this.container = Roo.get(container);
61332         this.container.update("");
61333         this.container.setStyle("overflow", "hidden");
61334     this.container.addClass('x-grid-container');
61335
61336     this.id = this.container.id;
61337
61338     Roo.apply(this, config);
61339     // check and correct shorthanded configs
61340     if(this.ds){
61341         this.dataSource = this.ds;
61342         delete this.ds;
61343     }
61344     if(this.cm){
61345         this.colModel = this.cm;
61346         delete this.cm;
61347     }
61348     if(this.sm){
61349         this.selModel = this.sm;
61350         delete this.sm;
61351     }
61352
61353     if (this.selModel) {
61354         this.selModel = Roo.factory(this.selModel, Roo.grid);
61355         this.sm = this.selModel;
61356         this.sm.xmodule = this.xmodule || false;
61357     }
61358     if (typeof(this.colModel.config) == 'undefined') {
61359         this.colModel = new Roo.grid.ColumnModel(this.colModel);
61360         this.cm = this.colModel;
61361         this.cm.xmodule = this.xmodule || false;
61362     }
61363     if (this.dataSource) {
61364         this.dataSource= Roo.factory(this.dataSource, Roo.data);
61365         this.ds = this.dataSource;
61366         this.ds.xmodule = this.xmodule || false;
61367          
61368     }
61369     
61370     
61371     
61372     if(this.width){
61373         this.container.setWidth(this.width);
61374     }
61375
61376     if(this.height){
61377         this.container.setHeight(this.height);
61378     }
61379     /** @private */
61380         this.addEvents({
61381         // raw events
61382         /**
61383          * @event click
61384          * The raw click event for the entire grid.
61385          * @param {Roo.EventObject} e
61386          */
61387         "click" : true,
61388         /**
61389          * @event dblclick
61390          * The raw dblclick event for the entire grid.
61391          * @param {Roo.EventObject} e
61392          */
61393         "dblclick" : true,
61394         /**
61395          * @event contextmenu
61396          * The raw contextmenu event for the entire grid.
61397          * @param {Roo.EventObject} e
61398          */
61399         "contextmenu" : true,
61400         /**
61401          * @event mousedown
61402          * The raw mousedown event for the entire grid.
61403          * @param {Roo.EventObject} e
61404          */
61405         "mousedown" : true,
61406         /**
61407          * @event mouseup
61408          * The raw mouseup event for the entire grid.
61409          * @param {Roo.EventObject} e
61410          */
61411         "mouseup" : true,
61412         /**
61413          * @event mouseover
61414          * The raw mouseover event for the entire grid.
61415          * @param {Roo.EventObject} e
61416          */
61417         "mouseover" : true,
61418         /**
61419          * @event mouseout
61420          * The raw mouseout event for the entire grid.
61421          * @param {Roo.EventObject} e
61422          */
61423         "mouseout" : true,
61424         /**
61425          * @event keypress
61426          * The raw keypress event for the entire grid.
61427          * @param {Roo.EventObject} e
61428          */
61429         "keypress" : true,
61430         /**
61431          * @event keydown
61432          * The raw keydown event for the entire grid.
61433          * @param {Roo.EventObject} e
61434          */
61435         "keydown" : true,
61436
61437         // custom events
61438
61439         /**
61440          * @event cellclick
61441          * Fires when a cell is clicked
61442          * @param {Grid} this
61443          * @param {Number} rowIndex
61444          * @param {Number} columnIndex
61445          * @param {Roo.EventObject} e
61446          */
61447         "cellclick" : true,
61448         /**
61449          * @event celldblclick
61450          * Fires when a cell is double clicked
61451          * @param {Grid} this
61452          * @param {Number} rowIndex
61453          * @param {Number} columnIndex
61454          * @param {Roo.EventObject} e
61455          */
61456         "celldblclick" : true,
61457         /**
61458          * @event rowclick
61459          * Fires when a row is clicked
61460          * @param {Grid} this
61461          * @param {Number} rowIndex
61462          * @param {Roo.EventObject} e
61463          */
61464         "rowclick" : true,
61465         /**
61466          * @event rowdblclick
61467          * Fires when a row is double clicked
61468          * @param {Grid} this
61469          * @param {Number} rowIndex
61470          * @param {Roo.EventObject} e
61471          */
61472         "rowdblclick" : true,
61473         /**
61474          * @event headerclick
61475          * Fires when a header is clicked
61476          * @param {Grid} this
61477          * @param {Number} columnIndex
61478          * @param {Roo.EventObject} e
61479          */
61480         "headerclick" : true,
61481         /**
61482          * @event headerdblclick
61483          * Fires when a header cell is double clicked
61484          * @param {Grid} this
61485          * @param {Number} columnIndex
61486          * @param {Roo.EventObject} e
61487          */
61488         "headerdblclick" : true,
61489         /**
61490          * @event rowcontextmenu
61491          * Fires when a row is right clicked
61492          * @param {Grid} this
61493          * @param {Number} rowIndex
61494          * @param {Roo.EventObject} e
61495          */
61496         "rowcontextmenu" : true,
61497         /**
61498          * @event cellcontextmenu
61499          * Fires when a cell is right clicked
61500          * @param {Grid} this
61501          * @param {Number} rowIndex
61502          * @param {Number} cellIndex
61503          * @param {Roo.EventObject} e
61504          */
61505          "cellcontextmenu" : true,
61506         /**
61507          * @event headercontextmenu
61508          * Fires when a header is right clicked
61509          * @param {Grid} this
61510          * @param {Number} columnIndex
61511          * @param {Roo.EventObject} e
61512          */
61513         "headercontextmenu" : true,
61514         /**
61515          * @event bodyscroll
61516          * Fires when the body element is scrolled
61517          * @param {Number} scrollLeft
61518          * @param {Number} scrollTop
61519          */
61520         "bodyscroll" : true,
61521         /**
61522          * @event columnresize
61523          * Fires when the user resizes a column
61524          * @param {Number} columnIndex
61525          * @param {Number} newSize
61526          */
61527         "columnresize" : true,
61528         /**
61529          * @event columnmove
61530          * Fires when the user moves a column
61531          * @param {Number} oldIndex
61532          * @param {Number} newIndex
61533          */
61534         "columnmove" : true,
61535         /**
61536          * @event startdrag
61537          * Fires when row(s) start being dragged
61538          * @param {Grid} this
61539          * @param {Roo.GridDD} dd The drag drop object
61540          * @param {event} e The raw browser event
61541          */
61542         "startdrag" : true,
61543         /**
61544          * @event enddrag
61545          * Fires when a drag operation is complete
61546          * @param {Grid} this
61547          * @param {Roo.GridDD} dd The drag drop object
61548          * @param {event} e The raw browser event
61549          */
61550         "enddrag" : true,
61551         /**
61552          * @event dragdrop
61553          * Fires when dragged row(s) are dropped on a valid DD target
61554          * @param {Grid} this
61555          * @param {Roo.GridDD} dd The drag drop object
61556          * @param {String} targetId The target drag drop object
61557          * @param {event} e The raw browser event
61558          */
61559         "dragdrop" : true,
61560         /**
61561          * @event dragover
61562          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61563          * @param {Grid} this
61564          * @param {Roo.GridDD} dd The drag drop object
61565          * @param {String} targetId The target drag drop object
61566          * @param {event} e The raw browser event
61567          */
61568         "dragover" : true,
61569         /**
61570          * @event dragenter
61571          *  Fires when the dragged row(s) first cross another DD target while being dragged
61572          * @param {Grid} this
61573          * @param {Roo.GridDD} dd The drag drop object
61574          * @param {String} targetId The target drag drop object
61575          * @param {event} e The raw browser event
61576          */
61577         "dragenter" : true,
61578         /**
61579          * @event dragout
61580          * Fires when the dragged row(s) leave another DD target while being dragged
61581          * @param {Grid} this
61582          * @param {Roo.GridDD} dd The drag drop object
61583          * @param {String} targetId The target drag drop object
61584          * @param {event} e The raw browser event
61585          */
61586         "dragout" : true,
61587         /**
61588          * @event rowclass
61589          * Fires when a row is rendered, so you can change add a style to it.
61590          * @param {GridView} gridview   The grid view
61591          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61592          */
61593         'rowclass' : true,
61594
61595         /**
61596          * @event render
61597          * Fires when the grid is rendered
61598          * @param {Grid} grid
61599          */
61600         'render' : true
61601     });
61602
61603     Roo.grid.Grid.superclass.constructor.call(this);
61604 };
61605 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
61606     
61607     /**
61608          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
61609          */
61610         /**
61611          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
61612          */
61613         /**
61614          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
61615          */
61616         /**
61617          * @cfg {Roo.data.Store} ds The data store for the grid
61618          */
61619         /**
61620          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
61621          */
61622         /**
61623      * @cfg {String} ddGroup - drag drop group.
61624      */
61625       /**
61626      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
61627      */
61628
61629     /**
61630      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
61631      */
61632     minColumnWidth : 25,
61633
61634     /**
61635      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
61636      * <b>on initial render.</b> It is more efficient to explicitly size the columns
61637      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
61638      */
61639     autoSizeColumns : false,
61640
61641     /**
61642      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
61643      */
61644     autoSizeHeaders : true,
61645
61646     /**
61647      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
61648      */
61649     monitorWindowResize : true,
61650
61651     /**
61652      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
61653      * rows measured to get a columns size. Default is 0 (all rows).
61654      */
61655     maxRowsToMeasure : 0,
61656
61657     /**
61658      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
61659      */
61660     trackMouseOver : true,
61661
61662     /**
61663     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
61664     */
61665       /**
61666     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
61667     */
61668     
61669     /**
61670     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
61671     */
61672     enableDragDrop : false,
61673     
61674     /**
61675     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
61676     */
61677     enableColumnMove : true,
61678     
61679     /**
61680     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
61681     */
61682     enableColumnHide : true,
61683     
61684     /**
61685     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
61686     */
61687     enableRowHeightSync : false,
61688     
61689     /**
61690     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
61691     */
61692     stripeRows : true,
61693     
61694     /**
61695     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
61696     */
61697     autoHeight : false,
61698
61699     /**
61700      * @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.
61701      */
61702     autoExpandColumn : false,
61703
61704     /**
61705     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
61706     * Default is 50.
61707     */
61708     autoExpandMin : 50,
61709
61710     /**
61711     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
61712     */
61713     autoExpandMax : 1000,
61714
61715     /**
61716     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
61717     */
61718     view : null,
61719
61720     /**
61721     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
61722     */
61723     loadMask : false,
61724     /**
61725     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
61726     */
61727     dropTarget: false,
61728      /**
61729     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
61730     */ 
61731     sortColMenu : false,
61732     
61733     // private
61734     rendered : false,
61735
61736     /**
61737     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
61738     * of a fixed width. Default is false.
61739     */
61740     /**
61741     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
61742     */
61743     
61744     
61745     /**
61746     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
61747     * %0 is replaced with the number of selected rows.
61748     */
61749     ddText : "{0} selected row{1}",
61750     
61751     
61752     /**
61753      * Called once after all setup has been completed and the grid is ready to be rendered.
61754      * @return {Roo.grid.Grid} this
61755      */
61756     render : function()
61757     {
61758         var c = this.container;
61759         // try to detect autoHeight/width mode
61760         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
61761             this.autoHeight = true;
61762         }
61763         var view = this.getView();
61764         view.init(this);
61765
61766         c.on("click", this.onClick, this);
61767         c.on("dblclick", this.onDblClick, this);
61768         c.on("contextmenu", this.onContextMenu, this);
61769         c.on("keydown", this.onKeyDown, this);
61770         if (Roo.isTouch) {
61771             c.on("touchstart", this.onTouchStart, this);
61772         }
61773
61774         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
61775
61776         this.getSelectionModel().init(this);
61777
61778         view.render();
61779
61780         if(this.loadMask){
61781             this.loadMask = new Roo.LoadMask(this.container,
61782                     Roo.apply({store:this.dataSource}, this.loadMask));
61783         }
61784         
61785         
61786         if (this.toolbar && this.toolbar.xtype) {
61787             this.toolbar.container = this.getView().getHeaderPanel(true);
61788             this.toolbar = new Roo.Toolbar(this.toolbar);
61789         }
61790         if (this.footer && this.footer.xtype) {
61791             this.footer.dataSource = this.getDataSource();
61792             this.footer.container = this.getView().getFooterPanel(true);
61793             this.footer = Roo.factory(this.footer, Roo);
61794         }
61795         if (this.dropTarget && this.dropTarget.xtype) {
61796             delete this.dropTarget.xtype;
61797             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
61798         }
61799         
61800         
61801         this.rendered = true;
61802         this.fireEvent('render', this);
61803         return this;
61804     },
61805
61806     /**
61807      * Reconfigures the grid to use a different Store and Column Model.
61808      * The View will be bound to the new objects and refreshed.
61809      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
61810      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
61811      */
61812     reconfigure : function(dataSource, colModel){
61813         if(this.loadMask){
61814             this.loadMask.destroy();
61815             this.loadMask = new Roo.LoadMask(this.container,
61816                     Roo.apply({store:dataSource}, this.loadMask));
61817         }
61818         this.view.bind(dataSource, colModel);
61819         this.dataSource = dataSource;
61820         this.colModel = colModel;
61821         this.view.refresh(true);
61822     },
61823     /**
61824      * addColumns
61825      * Add's a column, default at the end..
61826      
61827      * @param {int} position to add (default end)
61828      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
61829      */
61830     addColumns : function(pos, ar)
61831     {
61832         
61833         for (var i =0;i< ar.length;i++) {
61834             var cfg = ar[i];
61835             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
61836             this.cm.lookup[cfg.id] = cfg;
61837         }
61838         
61839         
61840         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
61841             pos = this.cm.config.length; //this.cm.config.push(cfg);
61842         } 
61843         pos = Math.max(0,pos);
61844         ar.unshift(0);
61845         ar.unshift(pos);
61846         this.cm.config.splice.apply(this.cm.config, ar);
61847         
61848         
61849         
61850         this.view.generateRules(this.cm);
61851         this.view.refresh(true);
61852         
61853     },
61854     
61855     
61856     
61857     
61858     // private
61859     onKeyDown : function(e){
61860         this.fireEvent("keydown", e);
61861     },
61862
61863     /**
61864      * Destroy this grid.
61865      * @param {Boolean} removeEl True to remove the element
61866      */
61867     destroy : function(removeEl, keepListeners){
61868         if(this.loadMask){
61869             this.loadMask.destroy();
61870         }
61871         var c = this.container;
61872         c.removeAllListeners();
61873         this.view.destroy();
61874         this.colModel.purgeListeners();
61875         if(!keepListeners){
61876             this.purgeListeners();
61877         }
61878         c.update("");
61879         if(removeEl === true){
61880             c.remove();
61881         }
61882     },
61883
61884     // private
61885     processEvent : function(name, e){
61886         // does this fire select???
61887         //Roo.log('grid:processEvent '  + name);
61888         
61889         if (name != 'touchstart' ) {
61890             this.fireEvent(name, e);    
61891         }
61892         
61893         var t = e.getTarget();
61894         var v = this.view;
61895         var header = v.findHeaderIndex(t);
61896         if(header !== false){
61897             var ename = name == 'touchstart' ? 'click' : name;
61898              
61899             this.fireEvent("header" + ename, this, header, e);
61900         }else{
61901             var row = v.findRowIndex(t);
61902             var cell = v.findCellIndex(t);
61903             if (name == 'touchstart') {
61904                 // first touch is always a click.
61905                 // hopefull this happens after selection is updated.?
61906                 name = false;
61907                 
61908                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
61909                     var cs = this.selModel.getSelectedCell();
61910                     if (row == cs[0] && cell == cs[1]){
61911                         name = 'dblclick';
61912                     }
61913                 }
61914                 if (typeof(this.selModel.getSelections) != 'undefined') {
61915                     var cs = this.selModel.getSelections();
61916                     var ds = this.dataSource;
61917                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
61918                         name = 'dblclick';
61919                     }
61920                 }
61921                 if (!name) {
61922                     return;
61923                 }
61924             }
61925             
61926             
61927             if(row !== false){
61928                 this.fireEvent("row" + name, this, row, e);
61929                 if(cell !== false){
61930                     this.fireEvent("cell" + name, this, row, cell, e);
61931                 }
61932             }
61933         }
61934     },
61935
61936     // private
61937     onClick : function(e){
61938         this.processEvent("click", e);
61939     },
61940    // private
61941     onTouchStart : function(e){
61942         this.processEvent("touchstart", e);
61943     },
61944
61945     // private
61946     onContextMenu : function(e, t){
61947         this.processEvent("contextmenu", e);
61948     },
61949
61950     // private
61951     onDblClick : function(e){
61952         this.processEvent("dblclick", e);
61953     },
61954
61955     // private
61956     walkCells : function(row, col, step, fn, scope){
61957         var cm = this.colModel, clen = cm.getColumnCount();
61958         var ds = this.dataSource, rlen = ds.getCount(), first = true;
61959         if(step < 0){
61960             if(col < 0){
61961                 row--;
61962                 first = false;
61963             }
61964             while(row >= 0){
61965                 if(!first){
61966                     col = clen-1;
61967                 }
61968                 first = false;
61969                 while(col >= 0){
61970                     if(fn.call(scope || this, row, col, cm) === true){
61971                         return [row, col];
61972                     }
61973                     col--;
61974                 }
61975                 row--;
61976             }
61977         } else {
61978             if(col >= clen){
61979                 row++;
61980                 first = false;
61981             }
61982             while(row < rlen){
61983                 if(!first){
61984                     col = 0;
61985                 }
61986                 first = false;
61987                 while(col < clen){
61988                     if(fn.call(scope || this, row, col, cm) === true){
61989                         return [row, col];
61990                     }
61991                     col++;
61992                 }
61993                 row++;
61994             }
61995         }
61996         return null;
61997     },
61998
61999     // private
62000     getSelections : function(){
62001         return this.selModel.getSelections();
62002     },
62003
62004     /**
62005      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
62006      * but if manual update is required this method will initiate it.
62007      */
62008     autoSize : function(){
62009         if(this.rendered){
62010             this.view.layout();
62011             if(this.view.adjustForScroll){
62012                 this.view.adjustForScroll();
62013             }
62014         }
62015     },
62016
62017     /**
62018      * Returns the grid's underlying element.
62019      * @return {Element} The element
62020      */
62021     getGridEl : function(){
62022         return this.container;
62023     },
62024
62025     // private for compatibility, overridden by editor grid
62026     stopEditing : function(){},
62027
62028     /**
62029      * Returns the grid's SelectionModel.
62030      * @return {SelectionModel}
62031      */
62032     getSelectionModel : function(){
62033         if(!this.selModel){
62034             this.selModel = new Roo.grid.RowSelectionModel();
62035         }
62036         return this.selModel;
62037     },
62038
62039     /**
62040      * Returns the grid's DataSource.
62041      * @return {DataSource}
62042      */
62043     getDataSource : function(){
62044         return this.dataSource;
62045     },
62046
62047     /**
62048      * Returns the grid's ColumnModel.
62049      * @return {ColumnModel}
62050      */
62051     getColumnModel : function(){
62052         return this.colModel;
62053     },
62054
62055     /**
62056      * Returns the grid's GridView object.
62057      * @return {GridView}
62058      */
62059     getView : function(){
62060         if(!this.view){
62061             this.view = new Roo.grid.GridView(this.viewConfig);
62062             this.relayEvents(this.view, [
62063                 "beforerowremoved", "beforerowsinserted",
62064                 "beforerefresh", "rowremoved",
62065                 "rowsinserted", "rowupdated" ,"refresh"
62066             ]);
62067         }
62068         return this.view;
62069     },
62070     /**
62071      * Called to get grid's drag proxy text, by default returns this.ddText.
62072      * Override this to put something different in the dragged text.
62073      * @return {String}
62074      */
62075     getDragDropText : function(){
62076         var count = this.selModel.getCount();
62077         return String.format(this.ddText, count, count == 1 ? '' : 's');
62078     }
62079 });
62080 /*
62081  * Based on:
62082  * Ext JS Library 1.1.1
62083  * Copyright(c) 2006-2007, Ext JS, LLC.
62084  *
62085  * Originally Released Under LGPL - original licence link has changed is not relivant.
62086  *
62087  * Fork - LGPL
62088  * <script type="text/javascript">
62089  */
62090  /**
62091  * @class Roo.grid.AbstractGridView
62092  * @extends Roo.util.Observable
62093  * @abstract
62094  * Abstract base class for grid Views
62095  * @constructor
62096  */
62097 Roo.grid.AbstractGridView = function(){
62098         this.grid = null;
62099         
62100         this.events = {
62101             "beforerowremoved" : true,
62102             "beforerowsinserted" : true,
62103             "beforerefresh" : true,
62104             "rowremoved" : true,
62105             "rowsinserted" : true,
62106             "rowupdated" : true,
62107             "refresh" : true
62108         };
62109     Roo.grid.AbstractGridView.superclass.constructor.call(this);
62110 };
62111
62112 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
62113     rowClass : "x-grid-row",
62114     cellClass : "x-grid-cell",
62115     tdClass : "x-grid-td",
62116     hdClass : "x-grid-hd",
62117     splitClass : "x-grid-hd-split",
62118     
62119     init: function(grid){
62120         this.grid = grid;
62121                 var cid = this.grid.getGridEl().id;
62122         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
62123         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
62124         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
62125         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
62126         },
62127         
62128     getColumnRenderers : function(){
62129         var renderers = [];
62130         var cm = this.grid.colModel;
62131         var colCount = cm.getColumnCount();
62132         for(var i = 0; i < colCount; i++){
62133             renderers[i] = cm.getRenderer(i);
62134         }
62135         return renderers;
62136     },
62137     
62138     getColumnIds : function(){
62139         var ids = [];
62140         var cm = this.grid.colModel;
62141         var colCount = cm.getColumnCount();
62142         for(var i = 0; i < colCount; i++){
62143             ids[i] = cm.getColumnId(i);
62144         }
62145         return ids;
62146     },
62147     
62148     getDataIndexes : function(){
62149         if(!this.indexMap){
62150             this.indexMap = this.buildIndexMap();
62151         }
62152         return this.indexMap.colToData;
62153     },
62154     
62155     getColumnIndexByDataIndex : function(dataIndex){
62156         if(!this.indexMap){
62157             this.indexMap = this.buildIndexMap();
62158         }
62159         return this.indexMap.dataToCol[dataIndex];
62160     },
62161     
62162     /**
62163      * Set a css style for a column dynamically. 
62164      * @param {Number} colIndex The index of the column
62165      * @param {String} name The css property name
62166      * @param {String} value The css value
62167      */
62168     setCSSStyle : function(colIndex, name, value){
62169         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
62170         Roo.util.CSS.updateRule(selector, name, value);
62171     },
62172     
62173     generateRules : function(cm){
62174         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
62175         Roo.util.CSS.removeStyleSheet(rulesId);
62176         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62177             var cid = cm.getColumnId(i);
62178             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
62179                          this.tdSelector, cid, " {\n}\n",
62180                          this.hdSelector, cid, " {\n}\n",
62181                          this.splitSelector, cid, " {\n}\n");
62182         }
62183         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62184     }
62185 });/*
62186  * Based on:
62187  * Ext JS Library 1.1.1
62188  * Copyright(c) 2006-2007, Ext JS, LLC.
62189  *
62190  * Originally Released Under LGPL - original licence link has changed is not relivant.
62191  *
62192  * Fork - LGPL
62193  * <script type="text/javascript">
62194  */
62195
62196 // private
62197 // This is a support class used internally by the Grid components
62198 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
62199     this.grid = grid;
62200     this.view = grid.getView();
62201     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62202     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
62203     if(hd2){
62204         this.setHandleElId(Roo.id(hd));
62205         this.setOuterHandleElId(Roo.id(hd2));
62206     }
62207     this.scroll = false;
62208 };
62209 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
62210     maxDragWidth: 120,
62211     getDragData : function(e){
62212         var t = Roo.lib.Event.getTarget(e);
62213         var h = this.view.findHeaderCell(t);
62214         if(h){
62215             return {ddel: h.firstChild, header:h};
62216         }
62217         return false;
62218     },
62219
62220     onInitDrag : function(e){
62221         this.view.headersDisabled = true;
62222         var clone = this.dragData.ddel.cloneNode(true);
62223         clone.id = Roo.id();
62224         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
62225         this.proxy.update(clone);
62226         return true;
62227     },
62228
62229     afterValidDrop : function(){
62230         var v = this.view;
62231         setTimeout(function(){
62232             v.headersDisabled = false;
62233         }, 50);
62234     },
62235
62236     afterInvalidDrop : function(){
62237         var v = this.view;
62238         setTimeout(function(){
62239             v.headersDisabled = false;
62240         }, 50);
62241     }
62242 });
62243 /*
62244  * Based on:
62245  * Ext JS Library 1.1.1
62246  * Copyright(c) 2006-2007, Ext JS, LLC.
62247  *
62248  * Originally Released Under LGPL - original licence link has changed is not relivant.
62249  *
62250  * Fork - LGPL
62251  * <script type="text/javascript">
62252  */
62253 // private
62254 // This is a support class used internally by the Grid components
62255 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
62256     this.grid = grid;
62257     this.view = grid.getView();
62258     // split the proxies so they don't interfere with mouse events
62259     this.proxyTop = Roo.DomHelper.append(document.body, {
62260         cls:"col-move-top", html:"&#160;"
62261     }, true);
62262     this.proxyBottom = Roo.DomHelper.append(document.body, {
62263         cls:"col-move-bottom", html:"&#160;"
62264     }, true);
62265     this.proxyTop.hide = this.proxyBottom.hide = function(){
62266         this.setLeftTop(-100,-100);
62267         this.setStyle("visibility", "hidden");
62268     };
62269     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62270     // temporarily disabled
62271     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
62272     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
62273 };
62274 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
62275     proxyOffsets : [-4, -9],
62276     fly: Roo.Element.fly,
62277
62278     getTargetFromEvent : function(e){
62279         var t = Roo.lib.Event.getTarget(e);
62280         var cindex = this.view.findCellIndex(t);
62281         if(cindex !== false){
62282             return this.view.getHeaderCell(cindex);
62283         }
62284         return null;
62285     },
62286
62287     nextVisible : function(h){
62288         var v = this.view, cm = this.grid.colModel;
62289         h = h.nextSibling;
62290         while(h){
62291             if(!cm.isHidden(v.getCellIndex(h))){
62292                 return h;
62293             }
62294             h = h.nextSibling;
62295         }
62296         return null;
62297     },
62298
62299     prevVisible : function(h){
62300         var v = this.view, cm = this.grid.colModel;
62301         h = h.prevSibling;
62302         while(h){
62303             if(!cm.isHidden(v.getCellIndex(h))){
62304                 return h;
62305             }
62306             h = h.prevSibling;
62307         }
62308         return null;
62309     },
62310
62311     positionIndicator : function(h, n, e){
62312         var x = Roo.lib.Event.getPageX(e);
62313         var r = Roo.lib.Dom.getRegion(n.firstChild);
62314         var px, pt, py = r.top + this.proxyOffsets[1];
62315         if((r.right - x) <= (r.right-r.left)/2){
62316             px = r.right+this.view.borderWidth;
62317             pt = "after";
62318         }else{
62319             px = r.left;
62320             pt = "before";
62321         }
62322         var oldIndex = this.view.getCellIndex(h);
62323         var newIndex = this.view.getCellIndex(n);
62324
62325         if(this.grid.colModel.isFixed(newIndex)){
62326             return false;
62327         }
62328
62329         var locked = this.grid.colModel.isLocked(newIndex);
62330
62331         if(pt == "after"){
62332             newIndex++;
62333         }
62334         if(oldIndex < newIndex){
62335             newIndex--;
62336         }
62337         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
62338             return false;
62339         }
62340         px +=  this.proxyOffsets[0];
62341         this.proxyTop.setLeftTop(px, py);
62342         this.proxyTop.show();
62343         if(!this.bottomOffset){
62344             this.bottomOffset = this.view.mainHd.getHeight();
62345         }
62346         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
62347         this.proxyBottom.show();
62348         return pt;
62349     },
62350
62351     onNodeEnter : function(n, dd, e, data){
62352         if(data.header != n){
62353             this.positionIndicator(data.header, n, e);
62354         }
62355     },
62356
62357     onNodeOver : function(n, dd, e, data){
62358         var result = false;
62359         if(data.header != n){
62360             result = this.positionIndicator(data.header, n, e);
62361         }
62362         if(!result){
62363             this.proxyTop.hide();
62364             this.proxyBottom.hide();
62365         }
62366         return result ? this.dropAllowed : this.dropNotAllowed;
62367     },
62368
62369     onNodeOut : function(n, dd, e, data){
62370         this.proxyTop.hide();
62371         this.proxyBottom.hide();
62372     },
62373
62374     onNodeDrop : function(n, dd, e, data){
62375         var h = data.header;
62376         if(h != n){
62377             var cm = this.grid.colModel;
62378             var x = Roo.lib.Event.getPageX(e);
62379             var r = Roo.lib.Dom.getRegion(n.firstChild);
62380             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
62381             var oldIndex = this.view.getCellIndex(h);
62382             var newIndex = this.view.getCellIndex(n);
62383             var locked = cm.isLocked(newIndex);
62384             if(pt == "after"){
62385                 newIndex++;
62386             }
62387             if(oldIndex < newIndex){
62388                 newIndex--;
62389             }
62390             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
62391                 return false;
62392             }
62393             cm.setLocked(oldIndex, locked, true);
62394             cm.moveColumn(oldIndex, newIndex);
62395             this.grid.fireEvent("columnmove", oldIndex, newIndex);
62396             return true;
62397         }
62398         return false;
62399     }
62400 });
62401 /*
62402  * Based on:
62403  * Ext JS Library 1.1.1
62404  * Copyright(c) 2006-2007, Ext JS, LLC.
62405  *
62406  * Originally Released Under LGPL - original licence link has changed is not relivant.
62407  *
62408  * Fork - LGPL
62409  * <script type="text/javascript">
62410  */
62411   
62412 /**
62413  * @class Roo.grid.GridView
62414  * @extends Roo.util.Observable
62415  *
62416  * @constructor
62417  * @param {Object} config
62418  */
62419 Roo.grid.GridView = function(config){
62420     Roo.grid.GridView.superclass.constructor.call(this);
62421     this.el = null;
62422
62423     Roo.apply(this, config);
62424 };
62425
62426 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
62427
62428     unselectable :  'unselectable="on"',
62429     unselectableCls :  'x-unselectable',
62430     
62431     
62432     rowClass : "x-grid-row",
62433
62434     cellClass : "x-grid-col",
62435
62436     tdClass : "x-grid-td",
62437
62438     hdClass : "x-grid-hd",
62439
62440     splitClass : "x-grid-split",
62441
62442     sortClasses : ["sort-asc", "sort-desc"],
62443
62444     enableMoveAnim : false,
62445
62446     hlColor: "C3DAF9",
62447
62448     dh : Roo.DomHelper,
62449
62450     fly : Roo.Element.fly,
62451
62452     css : Roo.util.CSS,
62453
62454     borderWidth: 1,
62455
62456     splitOffset: 3,
62457
62458     scrollIncrement : 22,
62459
62460     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
62461
62462     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
62463
62464     bind : function(ds, cm){
62465         if(this.ds){
62466             this.ds.un("load", this.onLoad, this);
62467             this.ds.un("datachanged", this.onDataChange, this);
62468             this.ds.un("add", this.onAdd, this);
62469             this.ds.un("remove", this.onRemove, this);
62470             this.ds.un("update", this.onUpdate, this);
62471             this.ds.un("clear", this.onClear, this);
62472         }
62473         if(ds){
62474             ds.on("load", this.onLoad, this);
62475             ds.on("datachanged", this.onDataChange, this);
62476             ds.on("add", this.onAdd, this);
62477             ds.on("remove", this.onRemove, this);
62478             ds.on("update", this.onUpdate, this);
62479             ds.on("clear", this.onClear, this);
62480         }
62481         this.ds = ds;
62482
62483         if(this.cm){
62484             this.cm.un("widthchange", this.onColWidthChange, this);
62485             this.cm.un("headerchange", this.onHeaderChange, this);
62486             this.cm.un("hiddenchange", this.onHiddenChange, this);
62487             this.cm.un("columnmoved", this.onColumnMove, this);
62488             this.cm.un("columnlockchange", this.onColumnLock, this);
62489         }
62490         if(cm){
62491             this.generateRules(cm);
62492             cm.on("widthchange", this.onColWidthChange, this);
62493             cm.on("headerchange", this.onHeaderChange, this);
62494             cm.on("hiddenchange", this.onHiddenChange, this);
62495             cm.on("columnmoved", this.onColumnMove, this);
62496             cm.on("columnlockchange", this.onColumnLock, this);
62497         }
62498         this.cm = cm;
62499     },
62500
62501     init: function(grid){
62502         Roo.grid.GridView.superclass.init.call(this, grid);
62503
62504         this.bind(grid.dataSource, grid.colModel);
62505
62506         grid.on("headerclick", this.handleHeaderClick, this);
62507
62508         if(grid.trackMouseOver){
62509             grid.on("mouseover", this.onRowOver, this);
62510             grid.on("mouseout", this.onRowOut, this);
62511         }
62512         grid.cancelTextSelection = function(){};
62513         this.gridId = grid.id;
62514
62515         var tpls = this.templates || {};
62516
62517         if(!tpls.master){
62518             tpls.master = new Roo.Template(
62519                '<div class="x-grid" hidefocus="true">',
62520                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
62521                   '<div class="x-grid-topbar"></div>',
62522                   '<div class="x-grid-scroller"><div></div></div>',
62523                   '<div class="x-grid-locked">',
62524                       '<div class="x-grid-header">{lockedHeader}</div>',
62525                       '<div class="x-grid-body">{lockedBody}</div>',
62526                   "</div>",
62527                   '<div class="x-grid-viewport">',
62528                       '<div class="x-grid-header">{header}</div>',
62529                       '<div class="x-grid-body">{body}</div>',
62530                   "</div>",
62531                   '<div class="x-grid-bottombar"></div>',
62532                  
62533                   '<div class="x-grid-resize-proxy">&#160;</div>',
62534                "</div>"
62535             );
62536             tpls.master.disableformats = true;
62537         }
62538
62539         if(!tpls.header){
62540             tpls.header = new Roo.Template(
62541                '<table border="0" cellspacing="0" cellpadding="0">',
62542                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
62543                "</table>{splits}"
62544             );
62545             tpls.header.disableformats = true;
62546         }
62547         tpls.header.compile();
62548
62549         if(!tpls.hcell){
62550             tpls.hcell = new Roo.Template(
62551                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
62552                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
62553                 "</div></td>"
62554              );
62555              tpls.hcell.disableFormats = true;
62556         }
62557         tpls.hcell.compile();
62558
62559         if(!tpls.hsplit){
62560             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
62561                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
62562             tpls.hsplit.disableFormats = true;
62563         }
62564         tpls.hsplit.compile();
62565
62566         if(!tpls.body){
62567             tpls.body = new Roo.Template(
62568                '<table border="0" cellspacing="0" cellpadding="0">',
62569                "<tbody>{rows}</tbody>",
62570                "</table>"
62571             );
62572             tpls.body.disableFormats = true;
62573         }
62574         tpls.body.compile();
62575
62576         if(!tpls.row){
62577             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
62578             tpls.row.disableFormats = true;
62579         }
62580         tpls.row.compile();
62581
62582         if(!tpls.cell){
62583             tpls.cell = new Roo.Template(
62584                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
62585                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
62586                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
62587                 "</td>"
62588             );
62589             tpls.cell.disableFormats = true;
62590         }
62591         tpls.cell.compile();
62592
62593         this.templates = tpls;
62594     },
62595
62596     // remap these for backwards compat
62597     onColWidthChange : function(){
62598         this.updateColumns.apply(this, arguments);
62599     },
62600     onHeaderChange : function(){
62601         this.updateHeaders.apply(this, arguments);
62602     }, 
62603     onHiddenChange : function(){
62604         this.handleHiddenChange.apply(this, arguments);
62605     },
62606     onColumnMove : function(){
62607         this.handleColumnMove.apply(this, arguments);
62608     },
62609     onColumnLock : function(){
62610         this.handleLockChange.apply(this, arguments);
62611     },
62612
62613     onDataChange : function(){
62614         this.refresh();
62615         this.updateHeaderSortState();
62616     },
62617
62618     onClear : function(){
62619         this.refresh();
62620     },
62621
62622     onUpdate : function(ds, record){
62623         this.refreshRow(record);
62624     },
62625
62626     refreshRow : function(record){
62627         var ds = this.ds, index;
62628         if(typeof record == 'number'){
62629             index = record;
62630             record = ds.getAt(index);
62631         }else{
62632             index = ds.indexOf(record);
62633         }
62634         this.insertRows(ds, index, index, true);
62635         this.onRemove(ds, record, index+1, true);
62636         this.syncRowHeights(index, index);
62637         this.layout();
62638         this.fireEvent("rowupdated", this, index, record);
62639     },
62640
62641     onAdd : function(ds, records, index){
62642         this.insertRows(ds, index, index + (records.length-1));
62643     },
62644
62645     onRemove : function(ds, record, index, isUpdate){
62646         if(isUpdate !== true){
62647             this.fireEvent("beforerowremoved", this, index, record);
62648         }
62649         var bt = this.getBodyTable(), lt = this.getLockedTable();
62650         if(bt.rows[index]){
62651             bt.firstChild.removeChild(bt.rows[index]);
62652         }
62653         if(lt.rows[index]){
62654             lt.firstChild.removeChild(lt.rows[index]);
62655         }
62656         if(isUpdate !== true){
62657             this.stripeRows(index);
62658             this.syncRowHeights(index, index);
62659             this.layout();
62660             this.fireEvent("rowremoved", this, index, record);
62661         }
62662     },
62663
62664     onLoad : function(){
62665         this.scrollToTop();
62666     },
62667
62668     /**
62669      * Scrolls the grid to the top
62670      */
62671     scrollToTop : function(){
62672         if(this.scroller){
62673             this.scroller.dom.scrollTop = 0;
62674             this.syncScroll();
62675         }
62676     },
62677
62678     /**
62679      * Gets a panel in the header of the grid that can be used for toolbars etc.
62680      * After modifying the contents of this panel a call to grid.autoSize() may be
62681      * required to register any changes in size.
62682      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
62683      * @return Roo.Element
62684      */
62685     getHeaderPanel : function(doShow){
62686         if(doShow){
62687             this.headerPanel.show();
62688         }
62689         return this.headerPanel;
62690     },
62691
62692     /**
62693      * Gets a panel in the footer of the grid that can be used for toolbars etc.
62694      * After modifying the contents of this panel a call to grid.autoSize() may be
62695      * required to register any changes in size.
62696      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
62697      * @return Roo.Element
62698      */
62699     getFooterPanel : function(doShow){
62700         if(doShow){
62701             this.footerPanel.show();
62702         }
62703         return this.footerPanel;
62704     },
62705
62706     initElements : function(){
62707         var E = Roo.Element;
62708         var el = this.grid.getGridEl().dom.firstChild;
62709         var cs = el.childNodes;
62710
62711         this.el = new E(el);
62712         
62713          this.focusEl = new E(el.firstChild);
62714         this.focusEl.swallowEvent("click", true);
62715         
62716         this.headerPanel = new E(cs[1]);
62717         this.headerPanel.enableDisplayMode("block");
62718
62719         this.scroller = new E(cs[2]);
62720         this.scrollSizer = new E(this.scroller.dom.firstChild);
62721
62722         this.lockedWrap = new E(cs[3]);
62723         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
62724         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
62725
62726         this.mainWrap = new E(cs[4]);
62727         this.mainHd = new E(this.mainWrap.dom.firstChild);
62728         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
62729
62730         this.footerPanel = new E(cs[5]);
62731         this.footerPanel.enableDisplayMode("block");
62732
62733         this.resizeProxy = new E(cs[6]);
62734
62735         this.headerSelector = String.format(
62736            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
62737            this.lockedHd.id, this.mainHd.id
62738         );
62739
62740         this.splitterSelector = String.format(
62741            '#{0} div.x-grid-split, #{1} div.x-grid-split',
62742            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
62743         );
62744     },
62745     idToCssName : function(s)
62746     {
62747         return s.replace(/[^a-z0-9]+/ig, '-');
62748     },
62749
62750     getHeaderCell : function(index){
62751         return Roo.DomQuery.select(this.headerSelector)[index];
62752     },
62753
62754     getHeaderCellMeasure : function(index){
62755         return this.getHeaderCell(index).firstChild;
62756     },
62757
62758     getHeaderCellText : function(index){
62759         return this.getHeaderCell(index).firstChild.firstChild;
62760     },
62761
62762     getLockedTable : function(){
62763         return this.lockedBody.dom.firstChild;
62764     },
62765
62766     getBodyTable : function(){
62767         return this.mainBody.dom.firstChild;
62768     },
62769
62770     getLockedRow : function(index){
62771         return this.getLockedTable().rows[index];
62772     },
62773
62774     getRow : function(index){
62775         return this.getBodyTable().rows[index];
62776     },
62777
62778     getRowComposite : function(index){
62779         if(!this.rowEl){
62780             this.rowEl = new Roo.CompositeElementLite();
62781         }
62782         var els = [], lrow, mrow;
62783         if(lrow = this.getLockedRow(index)){
62784             els.push(lrow);
62785         }
62786         if(mrow = this.getRow(index)){
62787             els.push(mrow);
62788         }
62789         this.rowEl.elements = els;
62790         return this.rowEl;
62791     },
62792     /**
62793      * Gets the 'td' of the cell
62794      * 
62795      * @param {Integer} rowIndex row to select
62796      * @param {Integer} colIndex column to select
62797      * 
62798      * @return {Object} 
62799      */
62800     getCell : function(rowIndex, colIndex){
62801         var locked = this.cm.getLockedCount();
62802         var source;
62803         if(colIndex < locked){
62804             source = this.lockedBody.dom.firstChild;
62805         }else{
62806             source = this.mainBody.dom.firstChild;
62807             colIndex -= locked;
62808         }
62809         return source.rows[rowIndex].childNodes[colIndex];
62810     },
62811
62812     getCellText : function(rowIndex, colIndex){
62813         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
62814     },
62815
62816     getCellBox : function(cell){
62817         var b = this.fly(cell).getBox();
62818         if(Roo.isOpera){ // opera fails to report the Y
62819             b.y = cell.offsetTop + this.mainBody.getY();
62820         }
62821         return b;
62822     },
62823
62824     getCellIndex : function(cell){
62825         var id = String(cell.className).match(this.cellRE);
62826         if(id){
62827             return parseInt(id[1], 10);
62828         }
62829         return 0;
62830     },
62831
62832     findHeaderIndex : function(n){
62833         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62834         return r ? this.getCellIndex(r) : false;
62835     },
62836
62837     findHeaderCell : function(n){
62838         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62839         return r ? r : false;
62840     },
62841
62842     findRowIndex : function(n){
62843         if(!n){
62844             return false;
62845         }
62846         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
62847         return r ? r.rowIndex : false;
62848     },
62849
62850     findCellIndex : function(node){
62851         var stop = this.el.dom;
62852         while(node && node != stop){
62853             if(this.findRE.test(node.className)){
62854                 return this.getCellIndex(node);
62855             }
62856             node = node.parentNode;
62857         }
62858         return false;
62859     },
62860
62861     getColumnId : function(index){
62862         return this.cm.getColumnId(index);
62863     },
62864
62865     getSplitters : function()
62866     {
62867         if(this.splitterSelector){
62868            return Roo.DomQuery.select(this.splitterSelector);
62869         }else{
62870             return null;
62871       }
62872     },
62873
62874     getSplitter : function(index){
62875         return this.getSplitters()[index];
62876     },
62877
62878     onRowOver : function(e, t){
62879         var row;
62880         if((row = this.findRowIndex(t)) !== false){
62881             this.getRowComposite(row).addClass("x-grid-row-over");
62882         }
62883     },
62884
62885     onRowOut : function(e, t){
62886         var row;
62887         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
62888             this.getRowComposite(row).removeClass("x-grid-row-over");
62889         }
62890     },
62891
62892     renderHeaders : function(){
62893         var cm = this.cm;
62894         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
62895         var cb = [], lb = [], sb = [], lsb = [], p = {};
62896         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62897             p.cellId = "x-grid-hd-0-" + i;
62898             p.splitId = "x-grid-csplit-0-" + i;
62899             p.id = cm.getColumnId(i);
62900             p.value = cm.getColumnHeader(i) || "";
62901             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
62902             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
62903             if(!cm.isLocked(i)){
62904                 cb[cb.length] = ct.apply(p);
62905                 sb[sb.length] = st.apply(p);
62906             }else{
62907                 lb[lb.length] = ct.apply(p);
62908                 lsb[lsb.length] = st.apply(p);
62909             }
62910         }
62911         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
62912                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
62913     },
62914
62915     updateHeaders : function(){
62916         var html = this.renderHeaders();
62917         this.lockedHd.update(html[0]);
62918         this.mainHd.update(html[1]);
62919     },
62920
62921     /**
62922      * Focuses the specified row.
62923      * @param {Number} row The row index
62924      */
62925     focusRow : function(row)
62926     {
62927         //Roo.log('GridView.focusRow');
62928         var x = this.scroller.dom.scrollLeft;
62929         this.focusCell(row, 0, false);
62930         this.scroller.dom.scrollLeft = x;
62931     },
62932
62933     /**
62934      * Focuses the specified cell.
62935      * @param {Number} row The row index
62936      * @param {Number} col The column index
62937      * @param {Boolean} hscroll false to disable horizontal scrolling
62938      */
62939     focusCell : function(row, col, hscroll)
62940     {
62941         //Roo.log('GridView.focusCell');
62942         var el = this.ensureVisible(row, col, hscroll);
62943         this.focusEl.alignTo(el, "tl-tl");
62944         if(Roo.isGecko){
62945             this.focusEl.focus();
62946         }else{
62947             this.focusEl.focus.defer(1, this.focusEl);
62948         }
62949     },
62950
62951     /**
62952      * Scrolls the specified cell into view
62953      * @param {Number} row The row index
62954      * @param {Number} col The column index
62955      * @param {Boolean} hscroll false to disable horizontal scrolling
62956      */
62957     ensureVisible : function(row, col, hscroll)
62958     {
62959         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
62960         //return null; //disable for testing.
62961         if(typeof row != "number"){
62962             row = row.rowIndex;
62963         }
62964         if(row < 0 && row >= this.ds.getCount()){
62965             return  null;
62966         }
62967         col = (col !== undefined ? col : 0);
62968         var cm = this.grid.colModel;
62969         while(cm.isHidden(col)){
62970             col++;
62971         }
62972
62973         var el = this.getCell(row, col);
62974         if(!el){
62975             return null;
62976         }
62977         var c = this.scroller.dom;
62978
62979         var ctop = parseInt(el.offsetTop, 10);
62980         var cleft = parseInt(el.offsetLeft, 10);
62981         var cbot = ctop + el.offsetHeight;
62982         var cright = cleft + el.offsetWidth;
62983         
62984         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
62985         var stop = parseInt(c.scrollTop, 10);
62986         var sleft = parseInt(c.scrollLeft, 10);
62987         var sbot = stop + ch;
62988         var sright = sleft + c.clientWidth;
62989         /*
62990         Roo.log('GridView.ensureVisible:' +
62991                 ' ctop:' + ctop +
62992                 ' c.clientHeight:' + c.clientHeight +
62993                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
62994                 ' stop:' + stop +
62995                 ' cbot:' + cbot +
62996                 ' sbot:' + sbot +
62997                 ' ch:' + ch  
62998                 );
62999         */
63000         if(ctop < stop){
63001             c.scrollTop = ctop;
63002             //Roo.log("set scrolltop to ctop DISABLE?");
63003         }else if(cbot > sbot){
63004             //Roo.log("set scrolltop to cbot-ch");
63005             c.scrollTop = cbot-ch;
63006         }
63007         
63008         if(hscroll !== false){
63009             if(cleft < sleft){
63010                 c.scrollLeft = cleft;
63011             }else if(cright > sright){
63012                 c.scrollLeft = cright-c.clientWidth;
63013             }
63014         }
63015          
63016         return el;
63017     },
63018
63019     updateColumns : function(){
63020         this.grid.stopEditing();
63021         var cm = this.grid.colModel, colIds = this.getColumnIds();
63022         //var totalWidth = cm.getTotalWidth();
63023         var pos = 0;
63024         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63025             //if(cm.isHidden(i)) continue;
63026             var w = cm.getColumnWidth(i);
63027             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63028             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63029         }
63030         this.updateSplitters();
63031     },
63032
63033     generateRules : function(cm){
63034         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
63035         Roo.util.CSS.removeStyleSheet(rulesId);
63036         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63037             var cid = cm.getColumnId(i);
63038             var align = '';
63039             if(cm.config[i].align){
63040                 align = 'text-align:'+cm.config[i].align+';';
63041             }
63042             var hidden = '';
63043             if(cm.isHidden(i)){
63044                 hidden = 'display:none;';
63045             }
63046             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
63047             ruleBuf.push(
63048                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
63049                     this.hdSelector, cid, " {\n", align, width, "}\n",
63050                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
63051                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
63052         }
63053         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
63054     },
63055
63056     updateSplitters : function(){
63057         var cm = this.cm, s = this.getSplitters();
63058         if(s){ // splitters not created yet
63059             var pos = 0, locked = true;
63060             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63061                 if(cm.isHidden(i)) {
63062                     continue;
63063                 }
63064                 var w = cm.getColumnWidth(i); // make sure it's a number
63065                 if(!cm.isLocked(i) && locked){
63066                     pos = 0;
63067                     locked = false;
63068                 }
63069                 pos += w;
63070                 s[i].style.left = (pos-this.splitOffset) + "px";
63071             }
63072         }
63073     },
63074
63075     handleHiddenChange : function(colModel, colIndex, hidden){
63076         if(hidden){
63077             this.hideColumn(colIndex);
63078         }else{
63079             this.unhideColumn(colIndex);
63080         }
63081     },
63082
63083     hideColumn : function(colIndex){
63084         var cid = this.getColumnId(colIndex);
63085         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
63086         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
63087         if(Roo.isSafari){
63088             this.updateHeaders();
63089         }
63090         this.updateSplitters();
63091         this.layout();
63092     },
63093
63094     unhideColumn : function(colIndex){
63095         var cid = this.getColumnId(colIndex);
63096         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
63097         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
63098
63099         if(Roo.isSafari){
63100             this.updateHeaders();
63101         }
63102         this.updateSplitters();
63103         this.layout();
63104     },
63105
63106     insertRows : function(dm, firstRow, lastRow, isUpdate){
63107         if(firstRow == 0 && lastRow == dm.getCount()-1){
63108             this.refresh();
63109         }else{
63110             if(!isUpdate){
63111                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
63112             }
63113             var s = this.getScrollState();
63114             var markup = this.renderRows(firstRow, lastRow);
63115             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
63116             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
63117             this.restoreScroll(s);
63118             if(!isUpdate){
63119                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
63120                 this.syncRowHeights(firstRow, lastRow);
63121                 this.stripeRows(firstRow);
63122                 this.layout();
63123             }
63124         }
63125     },
63126
63127     bufferRows : function(markup, target, index){
63128         var before = null, trows = target.rows, tbody = target.tBodies[0];
63129         if(index < trows.length){
63130             before = trows[index];
63131         }
63132         var b = document.createElement("div");
63133         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
63134         var rows = b.firstChild.rows;
63135         for(var i = 0, len = rows.length; i < len; i++){
63136             if(before){
63137                 tbody.insertBefore(rows[0], before);
63138             }else{
63139                 tbody.appendChild(rows[0]);
63140             }
63141         }
63142         b.innerHTML = "";
63143         b = null;
63144     },
63145
63146     deleteRows : function(dm, firstRow, lastRow){
63147         if(dm.getRowCount()<1){
63148             this.fireEvent("beforerefresh", this);
63149             this.mainBody.update("");
63150             this.lockedBody.update("");
63151             this.fireEvent("refresh", this);
63152         }else{
63153             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
63154             var bt = this.getBodyTable();
63155             var tbody = bt.firstChild;
63156             var rows = bt.rows;
63157             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
63158                 tbody.removeChild(rows[firstRow]);
63159             }
63160             this.stripeRows(firstRow);
63161             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
63162         }
63163     },
63164
63165     updateRows : function(dataSource, firstRow, lastRow){
63166         var s = this.getScrollState();
63167         this.refresh();
63168         this.restoreScroll(s);
63169     },
63170
63171     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
63172         if(!noRefresh){
63173            this.refresh();
63174         }
63175         this.updateHeaderSortState();
63176     },
63177
63178     getScrollState : function(){
63179         
63180         var sb = this.scroller.dom;
63181         return {left: sb.scrollLeft, top: sb.scrollTop};
63182     },
63183
63184     stripeRows : function(startRow){
63185         if(!this.grid.stripeRows || this.ds.getCount() < 1){
63186             return;
63187         }
63188         startRow = startRow || 0;
63189         var rows = this.getBodyTable().rows;
63190         var lrows = this.getLockedTable().rows;
63191         var cls = ' x-grid-row-alt ';
63192         for(var i = startRow, len = rows.length; i < len; i++){
63193             var row = rows[i], lrow = lrows[i];
63194             var isAlt = ((i+1) % 2 == 0);
63195             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
63196             if(isAlt == hasAlt){
63197                 continue;
63198             }
63199             if(isAlt){
63200                 row.className += " x-grid-row-alt";
63201             }else{
63202                 row.className = row.className.replace("x-grid-row-alt", "");
63203             }
63204             if(lrow){
63205                 lrow.className = row.className;
63206             }
63207         }
63208     },
63209
63210     restoreScroll : function(state){
63211         //Roo.log('GridView.restoreScroll');
63212         var sb = this.scroller.dom;
63213         sb.scrollLeft = state.left;
63214         sb.scrollTop = state.top;
63215         this.syncScroll();
63216     },
63217
63218     syncScroll : function(){
63219         //Roo.log('GridView.syncScroll');
63220         var sb = this.scroller.dom;
63221         var sh = this.mainHd.dom;
63222         var bs = this.mainBody.dom;
63223         var lv = this.lockedBody.dom;
63224         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
63225         lv.scrollTop = bs.scrollTop = sb.scrollTop;
63226     },
63227
63228     handleScroll : function(e){
63229         this.syncScroll();
63230         var sb = this.scroller.dom;
63231         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
63232         e.stopEvent();
63233     },
63234
63235     handleWheel : function(e){
63236         var d = e.getWheelDelta();
63237         this.scroller.dom.scrollTop -= d*22;
63238         // set this here to prevent jumpy scrolling on large tables
63239         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
63240         e.stopEvent();
63241     },
63242
63243     renderRows : function(startRow, endRow){
63244         // pull in all the crap needed to render rows
63245         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
63246         var colCount = cm.getColumnCount();
63247
63248         if(ds.getCount() < 1){
63249             return ["", ""];
63250         }
63251
63252         // build a map for all the columns
63253         var cs = [];
63254         for(var i = 0; i < colCount; i++){
63255             var name = cm.getDataIndex(i);
63256             cs[i] = {
63257                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
63258                 renderer : cm.getRenderer(i),
63259                 id : cm.getColumnId(i),
63260                 locked : cm.isLocked(i),
63261                 has_editor : cm.isCellEditable(i)
63262             };
63263         }
63264
63265         startRow = startRow || 0;
63266         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63267
63268         // records to render
63269         var rs = ds.getRange(startRow, endRow);
63270
63271         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63272     },
63273
63274     // As much as I hate to duplicate code, this was branched because FireFox really hates
63275     // [].join("") on strings. The performance difference was substantial enough to
63276     // branch this function
63277     doRender : Roo.isGecko ?
63278             function(cs, rs, ds, startRow, colCount, stripe){
63279                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63280                 // buffers
63281                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63282                 
63283                 var hasListener = this.grid.hasListener('rowclass');
63284                 var rowcfg = {};
63285                 for(var j = 0, len = rs.length; j < len; j++){
63286                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
63287                     for(var i = 0; i < colCount; i++){
63288                         c = cs[i];
63289                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63290                         p.id = c.id;
63291                         p.css = p.attr = "";
63292                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63293                         if(p.value == undefined || p.value === "") {
63294                             p.value = "&#160;";
63295                         }
63296                         if(c.has_editor){
63297                             p.css += ' x-grid-editable-cell';
63298                         }
63299                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
63300                             p.css +=  ' x-grid-dirty-cell';
63301                         }
63302                         var markup = ct.apply(p);
63303                         if(!c.locked){
63304                             cb+= markup;
63305                         }else{
63306                             lcb+= markup;
63307                         }
63308                     }
63309                     var alt = [];
63310                     if(stripe && ((rowIndex+1) % 2 == 0)){
63311                         alt.push("x-grid-row-alt")
63312                     }
63313                     if(r.dirty){
63314                         alt.push(  " x-grid-dirty-row");
63315                     }
63316                     rp.cells = lcb;
63317                     if(this.getRowClass){
63318                         alt.push(this.getRowClass(r, rowIndex));
63319                     }
63320                     if (hasListener) {
63321                         rowcfg = {
63322                              
63323                             record: r,
63324                             rowIndex : rowIndex,
63325                             rowClass : ''
63326                         };
63327                         this.grid.fireEvent('rowclass', this, rowcfg);
63328                         alt.push(rowcfg.rowClass);
63329                     }
63330                     rp.alt = alt.join(" ");
63331                     lbuf+= rt.apply(rp);
63332                     rp.cells = cb;
63333                     buf+=  rt.apply(rp);
63334                 }
63335                 return [lbuf, buf];
63336             } :
63337             function(cs, rs, ds, startRow, colCount, stripe){
63338                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63339                 // buffers
63340                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63341                 var hasListener = this.grid.hasListener('rowclass');
63342  
63343                 var rowcfg = {};
63344                 for(var j = 0, len = rs.length; j < len; j++){
63345                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
63346                     for(var i = 0; i < colCount; i++){
63347                         c = cs[i];
63348                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63349                         p.id = c.id;
63350                         p.css = p.attr = "";
63351                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63352                         if(p.value == undefined || p.value === "") {
63353                             p.value = "&#160;";
63354                         }
63355                         //Roo.log(c);
63356                          if(c.has_editor){
63357                             p.css += ' x-grid-editable-cell';
63358                         }
63359                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
63360                             p.css += ' x-grid-dirty-cell' 
63361                         }
63362                         
63363                         var markup = ct.apply(p);
63364                         if(!c.locked){
63365                             cb[cb.length] = markup;
63366                         }else{
63367                             lcb[lcb.length] = markup;
63368                         }
63369                     }
63370                     var alt = [];
63371                     if(stripe && ((rowIndex+1) % 2 == 0)){
63372                         alt.push( "x-grid-row-alt");
63373                     }
63374                     if(r.dirty){
63375                         alt.push(" x-grid-dirty-row");
63376                     }
63377                     rp.cells = lcb;
63378                     if(this.getRowClass){
63379                         alt.push( this.getRowClass(r, rowIndex));
63380                     }
63381                     if (hasListener) {
63382                         rowcfg = {
63383                              
63384                             record: r,
63385                             rowIndex : rowIndex,
63386                             rowClass : ''
63387                         };
63388                         this.grid.fireEvent('rowclass', this, rowcfg);
63389                         alt.push(rowcfg.rowClass);
63390                     }
63391                     
63392                     rp.alt = alt.join(" ");
63393                     rp.cells = lcb.join("");
63394                     lbuf[lbuf.length] = rt.apply(rp);
63395                     rp.cells = cb.join("");
63396                     buf[buf.length] =  rt.apply(rp);
63397                 }
63398                 return [lbuf.join(""), buf.join("")];
63399             },
63400
63401     renderBody : function(){
63402         var markup = this.renderRows();
63403         var bt = this.templates.body;
63404         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
63405     },
63406
63407     /**
63408      * Refreshes the grid
63409      * @param {Boolean} headersToo
63410      */
63411     refresh : function(headersToo){
63412         this.fireEvent("beforerefresh", this);
63413         this.grid.stopEditing();
63414         var result = this.renderBody();
63415         this.lockedBody.update(result[0]);
63416         this.mainBody.update(result[1]);
63417         if(headersToo === true){
63418             this.updateHeaders();
63419             this.updateColumns();
63420             this.updateSplitters();
63421             this.updateHeaderSortState();
63422         }
63423         this.syncRowHeights();
63424         this.layout();
63425         this.fireEvent("refresh", this);
63426     },
63427
63428     handleColumnMove : function(cm, oldIndex, newIndex){
63429         this.indexMap = null;
63430         var s = this.getScrollState();
63431         this.refresh(true);
63432         this.restoreScroll(s);
63433         this.afterMove(newIndex);
63434     },
63435
63436     afterMove : function(colIndex){
63437         if(this.enableMoveAnim && Roo.enableFx){
63438             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
63439         }
63440         // if multisort - fix sortOrder, and reload..
63441         if (this.grid.dataSource.multiSort) {
63442             // the we can call sort again..
63443             var dm = this.grid.dataSource;
63444             var cm = this.grid.colModel;
63445             var so = [];
63446             for(var i = 0; i < cm.config.length; i++ ) {
63447                 
63448                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
63449                     continue; // dont' bother, it's not in sort list or being set.
63450                 }
63451                 
63452                 so.push(cm.config[i].dataIndex);
63453             };
63454             dm.sortOrder = so;
63455             dm.load(dm.lastOptions);
63456             
63457             
63458         }
63459         
63460     },
63461
63462     updateCell : function(dm, rowIndex, dataIndex){
63463         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
63464         if(typeof colIndex == "undefined"){ // not present in grid
63465             return;
63466         }
63467         var cm = this.grid.colModel;
63468         var cell = this.getCell(rowIndex, colIndex);
63469         var cellText = this.getCellText(rowIndex, colIndex);
63470
63471         var p = {
63472             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
63473             id : cm.getColumnId(colIndex),
63474             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
63475         };
63476         var renderer = cm.getRenderer(colIndex);
63477         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
63478         if(typeof val == "undefined" || val === "") {
63479             val = "&#160;";
63480         }
63481         cellText.innerHTML = val;
63482         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
63483         this.syncRowHeights(rowIndex, rowIndex);
63484     },
63485
63486     calcColumnWidth : function(colIndex, maxRowsToMeasure){
63487         var maxWidth = 0;
63488         if(this.grid.autoSizeHeaders){
63489             var h = this.getHeaderCellMeasure(colIndex);
63490             maxWidth = Math.max(maxWidth, h.scrollWidth);
63491         }
63492         var tb, index;
63493         if(this.cm.isLocked(colIndex)){
63494             tb = this.getLockedTable();
63495             index = colIndex;
63496         }else{
63497             tb = this.getBodyTable();
63498             index = colIndex - this.cm.getLockedCount();
63499         }
63500         if(tb && tb.rows){
63501             var rows = tb.rows;
63502             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
63503             for(var i = 0; i < stopIndex; i++){
63504                 var cell = rows[i].childNodes[index].firstChild;
63505                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
63506             }
63507         }
63508         return maxWidth + /*margin for error in IE*/ 5;
63509     },
63510     /**
63511      * Autofit a column to its content.
63512      * @param {Number} colIndex
63513      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
63514      */
63515      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
63516          if(this.cm.isHidden(colIndex)){
63517              return; // can't calc a hidden column
63518          }
63519         if(forceMinSize){
63520             var cid = this.cm.getColumnId(colIndex);
63521             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
63522            if(this.grid.autoSizeHeaders){
63523                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
63524            }
63525         }
63526         var newWidth = this.calcColumnWidth(colIndex);
63527         this.cm.setColumnWidth(colIndex,
63528             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
63529         if(!suppressEvent){
63530             this.grid.fireEvent("columnresize", colIndex, newWidth);
63531         }
63532     },
63533
63534     /**
63535      * Autofits all columns to their content and then expands to fit any extra space in the grid
63536      */
63537      autoSizeColumns : function(){
63538         var cm = this.grid.colModel;
63539         var colCount = cm.getColumnCount();
63540         for(var i = 0; i < colCount; i++){
63541             this.autoSizeColumn(i, true, true);
63542         }
63543         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
63544             this.fitColumns();
63545         }else{
63546             this.updateColumns();
63547             this.layout();
63548         }
63549     },
63550
63551     /**
63552      * Autofits all columns to the grid's width proportionate with their current size
63553      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
63554      */
63555     fitColumns : function(reserveScrollSpace){
63556         var cm = this.grid.colModel;
63557         var colCount = cm.getColumnCount();
63558         var cols = [];
63559         var width = 0;
63560         var i, w;
63561         for (i = 0; i < colCount; i++){
63562             if(!cm.isHidden(i) && !cm.isFixed(i)){
63563                 w = cm.getColumnWidth(i);
63564                 cols.push(i);
63565                 cols.push(w);
63566                 width += w;
63567             }
63568         }
63569         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
63570         if(reserveScrollSpace){
63571             avail -= 17;
63572         }
63573         var frac = (avail - cm.getTotalWidth())/width;
63574         while (cols.length){
63575             w = cols.pop();
63576             i = cols.pop();
63577             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
63578         }
63579         this.updateColumns();
63580         this.layout();
63581     },
63582
63583     onRowSelect : function(rowIndex){
63584         var row = this.getRowComposite(rowIndex);
63585         row.addClass("x-grid-row-selected");
63586     },
63587
63588     onRowDeselect : function(rowIndex){
63589         var row = this.getRowComposite(rowIndex);
63590         row.removeClass("x-grid-row-selected");
63591     },
63592
63593     onCellSelect : function(row, col){
63594         var cell = this.getCell(row, col);
63595         if(cell){
63596             Roo.fly(cell).addClass("x-grid-cell-selected");
63597         }
63598     },
63599
63600     onCellDeselect : function(row, col){
63601         var cell = this.getCell(row, col);
63602         if(cell){
63603             Roo.fly(cell).removeClass("x-grid-cell-selected");
63604         }
63605     },
63606
63607     updateHeaderSortState : function(){
63608         
63609         // sort state can be single { field: xxx, direction : yyy}
63610         // or   { xxx=>ASC , yyy : DESC ..... }
63611         
63612         var mstate = {};
63613         if (!this.ds.multiSort) { 
63614             var state = this.ds.getSortState();
63615             if(!state){
63616                 return;
63617             }
63618             mstate[state.field] = state.direction;
63619             // FIXME... - this is not used here.. but might be elsewhere..
63620             this.sortState = state;
63621             
63622         } else {
63623             mstate = this.ds.sortToggle;
63624         }
63625         //remove existing sort classes..
63626         
63627         var sc = this.sortClasses;
63628         var hds = this.el.select(this.headerSelector).removeClass(sc);
63629         
63630         for(var f in mstate) {
63631         
63632             var sortColumn = this.cm.findColumnIndex(f);
63633             
63634             if(sortColumn != -1){
63635                 var sortDir = mstate[f];        
63636                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
63637             }
63638         }
63639         
63640          
63641         
63642     },
63643
63644
63645     handleHeaderClick : function(g, index,e){
63646         
63647         Roo.log("header click");
63648         
63649         if (Roo.isTouch) {
63650             // touch events on header are handled by context
63651             this.handleHdCtx(g,index,e);
63652             return;
63653         }
63654         
63655         
63656         if(this.headersDisabled){
63657             return;
63658         }
63659         var dm = g.dataSource, cm = g.colModel;
63660         if(!cm.isSortable(index)){
63661             return;
63662         }
63663         g.stopEditing();
63664         
63665         if (dm.multiSort) {
63666             // update the sortOrder
63667             var so = [];
63668             for(var i = 0; i < cm.config.length; i++ ) {
63669                 
63670                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
63671                     continue; // dont' bother, it's not in sort list or being set.
63672                 }
63673                 
63674                 so.push(cm.config[i].dataIndex);
63675             };
63676             dm.sortOrder = so;
63677         }
63678         
63679         
63680         dm.sort(cm.getDataIndex(index));
63681     },
63682
63683
63684     destroy : function(){
63685         if(this.colMenu){
63686             this.colMenu.removeAll();
63687             Roo.menu.MenuMgr.unregister(this.colMenu);
63688             this.colMenu.getEl().remove();
63689             delete this.colMenu;
63690         }
63691         if(this.hmenu){
63692             this.hmenu.removeAll();
63693             Roo.menu.MenuMgr.unregister(this.hmenu);
63694             this.hmenu.getEl().remove();
63695             delete this.hmenu;
63696         }
63697         if(this.grid.enableColumnMove){
63698             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63699             if(dds){
63700                 for(var dd in dds){
63701                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
63702                         var elid = dds[dd].dragElId;
63703                         dds[dd].unreg();
63704                         Roo.get(elid).remove();
63705                     } else if(dds[dd].config.isTarget){
63706                         dds[dd].proxyTop.remove();
63707                         dds[dd].proxyBottom.remove();
63708                         dds[dd].unreg();
63709                     }
63710                     if(Roo.dd.DDM.locationCache[dd]){
63711                         delete Roo.dd.DDM.locationCache[dd];
63712                     }
63713                 }
63714                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63715             }
63716         }
63717         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
63718         this.bind(null, null);
63719         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
63720     },
63721
63722     handleLockChange : function(){
63723         this.refresh(true);
63724     },
63725
63726     onDenyColumnLock : function(){
63727
63728     },
63729
63730     onDenyColumnHide : function(){
63731
63732     },
63733
63734     handleHdMenuClick : function(item){
63735         var index = this.hdCtxIndex;
63736         var cm = this.cm, ds = this.ds;
63737         switch(item.id){
63738             case "asc":
63739                 ds.sort(cm.getDataIndex(index), "ASC");
63740                 break;
63741             case "desc":
63742                 ds.sort(cm.getDataIndex(index), "DESC");
63743                 break;
63744             case "lock":
63745                 var lc = cm.getLockedCount();
63746                 if(cm.getColumnCount(true) <= lc+1){
63747                     this.onDenyColumnLock();
63748                     return;
63749                 }
63750                 if(lc != index){
63751                     cm.setLocked(index, true, true);
63752                     cm.moveColumn(index, lc);
63753                     this.grid.fireEvent("columnmove", index, lc);
63754                 }else{
63755                     cm.setLocked(index, true);
63756                 }
63757             break;
63758             case "unlock":
63759                 var lc = cm.getLockedCount();
63760                 if((lc-1) != index){
63761                     cm.setLocked(index, false, true);
63762                     cm.moveColumn(index, lc-1);
63763                     this.grid.fireEvent("columnmove", index, lc-1);
63764                 }else{
63765                     cm.setLocked(index, false);
63766                 }
63767             break;
63768             case 'wider': // used to expand cols on touch..
63769             case 'narrow':
63770                 var cw = cm.getColumnWidth(index);
63771                 cw += (item.id == 'wider' ? 1 : -1) * 50;
63772                 cw = Math.max(0, cw);
63773                 cw = Math.min(cw,4000);
63774                 cm.setColumnWidth(index, cw);
63775                 break;
63776                 
63777             default:
63778                 index = cm.getIndexById(item.id.substr(4));
63779                 if(index != -1){
63780                     if(item.checked && cm.getColumnCount(true) <= 1){
63781                         this.onDenyColumnHide();
63782                         return false;
63783                     }
63784                     cm.setHidden(index, item.checked);
63785                 }
63786         }
63787         return true;
63788     },
63789
63790     beforeColMenuShow : function(){
63791         var cm = this.cm,  colCount = cm.getColumnCount();
63792         this.colMenu.removeAll();
63793         
63794         var items = [];
63795         for(var i = 0; i < colCount; i++){
63796             items.push({
63797                 id: "col-"+cm.getColumnId(i),
63798                 text: cm.getColumnHeader(i),
63799                 checked: !cm.isHidden(i),
63800                 hideOnClick:false
63801             });
63802         }
63803         
63804         if (this.grid.sortColMenu) {
63805             items.sort(function(a,b) {
63806                 if (a.text == b.text) {
63807                     return 0;
63808                 }
63809                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
63810             });
63811         }
63812         
63813         for(var i = 0; i < colCount; i++){
63814             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
63815         }
63816     },
63817
63818     handleHdCtx : function(g, index, e){
63819         e.stopEvent();
63820         var hd = this.getHeaderCell(index);
63821         this.hdCtxIndex = index;
63822         var ms = this.hmenu.items, cm = this.cm;
63823         ms.get("asc").setDisabled(!cm.isSortable(index));
63824         ms.get("desc").setDisabled(!cm.isSortable(index));
63825         if(this.grid.enableColLock !== false){
63826             ms.get("lock").setDisabled(cm.isLocked(index));
63827             ms.get("unlock").setDisabled(!cm.isLocked(index));
63828         }
63829         this.hmenu.show(hd, "tl-bl");
63830     },
63831
63832     handleHdOver : function(e){
63833         var hd = this.findHeaderCell(e.getTarget());
63834         if(hd && !this.headersDisabled){
63835             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
63836                this.fly(hd).addClass("x-grid-hd-over");
63837             }
63838         }
63839     },
63840
63841     handleHdOut : function(e){
63842         var hd = this.findHeaderCell(e.getTarget());
63843         if(hd){
63844             this.fly(hd).removeClass("x-grid-hd-over");
63845         }
63846     },
63847
63848     handleSplitDblClick : function(e, t){
63849         var i = this.getCellIndex(t);
63850         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
63851             this.autoSizeColumn(i, true);
63852             this.layout();
63853         }
63854     },
63855
63856     render : function(){
63857
63858         var cm = this.cm;
63859         var colCount = cm.getColumnCount();
63860
63861         if(this.grid.monitorWindowResize === true){
63862             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
63863         }
63864         var header = this.renderHeaders();
63865         var body = this.templates.body.apply({rows:""});
63866         var html = this.templates.master.apply({
63867             lockedBody: body,
63868             body: body,
63869             lockedHeader: header[0],
63870             header: header[1]
63871         });
63872
63873         //this.updateColumns();
63874
63875         this.grid.getGridEl().dom.innerHTML = html;
63876
63877         this.initElements();
63878         
63879         // a kludge to fix the random scolling effect in webkit
63880         this.el.on("scroll", function() {
63881             this.el.dom.scrollTop=0; // hopefully not recursive..
63882         },this);
63883
63884         this.scroller.on("scroll", this.handleScroll, this);
63885         this.lockedBody.on("mousewheel", this.handleWheel, this);
63886         this.mainBody.on("mousewheel", this.handleWheel, this);
63887
63888         this.mainHd.on("mouseover", this.handleHdOver, this);
63889         this.mainHd.on("mouseout", this.handleHdOut, this);
63890         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
63891                 {delegate: "."+this.splitClass});
63892
63893         this.lockedHd.on("mouseover", this.handleHdOver, this);
63894         this.lockedHd.on("mouseout", this.handleHdOut, this);
63895         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
63896                 {delegate: "."+this.splitClass});
63897
63898         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
63899             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63900         }
63901
63902         this.updateSplitters();
63903
63904         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
63905             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63906             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63907         }
63908
63909         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
63910             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
63911             this.hmenu.add(
63912                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
63913                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
63914             );
63915             if(this.grid.enableColLock !== false){
63916                 this.hmenu.add('-',
63917                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
63918                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
63919                 );
63920             }
63921             if (Roo.isTouch) {
63922                  this.hmenu.add('-',
63923                     {id:"wider", text: this.columnsWiderText},
63924                     {id:"narrow", text: this.columnsNarrowText }
63925                 );
63926                 
63927                  
63928             }
63929             
63930             if(this.grid.enableColumnHide !== false){
63931
63932                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
63933                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
63934                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
63935
63936                 this.hmenu.add('-',
63937                     {id:"columns", text: this.columnsText, menu: this.colMenu}
63938                 );
63939             }
63940             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
63941
63942             this.grid.on("headercontextmenu", this.handleHdCtx, this);
63943         }
63944
63945         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
63946             this.dd = new Roo.grid.GridDragZone(this.grid, {
63947                 ddGroup : this.grid.ddGroup || 'GridDD'
63948             });
63949             
63950         }
63951
63952         /*
63953         for(var i = 0; i < colCount; i++){
63954             if(cm.isHidden(i)){
63955                 this.hideColumn(i);
63956             }
63957             if(cm.config[i].align){
63958                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
63959                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
63960             }
63961         }*/
63962         
63963         this.updateHeaderSortState();
63964
63965         this.beforeInitialResize();
63966         this.layout(true);
63967
63968         // two part rendering gives faster view to the user
63969         this.renderPhase2.defer(1, this);
63970     },
63971
63972     renderPhase2 : function(){
63973         // render the rows now
63974         this.refresh();
63975         if(this.grid.autoSizeColumns){
63976             this.autoSizeColumns();
63977         }
63978     },
63979
63980     beforeInitialResize : function(){
63981
63982     },
63983
63984     onColumnSplitterMoved : function(i, w){
63985         this.userResized = true;
63986         var cm = this.grid.colModel;
63987         cm.setColumnWidth(i, w, true);
63988         var cid = cm.getColumnId(i);
63989         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63990         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63991         this.updateSplitters();
63992         this.layout();
63993         this.grid.fireEvent("columnresize", i, w);
63994     },
63995
63996     syncRowHeights : function(startIndex, endIndex){
63997         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
63998             startIndex = startIndex || 0;
63999             var mrows = this.getBodyTable().rows;
64000             var lrows = this.getLockedTable().rows;
64001             var len = mrows.length-1;
64002             endIndex = Math.min(endIndex || len, len);
64003             for(var i = startIndex; i <= endIndex; i++){
64004                 var m = mrows[i], l = lrows[i];
64005                 var h = Math.max(m.offsetHeight, l.offsetHeight);
64006                 m.style.height = l.style.height = h + "px";
64007             }
64008         }
64009     },
64010
64011     layout : function(initialRender, is2ndPass)
64012     {
64013         var g = this.grid;
64014         var auto = g.autoHeight;
64015         var scrollOffset = 16;
64016         var c = g.getGridEl(), cm = this.cm,
64017                 expandCol = g.autoExpandColumn,
64018                 gv = this;
64019         //c.beginMeasure();
64020
64021         if(!c.dom.offsetWidth){ // display:none?
64022             if(initialRender){
64023                 this.lockedWrap.show();
64024                 this.mainWrap.show();
64025             }
64026             return;
64027         }
64028
64029         var hasLock = this.cm.isLocked(0);
64030
64031         var tbh = this.headerPanel.getHeight();
64032         var bbh = this.footerPanel.getHeight();
64033
64034         if(auto){
64035             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
64036             var newHeight = ch + c.getBorderWidth("tb");
64037             if(g.maxHeight){
64038                 newHeight = Math.min(g.maxHeight, newHeight);
64039             }
64040             c.setHeight(newHeight);
64041         }
64042
64043         if(g.autoWidth){
64044             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
64045         }
64046
64047         var s = this.scroller;
64048
64049         var csize = c.getSize(true);
64050
64051         this.el.setSize(csize.width, csize.height);
64052
64053         this.headerPanel.setWidth(csize.width);
64054         this.footerPanel.setWidth(csize.width);
64055
64056         var hdHeight = this.mainHd.getHeight();
64057         var vw = csize.width;
64058         var vh = csize.height - (tbh + bbh);
64059
64060         s.setSize(vw, vh);
64061
64062         var bt = this.getBodyTable();
64063         
64064         if(cm.getLockedCount() == cm.config.length){
64065             bt = this.getLockedTable();
64066         }
64067         
64068         var ltWidth = hasLock ?
64069                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
64070
64071         var scrollHeight = bt.offsetHeight;
64072         var scrollWidth = ltWidth + bt.offsetWidth;
64073         var vscroll = false, hscroll = false;
64074
64075         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
64076
64077         var lw = this.lockedWrap, mw = this.mainWrap;
64078         var lb = this.lockedBody, mb = this.mainBody;
64079
64080         setTimeout(function(){
64081             var t = s.dom.offsetTop;
64082             var w = s.dom.clientWidth,
64083                 h = s.dom.clientHeight;
64084
64085             lw.setTop(t);
64086             lw.setSize(ltWidth, h);
64087
64088             mw.setLeftTop(ltWidth, t);
64089             mw.setSize(w-ltWidth, h);
64090
64091             lb.setHeight(h-hdHeight);
64092             mb.setHeight(h-hdHeight);
64093
64094             if(is2ndPass !== true && !gv.userResized && expandCol){
64095                 // high speed resize without full column calculation
64096                 
64097                 var ci = cm.getIndexById(expandCol);
64098                 if (ci < 0) {
64099                     ci = cm.findColumnIndex(expandCol);
64100                 }
64101                 ci = Math.max(0, ci); // make sure it's got at least the first col.
64102                 var expandId = cm.getColumnId(ci);
64103                 var  tw = cm.getTotalWidth(false);
64104                 var currentWidth = cm.getColumnWidth(ci);
64105                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
64106                 if(currentWidth != cw){
64107                     cm.setColumnWidth(ci, cw, true);
64108                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64109                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64110                     gv.updateSplitters();
64111                     gv.layout(false, true);
64112                 }
64113             }
64114
64115             if(initialRender){
64116                 lw.show();
64117                 mw.show();
64118             }
64119             //c.endMeasure();
64120         }, 10);
64121     },
64122
64123     onWindowResize : function(){
64124         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
64125             return;
64126         }
64127         this.layout();
64128     },
64129
64130     appendFooter : function(parentEl){
64131         return null;
64132     },
64133
64134     sortAscText : "Sort Ascending",
64135     sortDescText : "Sort Descending",
64136     lockText : "Lock Column",
64137     unlockText : "Unlock Column",
64138     columnsText : "Columns",
64139  
64140     columnsWiderText : "Wider",
64141     columnsNarrowText : "Thinner"
64142 });
64143
64144
64145 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
64146     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
64147     this.proxy.el.addClass('x-grid3-col-dd');
64148 };
64149
64150 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
64151     handleMouseDown : function(e){
64152
64153     },
64154
64155     callHandleMouseDown : function(e){
64156         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
64157     }
64158 });
64159 /*
64160  * Based on:
64161  * Ext JS Library 1.1.1
64162  * Copyright(c) 2006-2007, Ext JS, LLC.
64163  *
64164  * Originally Released Under LGPL - original licence link has changed is not relivant.
64165  *
64166  * Fork - LGPL
64167  * <script type="text/javascript">
64168  */
64169  /**
64170  * @extends Roo.dd.DDProxy
64171  * @class Roo.grid.SplitDragZone
64172  * Support for Column Header resizing
64173  * @constructor
64174  * @param {Object} config
64175  */
64176 // private
64177 // This is a support class used internally by the Grid components
64178 Roo.grid.SplitDragZone = function(grid, hd, hd2){
64179     this.grid = grid;
64180     this.view = grid.getView();
64181     this.proxy = this.view.resizeProxy;
64182     Roo.grid.SplitDragZone.superclass.constructor.call(
64183         this,
64184         hd, // ID
64185         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
64186         {  // CONFIG
64187             dragElId : Roo.id(this.proxy.dom),
64188             resizeFrame:false
64189         }
64190     );
64191     
64192     this.setHandleElId(Roo.id(hd));
64193     if (hd2 !== false) {
64194         this.setOuterHandleElId(Roo.id(hd2));
64195     }
64196     
64197     this.scroll = false;
64198 };
64199 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
64200     fly: Roo.Element.fly,
64201
64202     b4StartDrag : function(x, y){
64203         this.view.headersDisabled = true;
64204         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
64205                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
64206         );
64207         this.proxy.setHeight(h);
64208         
64209         // for old system colWidth really stored the actual width?
64210         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
64211         // which in reality did not work.. - it worked only for fixed sizes
64212         // for resizable we need to use actual sizes.
64213         var w = this.cm.getColumnWidth(this.cellIndex);
64214         if (!this.view.mainWrap) {
64215             // bootstrap.
64216             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
64217         }
64218         
64219         
64220         
64221         // this was w-this.grid.minColumnWidth;
64222         // doesnt really make sense? - w = thie curren width or the rendered one?
64223         var minw = Math.max(w-this.grid.minColumnWidth, 0);
64224         this.resetConstraints();
64225         this.setXConstraint(minw, 1000);
64226         this.setYConstraint(0, 0);
64227         this.minX = x - minw;
64228         this.maxX = x + 1000;
64229         this.startPos = x;
64230         if (!this.view.mainWrap) { // this is Bootstrap code..
64231             this.getDragEl().style.display='block';
64232         }
64233         
64234         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64235     },
64236
64237
64238     handleMouseDown : function(e){
64239         ev = Roo.EventObject.setEvent(e);
64240         var t = this.fly(ev.getTarget());
64241         if(t.hasClass("x-grid-split")){
64242             this.cellIndex = this.view.getCellIndex(t.dom);
64243             this.split = t.dom;
64244             this.cm = this.grid.colModel;
64245             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64246                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64247             }
64248         }
64249     },
64250
64251     endDrag : function(e){
64252         this.view.headersDisabled = false;
64253         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
64254         var diff = endX - this.startPos;
64255         // 
64256         var w = this.cm.getColumnWidth(this.cellIndex);
64257         if (!this.view.mainWrap) {
64258             w = 0;
64259         }
64260         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
64261     },
64262
64263     autoOffset : function(){
64264         this.setDelta(0,0);
64265     }
64266 });/*
64267  * Based on:
64268  * Ext JS Library 1.1.1
64269  * Copyright(c) 2006-2007, Ext JS, LLC.
64270  *
64271  * Originally Released Under LGPL - original licence link has changed is not relivant.
64272  *
64273  * Fork - LGPL
64274  * <script type="text/javascript">
64275  */
64276  
64277 // private
64278 // This is a support class used internally by the Grid components
64279 Roo.grid.GridDragZone = function(grid, config){
64280     this.view = grid.getView();
64281     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64282     if(this.view.lockedBody){
64283         this.setHandleElId(Roo.id(this.view.mainBody.dom));
64284         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
64285     }
64286     this.scroll = false;
64287     this.grid = grid;
64288     this.ddel = document.createElement('div');
64289     this.ddel.className = 'x-grid-dd-wrap';
64290 };
64291
64292 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
64293     ddGroup : "GridDD",
64294
64295     getDragData : function(e){
64296         var t = Roo.lib.Event.getTarget(e);
64297         var rowIndex = this.view.findRowIndex(t);
64298         var sm = this.grid.selModel;
64299             
64300         //Roo.log(rowIndex);
64301         
64302         if (sm.getSelectedCell) {
64303             // cell selection..
64304             if (!sm.getSelectedCell()) {
64305                 return false;
64306             }
64307             if (rowIndex != sm.getSelectedCell()[0]) {
64308                 return false;
64309             }
64310         
64311         }
64312         if (sm.getSelections && sm.getSelections().length < 1) {
64313             return false;
64314         }
64315         
64316         
64317         // before it used to all dragging of unseleted... - now we dont do that.
64318         if(rowIndex !== false){
64319             
64320             // if editorgrid.. 
64321             
64322             
64323             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
64324                
64325             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
64326               //  
64327             //}
64328             if (e.hasModifier()){
64329                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
64330             }
64331             
64332             Roo.log("getDragData");
64333             
64334             return {
64335                 grid: this.grid,
64336                 ddel: this.ddel,
64337                 rowIndex: rowIndex,
64338                 selections: sm.getSelections ? sm.getSelections() : (
64339                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
64340             };
64341         }
64342         return false;
64343     },
64344     
64345     
64346     onInitDrag : function(e){
64347         var data = this.dragData;
64348         this.ddel.innerHTML = this.grid.getDragDropText();
64349         this.proxy.update(this.ddel);
64350         // fire start drag?
64351     },
64352
64353     afterRepair : function(){
64354         this.dragging = false;
64355     },
64356
64357     getRepairXY : function(e, data){
64358         return false;
64359     },
64360
64361     onEndDrag : function(data, e){
64362         // fire end drag?
64363     },
64364
64365     onValidDrop : function(dd, e, id){
64366         // fire drag drop?
64367         this.hideProxy();
64368     },
64369
64370     beforeInvalidDrop : function(e, id){
64371
64372     }
64373 });/*
64374  * Based on:
64375  * Ext JS Library 1.1.1
64376  * Copyright(c) 2006-2007, Ext JS, LLC.
64377  *
64378  * Originally Released Under LGPL - original licence link has changed is not relivant.
64379  *
64380  * Fork - LGPL
64381  * <script type="text/javascript">
64382  */
64383  
64384
64385 /**
64386  * @class Roo.grid.ColumnModel
64387  * @extends Roo.util.Observable
64388  * This is the default implementation of a ColumnModel used by the Grid. It defines
64389  * the columns in the grid.
64390  * <br>Usage:<br>
64391  <pre><code>
64392  var colModel = new Roo.grid.ColumnModel([
64393         {header: "Ticker", width: 60, sortable: true, locked: true},
64394         {header: "Company Name", width: 150, sortable: true},
64395         {header: "Market Cap.", width: 100, sortable: true},
64396         {header: "$ Sales", width: 100, sortable: true, renderer: money},
64397         {header: "Employees", width: 100, sortable: true, resizable: false}
64398  ]);
64399  </code></pre>
64400  * <p>
64401  
64402  * The config options listed for this class are options which may appear in each
64403  * individual column definition.
64404  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
64405  * @constructor
64406  * @param {Object} config An Array of column config objects. See this class's
64407  * config objects for details.
64408 */
64409 Roo.grid.ColumnModel = function(config){
64410         /**
64411      * The config passed into the constructor
64412      */
64413     this.config = []; //config;
64414     this.lookup = {};
64415
64416     // if no id, create one
64417     // if the column does not have a dataIndex mapping,
64418     // map it to the order it is in the config
64419     for(var i = 0, len = config.length; i < len; i++){
64420         this.addColumn(config[i]);
64421         
64422     }
64423
64424     /**
64425      * The width of columns which have no width specified (defaults to 100)
64426      * @type Number
64427      */
64428     this.defaultWidth = 100;
64429
64430     /**
64431      * Default sortable of columns which have no sortable specified (defaults to false)
64432      * @type Boolean
64433      */
64434     this.defaultSortable = false;
64435
64436     this.addEvents({
64437         /**
64438              * @event widthchange
64439              * Fires when the width of a column changes.
64440              * @param {ColumnModel} this
64441              * @param {Number} columnIndex The column index
64442              * @param {Number} newWidth The new width
64443              */
64444             "widthchange": true,
64445         /**
64446              * @event headerchange
64447              * Fires when the text of a header changes.
64448              * @param {ColumnModel} this
64449              * @param {Number} columnIndex The column index
64450              * @param {Number} newText The new header text
64451              */
64452             "headerchange": true,
64453         /**
64454              * @event hiddenchange
64455              * Fires when a column is hidden or "unhidden".
64456              * @param {ColumnModel} this
64457              * @param {Number} columnIndex The column index
64458              * @param {Boolean} hidden true if hidden, false otherwise
64459              */
64460             "hiddenchange": true,
64461             /**
64462          * @event columnmoved
64463          * Fires when a column is moved.
64464          * @param {ColumnModel} this
64465          * @param {Number} oldIndex
64466          * @param {Number} newIndex
64467          */
64468         "columnmoved" : true,
64469         /**
64470          * @event columlockchange
64471          * Fires when a column's locked state is changed
64472          * @param {ColumnModel} this
64473          * @param {Number} colIndex
64474          * @param {Boolean} locked true if locked
64475          */
64476         "columnlockchange" : true
64477     });
64478     Roo.grid.ColumnModel.superclass.constructor.call(this);
64479 };
64480 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
64481     /**
64482      * @cfg {String} header [required] The header text to display in the Grid view.
64483      */
64484         /**
64485      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
64486      */
64487         /**
64488      * @cfg {String} smHeader Header at Bootsrap Small width
64489      */
64490         /**
64491      * @cfg {String} mdHeader Header at Bootsrap Medium width
64492      */
64493         /**
64494      * @cfg {String} lgHeader Header at Bootsrap Large width
64495      */
64496         /**
64497      * @cfg {String} xlHeader Header at Bootsrap extra Large width
64498      */
64499     /**
64500      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
64501      * {@link Roo.data.Record} definition from which to draw the column's value. If not
64502      * specified, the column's index is used as an index into the Record's data Array.
64503      */
64504     /**
64505      * @cfg {Number} width  The initial width in pixels of the column. Using this
64506      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
64507      */
64508     /**
64509      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
64510      * Defaults to the value of the {@link #defaultSortable} property.
64511      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
64512      */
64513     /**
64514      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
64515      */
64516     /**
64517      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
64518      */
64519     /**
64520      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
64521      */
64522     /**
64523      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
64524      */
64525     /**
64526      * @cfg {Function} renderer A function used to generate HTML markup for a cell
64527      * given the cell's data value. See {@link #setRenderer}. If not specified, the
64528      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
64529      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
64530      */
64531        /**
64532      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
64533      */
64534     /**
64535      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
64536      */
64537     /**
64538      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
64539      */
64540     /**
64541      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
64542      */
64543     /**
64544      * @cfg {String} tooltip mouse over tooltip text
64545      */
64546     /**
64547      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
64548      */
64549     /**
64550      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
64551      */
64552     /**
64553      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
64554      */
64555     /**
64556      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
64557      */
64558         /**
64559      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
64560      */
64561     /**
64562      * Returns the id of the column at the specified index.
64563      * @param {Number} index The column index
64564      * @return {String} the id
64565      */
64566     getColumnId : function(index){
64567         return this.config[index].id;
64568     },
64569
64570     /**
64571      * Returns the column for a specified id.
64572      * @param {String} id The column id
64573      * @return {Object} the column
64574      */
64575     getColumnById : function(id){
64576         return this.lookup[id];
64577     },
64578
64579     
64580     /**
64581      * Returns the column Object for a specified dataIndex.
64582      * @param {String} dataIndex The column dataIndex
64583      * @return {Object|Boolean} the column or false if not found
64584      */
64585     getColumnByDataIndex: function(dataIndex){
64586         var index = this.findColumnIndex(dataIndex);
64587         return index > -1 ? this.config[index] : false;
64588     },
64589     
64590     /**
64591      * Returns the index for a specified column id.
64592      * @param {String} id The column id
64593      * @return {Number} the index, or -1 if not found
64594      */
64595     getIndexById : function(id){
64596         for(var i = 0, len = this.config.length; i < len; i++){
64597             if(this.config[i].id == id){
64598                 return i;
64599             }
64600         }
64601         return -1;
64602     },
64603     
64604     /**
64605      * Returns the index for a specified column dataIndex.
64606      * @param {String} dataIndex The column dataIndex
64607      * @return {Number} the index, or -1 if not found
64608      */
64609     
64610     findColumnIndex : function(dataIndex){
64611         for(var i = 0, len = this.config.length; i < len; i++){
64612             if(this.config[i].dataIndex == dataIndex){
64613                 return i;
64614             }
64615         }
64616         return -1;
64617     },
64618     
64619     
64620     moveColumn : function(oldIndex, newIndex){
64621         var c = this.config[oldIndex];
64622         this.config.splice(oldIndex, 1);
64623         this.config.splice(newIndex, 0, c);
64624         this.dataMap = null;
64625         this.fireEvent("columnmoved", this, oldIndex, newIndex);
64626     },
64627
64628     isLocked : function(colIndex){
64629         return this.config[colIndex].locked === true;
64630     },
64631
64632     setLocked : function(colIndex, value, suppressEvent){
64633         if(this.isLocked(colIndex) == value){
64634             return;
64635         }
64636         this.config[colIndex].locked = value;
64637         if(!suppressEvent){
64638             this.fireEvent("columnlockchange", this, colIndex, value);
64639         }
64640     },
64641
64642     getTotalLockedWidth : function(){
64643         var totalWidth = 0;
64644         for(var i = 0; i < this.config.length; i++){
64645             if(this.isLocked(i) && !this.isHidden(i)){
64646                 this.totalWidth += this.getColumnWidth(i);
64647             }
64648         }
64649         return totalWidth;
64650     },
64651
64652     getLockedCount : function(){
64653         for(var i = 0, len = this.config.length; i < len; i++){
64654             if(!this.isLocked(i)){
64655                 return i;
64656             }
64657         }
64658         
64659         return this.config.length;
64660     },
64661
64662     /**
64663      * Returns the number of columns.
64664      * @return {Number}
64665      */
64666     getColumnCount : function(visibleOnly){
64667         if(visibleOnly === true){
64668             var c = 0;
64669             for(var i = 0, len = this.config.length; i < len; i++){
64670                 if(!this.isHidden(i)){
64671                     c++;
64672                 }
64673             }
64674             return c;
64675         }
64676         return this.config.length;
64677     },
64678
64679     /**
64680      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
64681      * @param {Function} fn
64682      * @param {Object} scope (optional)
64683      * @return {Array} result
64684      */
64685     getColumnsBy : function(fn, scope){
64686         var r = [];
64687         for(var i = 0, len = this.config.length; i < len; i++){
64688             var c = this.config[i];
64689             if(fn.call(scope||this, c, i) === true){
64690                 r[r.length] = c;
64691             }
64692         }
64693         return r;
64694     },
64695
64696     /**
64697      * Returns true if the specified column is sortable.
64698      * @param {Number} col The column index
64699      * @return {Boolean}
64700      */
64701     isSortable : function(col){
64702         if(typeof this.config[col].sortable == "undefined"){
64703             return this.defaultSortable;
64704         }
64705         return this.config[col].sortable;
64706     },
64707
64708     /**
64709      * Returns the rendering (formatting) function defined for the column.
64710      * @param {Number} col The column index.
64711      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
64712      */
64713     getRenderer : function(col){
64714         if(!this.config[col].renderer){
64715             return Roo.grid.ColumnModel.defaultRenderer;
64716         }
64717         return this.config[col].renderer;
64718     },
64719
64720     /**
64721      * Sets the rendering (formatting) function for a column.
64722      * @param {Number} col The column index
64723      * @param {Function} fn The function to use to process the cell's raw data
64724      * to return HTML markup for the grid view. The render function is called with
64725      * the following parameters:<ul>
64726      * <li>Data value.</li>
64727      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
64728      * <li>css A CSS style string to apply to the table cell.</li>
64729      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
64730      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
64731      * <li>Row index</li>
64732      * <li>Column index</li>
64733      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
64734      */
64735     setRenderer : function(col, fn){
64736         this.config[col].renderer = fn;
64737     },
64738
64739     /**
64740      * Returns the width for the specified column.
64741      * @param {Number} col The column index
64742      * @param (optional) {String} gridSize bootstrap width size.
64743      * @return {Number}
64744      */
64745     getColumnWidth : function(col, gridSize)
64746         {
64747                 var cfg = this.config[col];
64748                 
64749                 if (typeof(gridSize) == 'undefined') {
64750                         return cfg.width * 1 || this.defaultWidth;
64751                 }
64752                 if (gridSize === false) { // if we set it..
64753                         return cfg.width || false;
64754                 }
64755                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
64756                 
64757                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
64758                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
64759                                 continue;
64760                         }
64761                         return cfg[ sizes[i] ];
64762                 }
64763                 return 1;
64764                 
64765     },
64766
64767     /**
64768      * Sets the width for a column.
64769      * @param {Number} col The column index
64770      * @param {Number} width The new width
64771      */
64772     setColumnWidth : function(col, width, suppressEvent){
64773         this.config[col].width = width;
64774         this.totalWidth = null;
64775         if(!suppressEvent){
64776              this.fireEvent("widthchange", this, col, width);
64777         }
64778     },
64779
64780     /**
64781      * Returns the total width of all columns.
64782      * @param {Boolean} includeHidden True to include hidden column widths
64783      * @return {Number}
64784      */
64785     getTotalWidth : function(includeHidden){
64786         if(!this.totalWidth){
64787             this.totalWidth = 0;
64788             for(var i = 0, len = this.config.length; i < len; i++){
64789                 if(includeHidden || !this.isHidden(i)){
64790                     this.totalWidth += this.getColumnWidth(i);
64791                 }
64792             }
64793         }
64794         return this.totalWidth;
64795     },
64796
64797     /**
64798      * Returns the header for the specified column.
64799      * @param {Number} col The column index
64800      * @return {String}
64801      */
64802     getColumnHeader : function(col){
64803         return this.config[col].header;
64804     },
64805
64806     /**
64807      * Sets the header for a column.
64808      * @param {Number} col The column index
64809      * @param {String} header The new header
64810      */
64811     setColumnHeader : function(col, header){
64812         this.config[col].header = header;
64813         this.fireEvent("headerchange", this, col, header);
64814     },
64815
64816     /**
64817      * Returns the tooltip for the specified column.
64818      * @param {Number} col The column index
64819      * @return {String}
64820      */
64821     getColumnTooltip : function(col){
64822             return this.config[col].tooltip;
64823     },
64824     /**
64825      * Sets the tooltip for a column.
64826      * @param {Number} col The column index
64827      * @param {String} tooltip The new tooltip
64828      */
64829     setColumnTooltip : function(col, tooltip){
64830             this.config[col].tooltip = tooltip;
64831     },
64832
64833     /**
64834      * Returns the dataIndex for the specified column.
64835      * @param {Number} col The column index
64836      * @return {Number}
64837      */
64838     getDataIndex : function(col){
64839         return this.config[col].dataIndex;
64840     },
64841
64842     /**
64843      * Sets the dataIndex for a column.
64844      * @param {Number} col The column index
64845      * @param {Number} dataIndex The new dataIndex
64846      */
64847     setDataIndex : function(col, dataIndex){
64848         this.config[col].dataIndex = dataIndex;
64849     },
64850
64851     
64852     
64853     /**
64854      * Returns true if the cell is editable.
64855      * @param {Number} colIndex The column index
64856      * @param {Number} rowIndex The row index - this is nto actually used..?
64857      * @return {Boolean}
64858      */
64859     isCellEditable : function(colIndex, rowIndex){
64860         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
64861     },
64862
64863     /**
64864      * Returns the editor defined for the cell/column.
64865      * return false or null to disable editing.
64866      * @param {Number} colIndex The column index
64867      * @param {Number} rowIndex The row index
64868      * @return {Object}
64869      */
64870     getCellEditor : function(colIndex, rowIndex){
64871         return this.config[colIndex].editor;
64872     },
64873
64874     /**
64875      * Sets if a column is editable.
64876      * @param {Number} col The column index
64877      * @param {Boolean} editable True if the column is editable
64878      */
64879     setEditable : function(col, editable){
64880         this.config[col].editable = editable;
64881     },
64882
64883
64884     /**
64885      * Returns true if the column is hidden.
64886      * @param {Number} colIndex The column index
64887      * @return {Boolean}
64888      */
64889     isHidden : function(colIndex){
64890         return this.config[colIndex].hidden;
64891     },
64892
64893
64894     /**
64895      * Returns true if the column width cannot be changed
64896      */
64897     isFixed : function(colIndex){
64898         return this.config[colIndex].fixed;
64899     },
64900
64901     /**
64902      * Returns true if the column can be resized
64903      * @return {Boolean}
64904      */
64905     isResizable : function(colIndex){
64906         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
64907     },
64908     /**
64909      * Sets if a column is hidden.
64910      * @param {Number} colIndex The column index
64911      * @param {Boolean} hidden True if the column is hidden
64912      */
64913     setHidden : function(colIndex, hidden){
64914         this.config[colIndex].hidden = hidden;
64915         this.totalWidth = null;
64916         this.fireEvent("hiddenchange", this, colIndex, hidden);
64917     },
64918
64919     /**
64920      * Sets the editor for a column.
64921      * @param {Number} col The column index
64922      * @param {Object} editor The editor object
64923      */
64924     setEditor : function(col, editor){
64925         this.config[col].editor = editor;
64926     },
64927     /**
64928      * Add a column (experimental...) - defaults to adding to the end..
64929      * @param {Object} config 
64930     */
64931     addColumn : function(c)
64932     {
64933     
64934         var i = this.config.length;
64935         this.config[i] = c;
64936         
64937         if(typeof c.dataIndex == "undefined"){
64938             c.dataIndex = i;
64939         }
64940         if(typeof c.renderer == "string"){
64941             c.renderer = Roo.util.Format[c.renderer];
64942         }
64943         if(typeof c.id == "undefined"){
64944             c.id = Roo.id();
64945         }
64946         if(c.editor && c.editor.xtype){
64947             c.editor  = Roo.factory(c.editor, Roo.grid);
64948         }
64949         if(c.editor && c.editor.isFormField){
64950             c.editor = new Roo.grid.GridEditor(c.editor);
64951         }
64952         this.lookup[c.id] = c;
64953     }
64954     
64955 });
64956
64957 Roo.grid.ColumnModel.defaultRenderer = function(value)
64958 {
64959     if(typeof value == "object") {
64960         return value;
64961     }
64962         if(typeof value == "string" && value.length < 1){
64963             return "&#160;";
64964         }
64965     
64966         return String.format("{0}", value);
64967 };
64968
64969 // Alias for backwards compatibility
64970 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
64971 /*
64972  * Based on:
64973  * Ext JS Library 1.1.1
64974  * Copyright(c) 2006-2007, Ext JS, LLC.
64975  *
64976  * Originally Released Under LGPL - original licence link has changed is not relivant.
64977  *
64978  * Fork - LGPL
64979  * <script type="text/javascript">
64980  */
64981
64982 /**
64983  * @class Roo.grid.AbstractSelectionModel
64984  * @extends Roo.util.Observable
64985  * @abstract
64986  * Abstract base class for grid SelectionModels.  It provides the interface that should be
64987  * implemented by descendant classes.  This class should not be directly instantiated.
64988  * @constructor
64989  */
64990 Roo.grid.AbstractSelectionModel = function(){
64991     this.locked = false;
64992     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
64993 };
64994
64995 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
64996     /** @ignore Called by the grid automatically. Do not call directly. */
64997     init : function(grid){
64998         this.grid = grid;
64999         this.initEvents();
65000     },
65001
65002     /**
65003      * Locks the selections.
65004      */
65005     lock : function(){
65006         this.locked = true;
65007     },
65008
65009     /**
65010      * Unlocks the selections.
65011      */
65012     unlock : function(){
65013         this.locked = false;
65014     },
65015
65016     /**
65017      * Returns true if the selections are locked.
65018      * @return {Boolean}
65019      */
65020     isLocked : function(){
65021         return this.locked;
65022     }
65023 });/*
65024  * Based on:
65025  * Ext JS Library 1.1.1
65026  * Copyright(c) 2006-2007, Ext JS, LLC.
65027  *
65028  * Originally Released Under LGPL - original licence link has changed is not relivant.
65029  *
65030  * Fork - LGPL
65031  * <script type="text/javascript">
65032  */
65033 /**
65034  * @extends Roo.grid.AbstractSelectionModel
65035  * @class Roo.grid.RowSelectionModel
65036  * The default SelectionModel used by {@link Roo.grid.Grid}.
65037  * It supports multiple selections and keyboard selection/navigation. 
65038  * @constructor
65039  * @param {Object} config
65040  */
65041 Roo.grid.RowSelectionModel = function(config){
65042     Roo.apply(this, config);
65043     this.selections = new Roo.util.MixedCollection(false, function(o){
65044         return o.id;
65045     });
65046
65047     this.last = false;
65048     this.lastActive = false;
65049
65050     this.addEvents({
65051         /**
65052         * @event selectionchange
65053         * Fires when the selection changes
65054         * @param {SelectionModel} this
65055         */
65056        "selectionchange" : true,
65057        /**
65058         * @event afterselectionchange
65059         * Fires after the selection changes (eg. by key press or clicking)
65060         * @param {SelectionModel} this
65061         */
65062        "afterselectionchange" : true,
65063        /**
65064         * @event beforerowselect
65065         * Fires when a row is selected being selected, return false to cancel.
65066         * @param {SelectionModel} this
65067         * @param {Number} rowIndex The selected index
65068         * @param {Boolean} keepExisting False if other selections will be cleared
65069         */
65070        "beforerowselect" : true,
65071        /**
65072         * @event rowselect
65073         * Fires when a row is selected.
65074         * @param {SelectionModel} this
65075         * @param {Number} rowIndex The selected index
65076         * @param {Roo.data.Record} r The record
65077         */
65078        "rowselect" : true,
65079        /**
65080         * @event rowdeselect
65081         * Fires when a row is deselected.
65082         * @param {SelectionModel} this
65083         * @param {Number} rowIndex The selected index
65084         */
65085         "rowdeselect" : true
65086     });
65087     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
65088     this.locked = false;
65089 };
65090
65091 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
65092     /**
65093      * @cfg {Boolean} singleSelect
65094      * True to allow selection of only one row at a time (defaults to false)
65095      */
65096     singleSelect : false,
65097
65098     // private
65099     initEvents : function(){
65100
65101         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
65102             this.grid.on("mousedown", this.handleMouseDown, this);
65103         }else{ // allow click to work like normal
65104             this.grid.on("rowclick", this.handleDragableRowClick, this);
65105         }
65106         // bootstrap does not have a view..
65107         var view = this.grid.view ? this.grid.view : this.grid;
65108         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
65109             "up" : function(e){
65110                 if(!e.shiftKey){
65111                     this.selectPrevious(e.shiftKey);
65112                 }else if(this.last !== false && this.lastActive !== false){
65113                     var last = this.last;
65114                     this.selectRange(this.last,  this.lastActive-1);
65115                     view.focusRow(this.lastActive);
65116                     if(last !== false){
65117                         this.last = last;
65118                     }
65119                 }else{
65120                     this.selectFirstRow();
65121                 }
65122                 this.fireEvent("afterselectionchange", this);
65123             },
65124             "down" : function(e){
65125                 if(!e.shiftKey){
65126                     this.selectNext(e.shiftKey);
65127                 }else if(this.last !== false && this.lastActive !== false){
65128                     var last = this.last;
65129                     this.selectRange(this.last,  this.lastActive+1);
65130                     view.focusRow(this.lastActive);
65131                     if(last !== false){
65132                         this.last = last;
65133                     }
65134                 }else{
65135                     this.selectFirstRow();
65136                 }
65137                 this.fireEvent("afterselectionchange", this);
65138             },
65139             scope: this
65140         });
65141
65142          
65143         view.on("refresh", this.onRefresh, this);
65144         view.on("rowupdated", this.onRowUpdated, this);
65145         view.on("rowremoved", this.onRemove, this);
65146     },
65147
65148     // private
65149     onRefresh : function(){
65150         var ds = this.grid.ds, i, v = this.grid.view;
65151         var s = this.selections;
65152         s.each(function(r){
65153             if((i = ds.indexOfId(r.id)) != -1){
65154                 v.onRowSelect(i);
65155                 s.add(ds.getAt(i)); // updating the selection relate data
65156             }else{
65157                 s.remove(r);
65158             }
65159         });
65160     },
65161
65162     // private
65163     onRemove : function(v, index, r){
65164         this.selections.remove(r);
65165     },
65166
65167     // private
65168     onRowUpdated : function(v, index, r){
65169         if(this.isSelected(r)){
65170             v.onRowSelect(index);
65171         }
65172     },
65173
65174     /**
65175      * Select records.
65176      * @param {Array} records The records to select
65177      * @param {Boolean} keepExisting (optional) True to keep existing selections
65178      */
65179     selectRecords : function(records, keepExisting){
65180         if(!keepExisting){
65181             this.clearSelections();
65182         }
65183         var ds = this.grid.ds;
65184         for(var i = 0, len = records.length; i < len; i++){
65185             this.selectRow(ds.indexOf(records[i]), true);
65186         }
65187     },
65188
65189     /**
65190      * Gets the number of selected rows.
65191      * @return {Number}
65192      */
65193     getCount : function(){
65194         return this.selections.length;
65195     },
65196
65197     /**
65198      * Selects the first row in the grid.
65199      */
65200     selectFirstRow : function(){
65201         this.selectRow(0);
65202     },
65203
65204     /**
65205      * Select the last row.
65206      * @param {Boolean} keepExisting (optional) True to keep existing selections
65207      */
65208     selectLastRow : function(keepExisting){
65209         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
65210     },
65211
65212     /**
65213      * Selects the row immediately following the last selected row.
65214      * @param {Boolean} keepExisting (optional) True to keep existing selections
65215      */
65216     selectNext : function(keepExisting){
65217         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
65218             this.selectRow(this.last+1, keepExisting);
65219             var view = this.grid.view ? this.grid.view : this.grid;
65220             view.focusRow(this.last);
65221         }
65222     },
65223
65224     /**
65225      * Selects the row that precedes the last selected row.
65226      * @param {Boolean} keepExisting (optional) True to keep existing selections
65227      */
65228     selectPrevious : function(keepExisting){
65229         if(this.last){
65230             this.selectRow(this.last-1, keepExisting);
65231             var view = this.grid.view ? this.grid.view : this.grid;
65232             view.focusRow(this.last);
65233         }
65234     },
65235
65236     /**
65237      * Returns the selected records
65238      * @return {Array} Array of selected records
65239      */
65240     getSelections : function(){
65241         return [].concat(this.selections.items);
65242     },
65243
65244     /**
65245      * Returns the first selected record.
65246      * @return {Record}
65247      */
65248     getSelected : function(){
65249         return this.selections.itemAt(0);
65250     },
65251
65252
65253     /**
65254      * Clears all selections.
65255      */
65256     clearSelections : function(fast){
65257         if(this.locked) {
65258             return;
65259         }
65260         if(fast !== true){
65261             var ds = this.grid.ds;
65262             var s = this.selections;
65263             s.each(function(r){
65264                 this.deselectRow(ds.indexOfId(r.id));
65265             }, this);
65266             s.clear();
65267         }else{
65268             this.selections.clear();
65269         }
65270         this.last = false;
65271     },
65272
65273
65274     /**
65275      * Selects all rows.
65276      */
65277     selectAll : function(){
65278         if(this.locked) {
65279             return;
65280         }
65281         this.selections.clear();
65282         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
65283             this.selectRow(i, true);
65284         }
65285     },
65286
65287     /**
65288      * Returns True if there is a selection.
65289      * @return {Boolean}
65290      */
65291     hasSelection : function(){
65292         return this.selections.length > 0;
65293     },
65294
65295     /**
65296      * Returns True if the specified row is selected.
65297      * @param {Number/Record} record The record or index of the record to check
65298      * @return {Boolean}
65299      */
65300     isSelected : function(index){
65301         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
65302         return (r && this.selections.key(r.id) ? true : false);
65303     },
65304
65305     /**
65306      * Returns True if the specified record id is selected.
65307      * @param {String} id The id of record to check
65308      * @return {Boolean}
65309      */
65310     isIdSelected : function(id){
65311         return (this.selections.key(id) ? true : false);
65312     },
65313
65314     // private
65315     handleMouseDown : function(e, t)
65316     {
65317         var view = this.grid.view ? this.grid.view : this.grid;
65318         var rowIndex;
65319         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
65320             return;
65321         };
65322         if(e.shiftKey && this.last !== false){
65323             var last = this.last;
65324             this.selectRange(last, rowIndex, e.ctrlKey);
65325             this.last = last; // reset the last
65326             view.focusRow(rowIndex);
65327         }else{
65328             var isSelected = this.isSelected(rowIndex);
65329             if(e.button !== 0 && isSelected){
65330                 view.focusRow(rowIndex);
65331             }else if(e.ctrlKey && isSelected){
65332                 this.deselectRow(rowIndex);
65333             }else if(!isSelected){
65334                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
65335                 view.focusRow(rowIndex);
65336             }
65337         }
65338         this.fireEvent("afterselectionchange", this);
65339     },
65340     // private
65341     handleDragableRowClick :  function(grid, rowIndex, e) 
65342     {
65343         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
65344             this.selectRow(rowIndex, false);
65345             var view = this.grid.view ? this.grid.view : this.grid;
65346             view.focusRow(rowIndex);
65347              this.fireEvent("afterselectionchange", this);
65348         }
65349     },
65350     
65351     /**
65352      * Selects multiple rows.
65353      * @param {Array} rows Array of the indexes of the row to select
65354      * @param {Boolean} keepExisting (optional) True to keep existing selections
65355      */
65356     selectRows : function(rows, keepExisting){
65357         if(!keepExisting){
65358             this.clearSelections();
65359         }
65360         for(var i = 0, len = rows.length; i < len; i++){
65361             this.selectRow(rows[i], true);
65362         }
65363     },
65364
65365     /**
65366      * Selects a range of rows. All rows in between startRow and endRow are also selected.
65367      * @param {Number} startRow The index of the first row in the range
65368      * @param {Number} endRow The index of the last row in the range
65369      * @param {Boolean} keepExisting (optional) True to retain existing selections
65370      */
65371     selectRange : function(startRow, endRow, keepExisting){
65372         if(this.locked) {
65373             return;
65374         }
65375         if(!keepExisting){
65376             this.clearSelections();
65377         }
65378         if(startRow <= endRow){
65379             for(var i = startRow; i <= endRow; i++){
65380                 this.selectRow(i, true);
65381             }
65382         }else{
65383             for(var i = startRow; i >= endRow; i--){
65384                 this.selectRow(i, true);
65385             }
65386         }
65387     },
65388
65389     /**
65390      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
65391      * @param {Number} startRow The index of the first row in the range
65392      * @param {Number} endRow The index of the last row in the range
65393      */
65394     deselectRange : function(startRow, endRow, preventViewNotify){
65395         if(this.locked) {
65396             return;
65397         }
65398         for(var i = startRow; i <= endRow; i++){
65399             this.deselectRow(i, preventViewNotify);
65400         }
65401     },
65402
65403     /**
65404      * Selects a row.
65405      * @param {Number} row The index of the row to select
65406      * @param {Boolean} keepExisting (optional) True to keep existing selections
65407      */
65408     selectRow : function(index, keepExisting, preventViewNotify){
65409         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
65410             return;
65411         }
65412         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
65413             if(!keepExisting || this.singleSelect){
65414                 this.clearSelections();
65415             }
65416             var r = this.grid.ds.getAt(index);
65417             this.selections.add(r);
65418             this.last = this.lastActive = index;
65419             if(!preventViewNotify){
65420                 var view = this.grid.view ? this.grid.view : this.grid;
65421                 view.onRowSelect(index);
65422             }
65423             this.fireEvent("rowselect", this, index, r);
65424             this.fireEvent("selectionchange", this);
65425         }
65426     },
65427
65428     /**
65429      * Deselects a row.
65430      * @param {Number} row The index of the row to deselect
65431      */
65432     deselectRow : function(index, preventViewNotify){
65433         if(this.locked) {
65434             return;
65435         }
65436         if(this.last == index){
65437             this.last = false;
65438         }
65439         if(this.lastActive == index){
65440             this.lastActive = false;
65441         }
65442         var r = this.grid.ds.getAt(index);
65443         this.selections.remove(r);
65444         if(!preventViewNotify){
65445             var view = this.grid.view ? this.grid.view : this.grid;
65446             view.onRowDeselect(index);
65447         }
65448         this.fireEvent("rowdeselect", this, index);
65449         this.fireEvent("selectionchange", this);
65450     },
65451
65452     // private
65453     restoreLast : function(){
65454         if(this._last){
65455             this.last = this._last;
65456         }
65457     },
65458
65459     // private
65460     acceptsNav : function(row, col, cm){
65461         return !cm.isHidden(col) && cm.isCellEditable(col, row);
65462     },
65463
65464     // private
65465     onEditorKey : function(field, e){
65466         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
65467         if(k == e.TAB){
65468             e.stopEvent();
65469             ed.completeEdit();
65470             if(e.shiftKey){
65471                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65472             }else{
65473                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65474             }
65475         }else if(k == e.ENTER && !e.ctrlKey){
65476             e.stopEvent();
65477             ed.completeEdit();
65478             if(e.shiftKey){
65479                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
65480             }else{
65481                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
65482             }
65483         }else if(k == e.ESC){
65484             ed.cancelEdit();
65485         }
65486         if(newCell){
65487             g.startEditing(newCell[0], newCell[1]);
65488         }
65489     }
65490 });/*
65491  * Based on:
65492  * Ext JS Library 1.1.1
65493  * Copyright(c) 2006-2007, Ext JS, LLC.
65494  *
65495  * Originally Released Under LGPL - original licence link has changed is not relivant.
65496  *
65497  * Fork - LGPL
65498  * <script type="text/javascript">
65499  */
65500 /**
65501  * @class Roo.grid.CellSelectionModel
65502  * @extends Roo.grid.AbstractSelectionModel
65503  * This class provides the basic implementation for cell selection in a grid.
65504  * @constructor
65505  * @param {Object} config The object containing the configuration of this model.
65506  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
65507  */
65508 Roo.grid.CellSelectionModel = function(config){
65509     Roo.apply(this, config);
65510
65511     this.selection = null;
65512
65513     this.addEvents({
65514         /**
65515              * @event beforerowselect
65516              * Fires before a cell is selected.
65517              * @param {SelectionModel} this
65518              * @param {Number} rowIndex The selected row index
65519              * @param {Number} colIndex The selected cell index
65520              */
65521             "beforecellselect" : true,
65522         /**
65523              * @event cellselect
65524              * Fires when a cell is selected.
65525              * @param {SelectionModel} this
65526              * @param {Number} rowIndex The selected row index
65527              * @param {Number} colIndex The selected cell index
65528              */
65529             "cellselect" : true,
65530         /**
65531              * @event selectionchange
65532              * Fires when the active selection changes.
65533              * @param {SelectionModel} this
65534              * @param {Object} selection null for no selection or an object (o) with two properties
65535                 <ul>
65536                 <li>o.record: the record object for the row the selection is in</li>
65537                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
65538                 </ul>
65539              */
65540             "selectionchange" : true,
65541         /**
65542              * @event tabend
65543              * Fires when the tab (or enter) was pressed on the last editable cell
65544              * You can use this to trigger add new row.
65545              * @param {SelectionModel} this
65546              */
65547             "tabend" : true,
65548          /**
65549              * @event beforeeditnext
65550              * Fires before the next editable sell is made active
65551              * You can use this to skip to another cell or fire the tabend
65552              *    if you set cell to false
65553              * @param {Object} eventdata object : { cell : [ row, col ] } 
65554              */
65555             "beforeeditnext" : true
65556     });
65557     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
65558 };
65559
65560 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
65561     
65562     enter_is_tab: false,
65563
65564     /** @ignore */
65565     initEvents : function(){
65566         this.grid.on("mousedown", this.handleMouseDown, this);
65567         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
65568         var view = this.grid.view;
65569         view.on("refresh", this.onViewChange, this);
65570         view.on("rowupdated", this.onRowUpdated, this);
65571         view.on("beforerowremoved", this.clearSelections, this);
65572         view.on("beforerowsinserted", this.clearSelections, this);
65573         if(this.grid.isEditor){
65574             this.grid.on("beforeedit", this.beforeEdit,  this);
65575         }
65576     },
65577
65578         //private
65579     beforeEdit : function(e){
65580         this.select(e.row, e.column, false, true, e.record);
65581     },
65582
65583         //private
65584     onRowUpdated : function(v, index, r){
65585         if(this.selection && this.selection.record == r){
65586             v.onCellSelect(index, this.selection.cell[1]);
65587         }
65588     },
65589
65590         //private
65591     onViewChange : function(){
65592         this.clearSelections(true);
65593     },
65594
65595         /**
65596          * Returns the currently selected cell,.
65597          * @return {Array} The selected cell (row, column) or null if none selected.
65598          */
65599     getSelectedCell : function(){
65600         return this.selection ? this.selection.cell : null;
65601     },
65602
65603     /**
65604      * Clears all selections.
65605      * @param {Boolean} true to prevent the gridview from being notified about the change.
65606      */
65607     clearSelections : function(preventNotify){
65608         var s = this.selection;
65609         if(s){
65610             if(preventNotify !== true){
65611                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
65612             }
65613             this.selection = null;
65614             this.fireEvent("selectionchange", this, null);
65615         }
65616     },
65617
65618     /**
65619      * Returns true if there is a selection.
65620      * @return {Boolean}
65621      */
65622     hasSelection : function(){
65623         return this.selection ? true : false;
65624     },
65625
65626     /** @ignore */
65627     handleMouseDown : function(e, t){
65628         var v = this.grid.getView();
65629         if(this.isLocked()){
65630             return;
65631         };
65632         var row = v.findRowIndex(t);
65633         var cell = v.findCellIndex(t);
65634         if(row !== false && cell !== false){
65635             this.select(row, cell);
65636         }
65637     },
65638
65639     /**
65640      * Selects a cell.
65641      * @param {Number} rowIndex
65642      * @param {Number} collIndex
65643      */
65644     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
65645         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
65646             this.clearSelections();
65647             r = r || this.grid.dataSource.getAt(rowIndex);
65648             this.selection = {
65649                 record : r,
65650                 cell : [rowIndex, colIndex]
65651             };
65652             if(!preventViewNotify){
65653                 var v = this.grid.getView();
65654                 v.onCellSelect(rowIndex, colIndex);
65655                 if(preventFocus !== true){
65656                     v.focusCell(rowIndex, colIndex);
65657                 }
65658             }
65659             this.fireEvent("cellselect", this, rowIndex, colIndex);
65660             this.fireEvent("selectionchange", this, this.selection);
65661         }
65662     },
65663
65664         //private
65665     isSelectable : function(rowIndex, colIndex, cm){
65666         return !cm.isHidden(colIndex);
65667     },
65668
65669     /** @ignore */
65670     handleKeyDown : function(e){
65671         //Roo.log('Cell Sel Model handleKeyDown');
65672         if(!e.isNavKeyPress()){
65673             return;
65674         }
65675         var g = this.grid, s = this.selection;
65676         if(!s){
65677             e.stopEvent();
65678             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
65679             if(cell){
65680                 this.select(cell[0], cell[1]);
65681             }
65682             return;
65683         }
65684         var sm = this;
65685         var walk = function(row, col, step){
65686             return g.walkCells(row, col, step, sm.isSelectable,  sm);
65687         };
65688         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
65689         var newCell;
65690
65691       
65692
65693         switch(k){
65694             case e.TAB:
65695                 // handled by onEditorKey
65696                 if (g.isEditor && g.editing) {
65697                     return;
65698                 }
65699                 if(e.shiftKey) {
65700                     newCell = walk(r, c-1, -1);
65701                 } else {
65702                     newCell = walk(r, c+1, 1);
65703                 }
65704                 break;
65705             
65706             case e.DOWN:
65707                newCell = walk(r+1, c, 1);
65708                 break;
65709             
65710             case e.UP:
65711                 newCell = walk(r-1, c, -1);
65712                 break;
65713             
65714             case e.RIGHT:
65715                 newCell = walk(r, c+1, 1);
65716                 break;
65717             
65718             case e.LEFT:
65719                 newCell = walk(r, c-1, -1);
65720                 break;
65721             
65722             case e.ENTER:
65723                 
65724                 if(g.isEditor && !g.editing){
65725                    g.startEditing(r, c);
65726                    e.stopEvent();
65727                    return;
65728                 }
65729                 
65730                 
65731              break;
65732         };
65733         if(newCell){
65734             this.select(newCell[0], newCell[1]);
65735             e.stopEvent();
65736             
65737         }
65738     },
65739
65740     acceptsNav : function(row, col, cm){
65741         return !cm.isHidden(col) && cm.isCellEditable(col, row);
65742     },
65743     /**
65744      * Selects a cell.
65745      * @param {Number} field (not used) - as it's normally used as a listener
65746      * @param {Number} e - event - fake it by using
65747      *
65748      * var e = Roo.EventObjectImpl.prototype;
65749      * e.keyCode = e.TAB
65750      *
65751      * 
65752      */
65753     onEditorKey : function(field, e){
65754         
65755         var k = e.getKey(),
65756             newCell,
65757             g = this.grid,
65758             ed = g.activeEditor,
65759             forward = false;
65760         ///Roo.log('onEditorKey' + k);
65761         
65762         
65763         if (this.enter_is_tab && k == e.ENTER) {
65764             k = e.TAB;
65765         }
65766         
65767         if(k == e.TAB){
65768             if(e.shiftKey){
65769                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65770             }else{
65771                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65772                 forward = true;
65773             }
65774             
65775             e.stopEvent();
65776             
65777         } else if(k == e.ENTER &&  !e.ctrlKey){
65778             ed.completeEdit();
65779             e.stopEvent();
65780             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65781         
65782                 } else if(k == e.ESC){
65783             ed.cancelEdit();
65784         }
65785                 
65786         if (newCell) {
65787             var ecall = { cell : newCell, forward : forward };
65788             this.fireEvent('beforeeditnext', ecall );
65789             newCell = ecall.cell;
65790                         forward = ecall.forward;
65791         }
65792                 
65793         if(newCell){
65794             //Roo.log('next cell after edit');
65795             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
65796         } else if (forward) {
65797             // tabbed past last
65798             this.fireEvent.defer(100, this, ['tabend',this]);
65799         }
65800     }
65801 });/*
65802  * Based on:
65803  * Ext JS Library 1.1.1
65804  * Copyright(c) 2006-2007, Ext JS, LLC.
65805  *
65806  * Originally Released Under LGPL - original licence link has changed is not relivant.
65807  *
65808  * Fork - LGPL
65809  * <script type="text/javascript">
65810  */
65811  
65812 /**
65813  * @class Roo.grid.EditorGrid
65814  * @extends Roo.grid.Grid
65815  * Class for creating and editable grid.
65816  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
65817  * The container MUST have some type of size defined for the grid to fill. The container will be 
65818  * automatically set to position relative if it isn't already.
65819  * @param {Object} dataSource The data model to bind to
65820  * @param {Object} colModel The column model with info about this grid's columns
65821  */
65822 Roo.grid.EditorGrid = function(container, config){
65823     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
65824     this.getGridEl().addClass("xedit-grid");
65825
65826     if(!this.selModel){
65827         this.selModel = new Roo.grid.CellSelectionModel();
65828     }
65829
65830     this.activeEditor = null;
65831
65832         this.addEvents({
65833             /**
65834              * @event beforeedit
65835              * Fires before cell editing is triggered. The edit event object has the following properties <br />
65836              * <ul style="padding:5px;padding-left:16px;">
65837              * <li>grid - This grid</li>
65838              * <li>record - The record being edited</li>
65839              * <li>field - The field name being edited</li>
65840              * <li>value - The value for the field being edited.</li>
65841              * <li>row - The grid row index</li>
65842              * <li>column - The grid column index</li>
65843              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65844              * </ul>
65845              * @param {Object} e An edit event (see above for description)
65846              */
65847             "beforeedit" : true,
65848             /**
65849              * @event afteredit
65850              * Fires after a cell is edited. <br />
65851              * <ul style="padding:5px;padding-left:16px;">
65852              * <li>grid - This grid</li>
65853              * <li>record - The record being edited</li>
65854              * <li>field - The field name being edited</li>
65855              * <li>value - The value being set</li>
65856              * <li>originalValue - The original value for the field, before the edit.</li>
65857              * <li>row - The grid row index</li>
65858              * <li>column - The grid column index</li>
65859              * </ul>
65860              * @param {Object} e An edit event (see above for description)
65861              */
65862             "afteredit" : true,
65863             /**
65864              * @event validateedit
65865              * Fires after a cell is edited, but before the value is set in the record. 
65866          * You can use this to modify the value being set in the field, Return false
65867              * to cancel the change. The edit event object has the following properties <br />
65868              * <ul style="padding:5px;padding-left:16px;">
65869          * <li>editor - This editor</li>
65870              * <li>grid - This grid</li>
65871              * <li>record - The record being edited</li>
65872              * <li>field - The field name being edited</li>
65873              * <li>value - The value being set</li>
65874              * <li>originalValue - The original value for the field, before the edit.</li>
65875              * <li>row - The grid row index</li>
65876              * <li>column - The grid column index</li>
65877              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65878              * </ul>
65879              * @param {Object} e An edit event (see above for description)
65880              */
65881             "validateedit" : true
65882         });
65883     this.on("bodyscroll", this.stopEditing,  this);
65884     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
65885 };
65886
65887 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
65888     /**
65889      * @cfg {Number} clicksToEdit
65890      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
65891      */
65892     clicksToEdit: 2,
65893
65894     // private
65895     isEditor : true,
65896     // private
65897     trackMouseOver: false, // causes very odd FF errors
65898
65899     onCellDblClick : function(g, row, col){
65900         this.startEditing(row, col);
65901     },
65902
65903     onEditComplete : function(ed, value, startValue){
65904         this.editing = false;
65905         this.activeEditor = null;
65906         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
65907         var r = ed.record;
65908         var field = this.colModel.getDataIndex(ed.col);
65909         var e = {
65910             grid: this,
65911             record: r,
65912             field: field,
65913             originalValue: startValue,
65914             value: value,
65915             row: ed.row,
65916             column: ed.col,
65917             cancel:false,
65918             editor: ed
65919         };
65920         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
65921         cell.show();
65922           
65923         if(String(value) !== String(startValue)){
65924             
65925             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
65926                 r.set(field, e.value);
65927                 // if we are dealing with a combo box..
65928                 // then we also set the 'name' colum to be the displayField
65929                 if (ed.field.displayField && ed.field.name) {
65930                     r.set(ed.field.name, ed.field.el.dom.value);
65931                 }
65932                 
65933                 delete e.cancel; //?? why!!!
65934                 this.fireEvent("afteredit", e);
65935             }
65936         } else {
65937             this.fireEvent("afteredit", e); // always fire it!
65938         }
65939         this.view.focusCell(ed.row, ed.col);
65940     },
65941
65942     /**
65943      * Starts editing the specified for the specified row/column
65944      * @param {Number} rowIndex
65945      * @param {Number} colIndex
65946      */
65947     startEditing : function(row, col){
65948         this.stopEditing();
65949         if(this.colModel.isCellEditable(col, row)){
65950             this.view.ensureVisible(row, col, true);
65951           
65952             var r = this.dataSource.getAt(row);
65953             var field = this.colModel.getDataIndex(col);
65954             var cell = Roo.get(this.view.getCell(row,col));
65955             var e = {
65956                 grid: this,
65957                 record: r,
65958                 field: field,
65959                 value: r.data[field],
65960                 row: row,
65961                 column: col,
65962                 cancel:false 
65963             };
65964             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
65965                 this.editing = true;
65966                 var ed = this.colModel.getCellEditor(col, row);
65967                 
65968                 if (!ed) {
65969                     return;
65970                 }
65971                 if(!ed.rendered){
65972                     ed.render(ed.parentEl || document.body);
65973                 }
65974                 ed.field.reset();
65975                
65976                 cell.hide();
65977                 
65978                 (function(){ // complex but required for focus issues in safari, ie and opera
65979                     ed.row = row;
65980                     ed.col = col;
65981                     ed.record = r;
65982                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
65983                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
65984                     this.activeEditor = ed;
65985                     var v = r.data[field];
65986                     ed.startEdit(this.view.getCell(row, col), v);
65987                     // combo's with 'displayField and name set
65988                     if (ed.field.displayField && ed.field.name) {
65989                         ed.field.el.dom.value = r.data[ed.field.name];
65990                     }
65991                     
65992                     
65993                 }).defer(50, this);
65994             }
65995         }
65996     },
65997         
65998     /**
65999      * Stops any active editing
66000      */
66001     stopEditing : function(){
66002         if(this.activeEditor){
66003             this.activeEditor.completeEdit();
66004         }
66005         this.activeEditor = null;
66006     },
66007         
66008          /**
66009      * Called to get grid's drag proxy text, by default returns this.ddText.
66010      * @return {String}
66011      */
66012     getDragDropText : function(){
66013         var count = this.selModel.getSelectedCell() ? 1 : 0;
66014         return String.format(this.ddText, count, count == 1 ? '' : 's');
66015     }
66016         
66017 });/*
66018  * Based on:
66019  * Ext JS Library 1.1.1
66020  * Copyright(c) 2006-2007, Ext JS, LLC.
66021  *
66022  * Originally Released Under LGPL - original licence link has changed is not relivant.
66023  *
66024  * Fork - LGPL
66025  * <script type="text/javascript">
66026  */
66027
66028 // private - not really -- you end up using it !
66029 // This is a support class used internally by the Grid components
66030
66031 /**
66032  * @class Roo.grid.GridEditor
66033  * @extends Roo.Editor
66034  * Class for creating and editable grid elements.
66035  * @param {Object} config any settings (must include field)
66036  */
66037 Roo.grid.GridEditor = function(field, config){
66038     if (!config && field.field) {
66039         config = field;
66040         field = Roo.factory(config.field, Roo.form);
66041     }
66042     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
66043     field.monitorTab = false;
66044 };
66045
66046 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
66047     
66048     /**
66049      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
66050      */
66051     
66052     alignment: "tl-tl",
66053     autoSize: "width",
66054     hideEl : false,
66055     cls: "x-small-editor x-grid-editor",
66056     shim:false,
66057     shadow:"frame"
66058 });/*
66059  * Based on:
66060  * Ext JS Library 1.1.1
66061  * Copyright(c) 2006-2007, Ext JS, LLC.
66062  *
66063  * Originally Released Under LGPL - original licence link has changed is not relivant.
66064  *
66065  * Fork - LGPL
66066  * <script type="text/javascript">
66067  */
66068   
66069
66070   
66071 Roo.grid.PropertyRecord = Roo.data.Record.create([
66072     {name:'name',type:'string'},  'value'
66073 ]);
66074
66075
66076 Roo.grid.PropertyStore = function(grid, source){
66077     this.grid = grid;
66078     this.store = new Roo.data.Store({
66079         recordType : Roo.grid.PropertyRecord
66080     });
66081     this.store.on('update', this.onUpdate,  this);
66082     if(source){
66083         this.setSource(source);
66084     }
66085     Roo.grid.PropertyStore.superclass.constructor.call(this);
66086 };
66087
66088
66089
66090 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
66091     setSource : function(o){
66092         this.source = o;
66093         this.store.removeAll();
66094         var data = [];
66095         for(var k in o){
66096             if(this.isEditableValue(o[k])){
66097                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
66098             }
66099         }
66100         this.store.loadRecords({records: data}, {}, true);
66101     },
66102
66103     onUpdate : function(ds, record, type){
66104         if(type == Roo.data.Record.EDIT){
66105             var v = record.data['value'];
66106             var oldValue = record.modified['value'];
66107             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
66108                 this.source[record.id] = v;
66109                 record.commit();
66110                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
66111             }else{
66112                 record.reject();
66113             }
66114         }
66115     },
66116
66117     getProperty : function(row){
66118        return this.store.getAt(row);
66119     },
66120
66121     isEditableValue: function(val){
66122         if(val && val instanceof Date){
66123             return true;
66124         }else if(typeof val == 'object' || typeof val == 'function'){
66125             return false;
66126         }
66127         return true;
66128     },
66129
66130     setValue : function(prop, value){
66131         this.source[prop] = value;
66132         this.store.getById(prop).set('value', value);
66133     },
66134
66135     getSource : function(){
66136         return this.source;
66137     }
66138 });
66139
66140 Roo.grid.PropertyColumnModel = function(grid, store){
66141     this.grid = grid;
66142     var g = Roo.grid;
66143     g.PropertyColumnModel.superclass.constructor.call(this, [
66144         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
66145         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
66146     ]);
66147     this.store = store;
66148     this.bselect = Roo.DomHelper.append(document.body, {
66149         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
66150             {tag: 'option', value: 'true', html: 'true'},
66151             {tag: 'option', value: 'false', html: 'false'}
66152         ]
66153     });
66154     Roo.id(this.bselect);
66155     var f = Roo.form;
66156     this.editors = {
66157         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
66158         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
66159         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
66160         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
66161         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
66162     };
66163     this.renderCellDelegate = this.renderCell.createDelegate(this);
66164     this.renderPropDelegate = this.renderProp.createDelegate(this);
66165 };
66166
66167 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
66168     
66169     
66170     nameText : 'Name',
66171     valueText : 'Value',
66172     
66173     dateFormat : 'm/j/Y',
66174     
66175     
66176     renderDate : function(dateVal){
66177         return dateVal.dateFormat(this.dateFormat);
66178     },
66179
66180     renderBool : function(bVal){
66181         return bVal ? 'true' : 'false';
66182     },
66183
66184     isCellEditable : function(colIndex, rowIndex){
66185         return colIndex == 1;
66186     },
66187
66188     getRenderer : function(col){
66189         return col == 1 ?
66190             this.renderCellDelegate : this.renderPropDelegate;
66191     },
66192
66193     renderProp : function(v){
66194         return this.getPropertyName(v);
66195     },
66196
66197     renderCell : function(val){
66198         var rv = val;
66199         if(val instanceof Date){
66200             rv = this.renderDate(val);
66201         }else if(typeof val == 'boolean'){
66202             rv = this.renderBool(val);
66203         }
66204         return Roo.util.Format.htmlEncode(rv);
66205     },
66206
66207     getPropertyName : function(name){
66208         var pn = this.grid.propertyNames;
66209         return pn && pn[name] ? pn[name] : name;
66210     },
66211
66212     getCellEditor : function(colIndex, rowIndex){
66213         var p = this.store.getProperty(rowIndex);
66214         var n = p.data['name'], val = p.data['value'];
66215         
66216         if(typeof(this.grid.customEditors[n]) == 'string'){
66217             return this.editors[this.grid.customEditors[n]];
66218         }
66219         if(typeof(this.grid.customEditors[n]) != 'undefined'){
66220             return this.grid.customEditors[n];
66221         }
66222         if(val instanceof Date){
66223             return this.editors['date'];
66224         }else if(typeof val == 'number'){
66225             return this.editors['number'];
66226         }else if(typeof val == 'boolean'){
66227             return this.editors['boolean'];
66228         }else{
66229             return this.editors['string'];
66230         }
66231     }
66232 });
66233
66234 /**
66235  * @class Roo.grid.PropertyGrid
66236  * @extends Roo.grid.EditorGrid
66237  * This class represents the  interface of a component based property grid control.
66238  * <br><br>Usage:<pre><code>
66239  var grid = new Roo.grid.PropertyGrid("my-container-id", {
66240       
66241  });
66242  // set any options
66243  grid.render();
66244  * </code></pre>
66245   
66246  * @constructor
66247  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66248  * The container MUST have some type of size defined for the grid to fill. The container will be
66249  * automatically set to position relative if it isn't already.
66250  * @param {Object} config A config object that sets properties on this grid.
66251  */
66252 Roo.grid.PropertyGrid = function(container, config){
66253     config = config || {};
66254     var store = new Roo.grid.PropertyStore(this);
66255     this.store = store;
66256     var cm = new Roo.grid.PropertyColumnModel(this, store);
66257     store.store.sort('name', 'ASC');
66258     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
66259         ds: store.store,
66260         cm: cm,
66261         enableColLock:false,
66262         enableColumnMove:false,
66263         stripeRows:false,
66264         trackMouseOver: false,
66265         clicksToEdit:1
66266     }, config));
66267     this.getGridEl().addClass('x-props-grid');
66268     this.lastEditRow = null;
66269     this.on('columnresize', this.onColumnResize, this);
66270     this.addEvents({
66271          /**
66272              * @event beforepropertychange
66273              * Fires before a property changes (return false to stop?)
66274              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66275              * @param {String} id Record Id
66276              * @param {String} newval New Value
66277          * @param {String} oldval Old Value
66278              */
66279         "beforepropertychange": true,
66280         /**
66281              * @event propertychange
66282              * Fires after a property changes
66283              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66284              * @param {String} id Record Id
66285              * @param {String} newval New Value
66286          * @param {String} oldval Old Value
66287              */
66288         "propertychange": true
66289     });
66290     this.customEditors = this.customEditors || {};
66291 };
66292 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
66293     
66294      /**
66295      * @cfg {Object} customEditors map of colnames=> custom editors.
66296      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
66297      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
66298      * false disables editing of the field.
66299          */
66300     
66301       /**
66302      * @cfg {Object} propertyNames map of property Names to their displayed value
66303          */
66304     
66305     render : function(){
66306         Roo.grid.PropertyGrid.superclass.render.call(this);
66307         this.autoSize.defer(100, this);
66308     },
66309
66310     autoSize : function(){
66311         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
66312         if(this.view){
66313             this.view.fitColumns();
66314         }
66315     },
66316
66317     onColumnResize : function(){
66318         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
66319         this.autoSize();
66320     },
66321     /**
66322      * Sets the data for the Grid
66323      * accepts a Key => Value object of all the elements avaiable.
66324      * @param {Object} data  to appear in grid.
66325      */
66326     setSource : function(source){
66327         this.store.setSource(source);
66328         //this.autoSize();
66329     },
66330     /**
66331      * Gets all the data from the grid.
66332      * @return {Object} data  data stored in grid
66333      */
66334     getSource : function(){
66335         return this.store.getSource();
66336     }
66337 });/*
66338   
66339  * Licence LGPL
66340  
66341  */
66342  
66343 /**
66344  * @class Roo.grid.Calendar
66345  * @extends Roo.grid.Grid
66346  * This class extends the Grid to provide a calendar widget
66347  * <br><br>Usage:<pre><code>
66348  var grid = new Roo.grid.Calendar("my-container-id", {
66349      ds: myDataStore,
66350      cm: myColModel,
66351      selModel: mySelectionModel,
66352      autoSizeColumns: true,
66353      monitorWindowResize: false,
66354      trackMouseOver: true
66355      eventstore : real data store..
66356  });
66357  // set any options
66358  grid.render();
66359   
66360   * @constructor
66361  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66362  * The container MUST have some type of size defined for the grid to fill. The container will be
66363  * automatically set to position relative if it isn't already.
66364  * @param {Object} config A config object that sets properties on this grid.
66365  */
66366 Roo.grid.Calendar = function(container, config){
66367         // initialize the container
66368         this.container = Roo.get(container);
66369         this.container.update("");
66370         this.container.setStyle("overflow", "hidden");
66371     this.container.addClass('x-grid-container');
66372
66373     this.id = this.container.id;
66374
66375     Roo.apply(this, config);
66376     // check and correct shorthanded configs
66377     
66378     var rows = [];
66379     var d =1;
66380     for (var r = 0;r < 6;r++) {
66381         
66382         rows[r]=[];
66383         for (var c =0;c < 7;c++) {
66384             rows[r][c]= '';
66385         }
66386     }
66387     if (this.eventStore) {
66388         this.eventStore= Roo.factory(this.eventStore, Roo.data);
66389         this.eventStore.on('load',this.onLoad, this);
66390         this.eventStore.on('beforeload',this.clearEvents, this);
66391          
66392     }
66393     
66394     this.dataSource = new Roo.data.Store({
66395             proxy: new Roo.data.MemoryProxy(rows),
66396             reader: new Roo.data.ArrayReader({}, [
66397                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
66398     });
66399
66400     this.dataSource.load();
66401     this.ds = this.dataSource;
66402     this.ds.xmodule = this.xmodule || false;
66403     
66404     
66405     var cellRender = function(v,x,r)
66406     {
66407         return String.format(
66408             '<div class="fc-day  fc-widget-content"><div>' +
66409                 '<div class="fc-event-container"></div>' +
66410                 '<div class="fc-day-number">{0}</div>'+
66411                 
66412                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
66413             '</div></div>', v);
66414     
66415     }
66416     
66417     
66418     this.colModel = new Roo.grid.ColumnModel( [
66419         {
66420             xtype: 'ColumnModel',
66421             xns: Roo.grid,
66422             dataIndex : 'weekday0',
66423             header : 'Sunday',
66424             renderer : cellRender
66425         },
66426         {
66427             xtype: 'ColumnModel',
66428             xns: Roo.grid,
66429             dataIndex : 'weekday1',
66430             header : 'Monday',
66431             renderer : cellRender
66432         },
66433         {
66434             xtype: 'ColumnModel',
66435             xns: Roo.grid,
66436             dataIndex : 'weekday2',
66437             header : 'Tuesday',
66438             renderer : cellRender
66439         },
66440         {
66441             xtype: 'ColumnModel',
66442             xns: Roo.grid,
66443             dataIndex : 'weekday3',
66444             header : 'Wednesday',
66445             renderer : cellRender
66446         },
66447         {
66448             xtype: 'ColumnModel',
66449             xns: Roo.grid,
66450             dataIndex : 'weekday4',
66451             header : 'Thursday',
66452             renderer : cellRender
66453         },
66454         {
66455             xtype: 'ColumnModel',
66456             xns: Roo.grid,
66457             dataIndex : 'weekday5',
66458             header : 'Friday',
66459             renderer : cellRender
66460         },
66461         {
66462             xtype: 'ColumnModel',
66463             xns: Roo.grid,
66464             dataIndex : 'weekday6',
66465             header : 'Saturday',
66466             renderer : cellRender
66467         }
66468     ]);
66469     this.cm = this.colModel;
66470     this.cm.xmodule = this.xmodule || false;
66471  
66472         
66473           
66474     //this.selModel = new Roo.grid.CellSelectionModel();
66475     //this.sm = this.selModel;
66476     //this.selModel.init(this);
66477     
66478     
66479     if(this.width){
66480         this.container.setWidth(this.width);
66481     }
66482
66483     if(this.height){
66484         this.container.setHeight(this.height);
66485     }
66486     /** @private */
66487         this.addEvents({
66488         // raw events
66489         /**
66490          * @event click
66491          * The raw click event for the entire grid.
66492          * @param {Roo.EventObject} e
66493          */
66494         "click" : true,
66495         /**
66496          * @event dblclick
66497          * The raw dblclick event for the entire grid.
66498          * @param {Roo.EventObject} e
66499          */
66500         "dblclick" : true,
66501         /**
66502          * @event contextmenu
66503          * The raw contextmenu event for the entire grid.
66504          * @param {Roo.EventObject} e
66505          */
66506         "contextmenu" : true,
66507         /**
66508          * @event mousedown
66509          * The raw mousedown event for the entire grid.
66510          * @param {Roo.EventObject} e
66511          */
66512         "mousedown" : true,
66513         /**
66514          * @event mouseup
66515          * The raw mouseup event for the entire grid.
66516          * @param {Roo.EventObject} e
66517          */
66518         "mouseup" : true,
66519         /**
66520          * @event mouseover
66521          * The raw mouseover event for the entire grid.
66522          * @param {Roo.EventObject} e
66523          */
66524         "mouseover" : true,
66525         /**
66526          * @event mouseout
66527          * The raw mouseout event for the entire grid.
66528          * @param {Roo.EventObject} e
66529          */
66530         "mouseout" : true,
66531         /**
66532          * @event keypress
66533          * The raw keypress event for the entire grid.
66534          * @param {Roo.EventObject} e
66535          */
66536         "keypress" : true,
66537         /**
66538          * @event keydown
66539          * The raw keydown event for the entire grid.
66540          * @param {Roo.EventObject} e
66541          */
66542         "keydown" : true,
66543
66544         // custom events
66545
66546         /**
66547          * @event cellclick
66548          * Fires when a cell is clicked
66549          * @param {Grid} this
66550          * @param {Number} rowIndex
66551          * @param {Number} columnIndex
66552          * @param {Roo.EventObject} e
66553          */
66554         "cellclick" : true,
66555         /**
66556          * @event celldblclick
66557          * Fires when a cell is double clicked
66558          * @param {Grid} this
66559          * @param {Number} rowIndex
66560          * @param {Number} columnIndex
66561          * @param {Roo.EventObject} e
66562          */
66563         "celldblclick" : true,
66564         /**
66565          * @event rowclick
66566          * Fires when a row is clicked
66567          * @param {Grid} this
66568          * @param {Number} rowIndex
66569          * @param {Roo.EventObject} e
66570          */
66571         "rowclick" : true,
66572         /**
66573          * @event rowdblclick
66574          * Fires when a row is double clicked
66575          * @param {Grid} this
66576          * @param {Number} rowIndex
66577          * @param {Roo.EventObject} e
66578          */
66579         "rowdblclick" : true,
66580         /**
66581          * @event headerclick
66582          * Fires when a header is clicked
66583          * @param {Grid} this
66584          * @param {Number} columnIndex
66585          * @param {Roo.EventObject} e
66586          */
66587         "headerclick" : true,
66588         /**
66589          * @event headerdblclick
66590          * Fires when a header cell is double clicked
66591          * @param {Grid} this
66592          * @param {Number} columnIndex
66593          * @param {Roo.EventObject} e
66594          */
66595         "headerdblclick" : true,
66596         /**
66597          * @event rowcontextmenu
66598          * Fires when a row is right clicked
66599          * @param {Grid} this
66600          * @param {Number} rowIndex
66601          * @param {Roo.EventObject} e
66602          */
66603         "rowcontextmenu" : true,
66604         /**
66605          * @event cellcontextmenu
66606          * Fires when a cell is right clicked
66607          * @param {Grid} this
66608          * @param {Number} rowIndex
66609          * @param {Number} cellIndex
66610          * @param {Roo.EventObject} e
66611          */
66612          "cellcontextmenu" : true,
66613         /**
66614          * @event headercontextmenu
66615          * Fires when a header is right clicked
66616          * @param {Grid} this
66617          * @param {Number} columnIndex
66618          * @param {Roo.EventObject} e
66619          */
66620         "headercontextmenu" : true,
66621         /**
66622          * @event bodyscroll
66623          * Fires when the body element is scrolled
66624          * @param {Number} scrollLeft
66625          * @param {Number} scrollTop
66626          */
66627         "bodyscroll" : true,
66628         /**
66629          * @event columnresize
66630          * Fires when the user resizes a column
66631          * @param {Number} columnIndex
66632          * @param {Number} newSize
66633          */
66634         "columnresize" : true,
66635         /**
66636          * @event columnmove
66637          * Fires when the user moves a column
66638          * @param {Number} oldIndex
66639          * @param {Number} newIndex
66640          */
66641         "columnmove" : true,
66642         /**
66643          * @event startdrag
66644          * Fires when row(s) start being dragged
66645          * @param {Grid} this
66646          * @param {Roo.GridDD} dd The drag drop object
66647          * @param {event} e The raw browser event
66648          */
66649         "startdrag" : true,
66650         /**
66651          * @event enddrag
66652          * Fires when a drag operation is complete
66653          * @param {Grid} this
66654          * @param {Roo.GridDD} dd The drag drop object
66655          * @param {event} e The raw browser event
66656          */
66657         "enddrag" : true,
66658         /**
66659          * @event dragdrop
66660          * Fires when dragged row(s) are dropped on a valid DD target
66661          * @param {Grid} this
66662          * @param {Roo.GridDD} dd The drag drop object
66663          * @param {String} targetId The target drag drop object
66664          * @param {event} e The raw browser event
66665          */
66666         "dragdrop" : true,
66667         /**
66668          * @event dragover
66669          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
66670          * @param {Grid} this
66671          * @param {Roo.GridDD} dd The drag drop object
66672          * @param {String} targetId The target drag drop object
66673          * @param {event} e The raw browser event
66674          */
66675         "dragover" : true,
66676         /**
66677          * @event dragenter
66678          *  Fires when the dragged row(s) first cross another DD target while being dragged
66679          * @param {Grid} this
66680          * @param {Roo.GridDD} dd The drag drop object
66681          * @param {String} targetId The target drag drop object
66682          * @param {event} e The raw browser event
66683          */
66684         "dragenter" : true,
66685         /**
66686          * @event dragout
66687          * Fires when the dragged row(s) leave another DD target while being dragged
66688          * @param {Grid} this
66689          * @param {Roo.GridDD} dd The drag drop object
66690          * @param {String} targetId The target drag drop object
66691          * @param {event} e The raw browser event
66692          */
66693         "dragout" : true,
66694         /**
66695          * @event rowclass
66696          * Fires when a row is rendered, so you can change add a style to it.
66697          * @param {GridView} gridview   The grid view
66698          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
66699          */
66700         'rowclass' : true,
66701
66702         /**
66703          * @event render
66704          * Fires when the grid is rendered
66705          * @param {Grid} grid
66706          */
66707         'render' : true,
66708             /**
66709              * @event select
66710              * Fires when a date is selected
66711              * @param {DatePicker} this
66712              * @param {Date} date The selected date
66713              */
66714         'select': true,
66715         /**
66716              * @event monthchange
66717              * Fires when the displayed month changes 
66718              * @param {DatePicker} this
66719              * @param {Date} date The selected month
66720              */
66721         'monthchange': true,
66722         /**
66723              * @event evententer
66724              * Fires when mouse over an event
66725              * @param {Calendar} this
66726              * @param {event} Event
66727              */
66728         'evententer': true,
66729         /**
66730              * @event eventleave
66731              * Fires when the mouse leaves an
66732              * @param {Calendar} this
66733              * @param {event}
66734              */
66735         'eventleave': true,
66736         /**
66737              * @event eventclick
66738              * Fires when the mouse click an
66739              * @param {Calendar} this
66740              * @param {event}
66741              */
66742         'eventclick': true,
66743         /**
66744              * @event eventrender
66745              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
66746              * @param {Calendar} this
66747              * @param {data} data to be modified
66748              */
66749         'eventrender': true
66750         
66751     });
66752
66753     Roo.grid.Grid.superclass.constructor.call(this);
66754     this.on('render', function() {
66755         this.view.el.addClass('x-grid-cal'); 
66756         
66757         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
66758
66759     },this);
66760     
66761     if (!Roo.grid.Calendar.style) {
66762         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
66763             
66764             
66765             '.x-grid-cal .x-grid-col' :  {
66766                 height: 'auto !important',
66767                 'vertical-align': 'top'
66768             },
66769             '.x-grid-cal  .fc-event-hori' : {
66770                 height: '14px'
66771             }
66772              
66773             
66774         }, Roo.id());
66775     }
66776
66777     
66778     
66779 };
66780 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
66781     /**
66782      * @cfg {Store} eventStore The store that loads events.
66783      */
66784     eventStore : 25,
66785
66786      
66787     activeDate : false,
66788     startDay : 0,
66789     autoWidth : true,
66790     monitorWindowResize : false,
66791
66792     
66793     resizeColumns : function() {
66794         var col = (this.view.el.getWidth() / 7) - 3;
66795         // loop through cols, and setWidth
66796         for(var i =0 ; i < 7 ; i++){
66797             this.cm.setColumnWidth(i, col);
66798         }
66799     },
66800      setDate :function(date) {
66801         
66802         Roo.log('setDate?');
66803         
66804         this.resizeColumns();
66805         var vd = this.activeDate;
66806         this.activeDate = date;
66807 //        if(vd && this.el){
66808 //            var t = date.getTime();
66809 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
66810 //                Roo.log('using add remove');
66811 //                
66812 //                this.fireEvent('monthchange', this, date);
66813 //                
66814 //                this.cells.removeClass("fc-state-highlight");
66815 //                this.cells.each(function(c){
66816 //                   if(c.dateValue == t){
66817 //                       c.addClass("fc-state-highlight");
66818 //                       setTimeout(function(){
66819 //                            try{c.dom.firstChild.focus();}catch(e){}
66820 //                       }, 50);
66821 //                       return false;
66822 //                   }
66823 //                   return true;
66824 //                });
66825 //                return;
66826 //            }
66827 //        }
66828         
66829         var days = date.getDaysInMonth();
66830         
66831         var firstOfMonth = date.getFirstDateOfMonth();
66832         var startingPos = firstOfMonth.getDay()-this.startDay;
66833         
66834         if(startingPos < this.startDay){
66835             startingPos += 7;
66836         }
66837         
66838         var pm = date.add(Date.MONTH, -1);
66839         var prevStart = pm.getDaysInMonth()-startingPos;
66840 //        
66841         
66842         
66843         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66844         
66845         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
66846         //this.cells.addClassOnOver('fc-state-hover');
66847         
66848         var cells = this.cells.elements;
66849         var textEls = this.textNodes;
66850         
66851         //Roo.each(cells, function(cell){
66852         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
66853         //});
66854         
66855         days += startingPos;
66856
66857         // convert everything to numbers so it's fast
66858         var day = 86400000;
66859         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
66860         //Roo.log(d);
66861         //Roo.log(pm);
66862         //Roo.log(prevStart);
66863         
66864         var today = new Date().clearTime().getTime();
66865         var sel = date.clearTime().getTime();
66866         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
66867         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
66868         var ddMatch = this.disabledDatesRE;
66869         var ddText = this.disabledDatesText;
66870         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
66871         var ddaysText = this.disabledDaysText;
66872         var format = this.format;
66873         
66874         var setCellClass = function(cal, cell){
66875             
66876             //Roo.log('set Cell Class');
66877             cell.title = "";
66878             var t = d.getTime();
66879             
66880             //Roo.log(d);
66881             
66882             
66883             cell.dateValue = t;
66884             if(t == today){
66885                 cell.className += " fc-today";
66886                 cell.className += " fc-state-highlight";
66887                 cell.title = cal.todayText;
66888             }
66889             if(t == sel){
66890                 // disable highlight in other month..
66891                 cell.className += " fc-state-highlight";
66892                 
66893             }
66894             // disabling
66895             if(t < min) {
66896                 //cell.className = " fc-state-disabled";
66897                 cell.title = cal.minText;
66898                 return;
66899             }
66900             if(t > max) {
66901                 //cell.className = " fc-state-disabled";
66902                 cell.title = cal.maxText;
66903                 return;
66904             }
66905             if(ddays){
66906                 if(ddays.indexOf(d.getDay()) != -1){
66907                     // cell.title = ddaysText;
66908                    // cell.className = " fc-state-disabled";
66909                 }
66910             }
66911             if(ddMatch && format){
66912                 var fvalue = d.dateFormat(format);
66913                 if(ddMatch.test(fvalue)){
66914                     cell.title = ddText.replace("%0", fvalue);
66915                    cell.className = " fc-state-disabled";
66916                 }
66917             }
66918             
66919             if (!cell.initialClassName) {
66920                 cell.initialClassName = cell.dom.className;
66921             }
66922             
66923             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
66924         };
66925
66926         var i = 0;
66927         
66928         for(; i < startingPos; i++) {
66929             cells[i].dayName =  (++prevStart);
66930             Roo.log(textEls[i]);
66931             d.setDate(d.getDate()+1);
66932             
66933             //cells[i].className = "fc-past fc-other-month";
66934             setCellClass(this, cells[i]);
66935         }
66936         
66937         var intDay = 0;
66938         
66939         for(; i < days; i++){
66940             intDay = i - startingPos + 1;
66941             cells[i].dayName =  (intDay);
66942             d.setDate(d.getDate()+1);
66943             
66944             cells[i].className = ''; // "x-date-active";
66945             setCellClass(this, cells[i]);
66946         }
66947         var extraDays = 0;
66948         
66949         for(; i < 42; i++) {
66950             //textEls[i].innerHTML = (++extraDays);
66951             
66952             d.setDate(d.getDate()+1);
66953             cells[i].dayName = (++extraDays);
66954             cells[i].className = "fc-future fc-other-month";
66955             setCellClass(this, cells[i]);
66956         }
66957         
66958         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
66959         
66960         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
66961         
66962         // this will cause all the cells to mis
66963         var rows= [];
66964         var i =0;
66965         for (var r = 0;r < 6;r++) {
66966             for (var c =0;c < 7;c++) {
66967                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
66968             }    
66969         }
66970         
66971         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66972         for(i=0;i<cells.length;i++) {
66973             
66974             this.cells.elements[i].dayName = cells[i].dayName ;
66975             this.cells.elements[i].className = cells[i].className;
66976             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
66977             this.cells.elements[i].title = cells[i].title ;
66978             this.cells.elements[i].dateValue = cells[i].dateValue ;
66979         }
66980         
66981         
66982         
66983         
66984         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
66985         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
66986         
66987         ////if(totalRows != 6){
66988             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
66989            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
66990        // }
66991         
66992         this.fireEvent('monthchange', this, date);
66993         
66994         
66995     },
66996  /**
66997      * Returns the grid's SelectionModel.
66998      * @return {SelectionModel}
66999      */
67000     getSelectionModel : function(){
67001         if(!this.selModel){
67002             this.selModel = new Roo.grid.CellSelectionModel();
67003         }
67004         return this.selModel;
67005     },
67006
67007     load: function() {
67008         this.eventStore.load()
67009         
67010         
67011         
67012     },
67013     
67014     findCell : function(dt) {
67015         dt = dt.clearTime().getTime();
67016         var ret = false;
67017         this.cells.each(function(c){
67018             //Roo.log("check " +c.dateValue + '?=' + dt);
67019             if(c.dateValue == dt){
67020                 ret = c;
67021                 return false;
67022             }
67023             return true;
67024         });
67025         
67026         return ret;
67027     },
67028     
67029     findCells : function(rec) {
67030         var s = rec.data.start_dt.clone().clearTime().getTime();
67031        // Roo.log(s);
67032         var e= rec.data.end_dt.clone().clearTime().getTime();
67033        // Roo.log(e);
67034         var ret = [];
67035         this.cells.each(function(c){
67036              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
67037             
67038             if(c.dateValue > e){
67039                 return ;
67040             }
67041             if(c.dateValue < s){
67042                 return ;
67043             }
67044             ret.push(c);
67045         });
67046         
67047         return ret;    
67048     },
67049     
67050     findBestRow: function(cells)
67051     {
67052         var ret = 0;
67053         
67054         for (var i =0 ; i < cells.length;i++) {
67055             ret  = Math.max(cells[i].rows || 0,ret);
67056         }
67057         return ret;
67058         
67059     },
67060     
67061     
67062     addItem : function(rec)
67063     {
67064         // look for vertical location slot in
67065         var cells = this.findCells(rec);
67066         
67067         rec.row = this.findBestRow(cells);
67068         
67069         // work out the location.
67070         
67071         var crow = false;
67072         var rows = [];
67073         for(var i =0; i < cells.length; i++) {
67074             if (!crow) {
67075                 crow = {
67076                     start : cells[i],
67077                     end :  cells[i]
67078                 };
67079                 continue;
67080             }
67081             if (crow.start.getY() == cells[i].getY()) {
67082                 // on same row.
67083                 crow.end = cells[i];
67084                 continue;
67085             }
67086             // different row.
67087             rows.push(crow);
67088             crow = {
67089                 start: cells[i],
67090                 end : cells[i]
67091             };
67092             
67093         }
67094         
67095         rows.push(crow);
67096         rec.els = [];
67097         rec.rows = rows;
67098         rec.cells = cells;
67099         for (var i = 0; i < cells.length;i++) {
67100             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
67101             
67102         }
67103         
67104         
67105     },
67106     
67107     clearEvents: function() {
67108         
67109         if (!this.eventStore.getCount()) {
67110             return;
67111         }
67112         // reset number of rows in cells.
67113         Roo.each(this.cells.elements, function(c){
67114             c.rows = 0;
67115         });
67116         
67117         this.eventStore.each(function(e) {
67118             this.clearEvent(e);
67119         },this);
67120         
67121     },
67122     
67123     clearEvent : function(ev)
67124     {
67125         if (ev.els) {
67126             Roo.each(ev.els, function(el) {
67127                 el.un('mouseenter' ,this.onEventEnter, this);
67128                 el.un('mouseleave' ,this.onEventLeave, this);
67129                 el.remove();
67130             },this);
67131             ev.els = [];
67132         }
67133     },
67134     
67135     
67136     renderEvent : function(ev,ctr) {
67137         if (!ctr) {
67138              ctr = this.view.el.select('.fc-event-container',true).first();
67139         }
67140         
67141          
67142         this.clearEvent(ev);
67143             //code
67144        
67145         
67146         
67147         ev.els = [];
67148         var cells = ev.cells;
67149         var rows = ev.rows;
67150         this.fireEvent('eventrender', this, ev);
67151         
67152         for(var i =0; i < rows.length; i++) {
67153             
67154             cls = '';
67155             if (i == 0) {
67156                 cls += ' fc-event-start';
67157             }
67158             if ((i+1) == rows.length) {
67159                 cls += ' fc-event-end';
67160             }
67161             
67162             //Roo.log(ev.data);
67163             // how many rows should it span..
67164             var cg = this.eventTmpl.append(ctr,Roo.apply({
67165                 fccls : cls
67166                 
67167             }, ev.data) , true);
67168             
67169             
67170             cg.on('mouseenter' ,this.onEventEnter, this, ev);
67171             cg.on('mouseleave' ,this.onEventLeave, this, ev);
67172             cg.on('click', this.onEventClick, this, ev);
67173             
67174             ev.els.push(cg);
67175             
67176             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
67177             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
67178             //Roo.log(cg);
67179              
67180             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
67181             cg.setWidth(ebox.right - sbox.x -2);
67182         }
67183     },
67184     
67185     renderEvents: function()
67186     {   
67187         // first make sure there is enough space..
67188         
67189         if (!this.eventTmpl) {
67190             this.eventTmpl = new Roo.Template(
67191                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
67192                     '<div class="fc-event-inner">' +
67193                         '<span class="fc-event-time">{time}</span>' +
67194                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
67195                     '</div>' +
67196                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
67197                 '</div>'
67198             );
67199                 
67200         }
67201                
67202         
67203         
67204         this.cells.each(function(c) {
67205             //Roo.log(c.select('.fc-day-content div',true).first());
67206             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
67207         });
67208         
67209         var ctr = this.view.el.select('.fc-event-container',true).first();
67210         
67211         var cls;
67212         this.eventStore.each(function(ev){
67213             
67214             this.renderEvent(ev);
67215              
67216              
67217         }, this);
67218         this.view.layout();
67219         
67220     },
67221     
67222     onEventEnter: function (e, el,event,d) {
67223         this.fireEvent('evententer', this, el, event);
67224     },
67225     
67226     onEventLeave: function (e, el,event,d) {
67227         this.fireEvent('eventleave', this, el, event);
67228     },
67229     
67230     onEventClick: function (e, el,event,d) {
67231         this.fireEvent('eventclick', this, el, event);
67232     },
67233     
67234     onMonthChange: function () {
67235         this.store.load();
67236     },
67237     
67238     onLoad: function () {
67239         
67240         //Roo.log('calendar onload');
67241 //         
67242         if(this.eventStore.getCount() > 0){
67243             
67244            
67245             
67246             this.eventStore.each(function(d){
67247                 
67248                 
67249                 // FIXME..
67250                 var add =   d.data;
67251                 if (typeof(add.end_dt) == 'undefined')  {
67252                     Roo.log("Missing End time in calendar data: ");
67253                     Roo.log(d);
67254                     return;
67255                 }
67256                 if (typeof(add.start_dt) == 'undefined')  {
67257                     Roo.log("Missing Start time in calendar data: ");
67258                     Roo.log(d);
67259                     return;
67260                 }
67261                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
67262                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
67263                 add.id = add.id || d.id;
67264                 add.title = add.title || '??';
67265                 
67266                 this.addItem(d);
67267                 
67268              
67269             },this);
67270         }
67271         
67272         this.renderEvents();
67273     }
67274     
67275
67276 });
67277 /*
67278  grid : {
67279                 xtype: 'Grid',
67280                 xns: Roo.grid,
67281                 listeners : {
67282                     render : function ()
67283                     {
67284                         _this.grid = this;
67285                         
67286                         if (!this.view.el.hasClass('course-timesheet')) {
67287                             this.view.el.addClass('course-timesheet');
67288                         }
67289                         if (this.tsStyle) {
67290                             this.ds.load({});
67291                             return; 
67292                         }
67293                         Roo.log('width');
67294                         Roo.log(_this.grid.view.el.getWidth());
67295                         
67296                         
67297                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
67298                             '.course-timesheet .x-grid-row' : {
67299                                 height: '80px'
67300                             },
67301                             '.x-grid-row td' : {
67302                                 'vertical-align' : 0
67303                             },
67304                             '.course-edit-link' : {
67305                                 'color' : 'blue',
67306                                 'text-overflow' : 'ellipsis',
67307                                 'overflow' : 'hidden',
67308                                 'white-space' : 'nowrap',
67309                                 'cursor' : 'pointer'
67310                             },
67311                             '.sub-link' : {
67312                                 'color' : 'green'
67313                             },
67314                             '.de-act-sup-link' : {
67315                                 'color' : 'purple',
67316                                 'text-decoration' : 'line-through'
67317                             },
67318                             '.de-act-link' : {
67319                                 'color' : 'red',
67320                                 'text-decoration' : 'line-through'
67321                             },
67322                             '.course-timesheet .course-highlight' : {
67323                                 'border-top-style': 'dashed !important',
67324                                 'border-bottom-bottom': 'dashed !important'
67325                             },
67326                             '.course-timesheet .course-item' : {
67327                                 'font-family'   : 'tahoma, arial, helvetica',
67328                                 'font-size'     : '11px',
67329                                 'overflow'      : 'hidden',
67330                                 'padding-left'  : '10px',
67331                                 'padding-right' : '10px',
67332                                 'padding-top' : '10px' 
67333                             }
67334                             
67335                         }, Roo.id());
67336                                 this.ds.load({});
67337                     }
67338                 },
67339                 autoWidth : true,
67340                 monitorWindowResize : false,
67341                 cellrenderer : function(v,x,r)
67342                 {
67343                     return v;
67344                 },
67345                 sm : {
67346                     xtype: 'CellSelectionModel',
67347                     xns: Roo.grid
67348                 },
67349                 dataSource : {
67350                     xtype: 'Store',
67351                     xns: Roo.data,
67352                     listeners : {
67353                         beforeload : function (_self, options)
67354                         {
67355                             options.params = options.params || {};
67356                             options.params._month = _this.monthField.getValue();
67357                             options.params.limit = 9999;
67358                             options.params['sort'] = 'when_dt';    
67359                             options.params['dir'] = 'ASC';    
67360                             this.proxy.loadResponse = this.loadResponse;
67361                             Roo.log("load?");
67362                             //this.addColumns();
67363                         },
67364                         load : function (_self, records, options)
67365                         {
67366                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
67367                                 // if you click on the translation.. you can edit it...
67368                                 var el = Roo.get(this);
67369                                 var id = el.dom.getAttribute('data-id');
67370                                 var d = el.dom.getAttribute('data-date');
67371                                 var t = el.dom.getAttribute('data-time');
67372                                 //var id = this.child('span').dom.textContent;
67373                                 
67374                                 //Roo.log(this);
67375                                 Pman.Dialog.CourseCalendar.show({
67376                                     id : id,
67377                                     when_d : d,
67378                                     when_t : t,
67379                                     productitem_active : id ? 1 : 0
67380                                 }, function() {
67381                                     _this.grid.ds.load({});
67382                                 });
67383                            
67384                            });
67385                            
67386                            _this.panel.fireEvent('resize', [ '', '' ]);
67387                         }
67388                     },
67389                     loadResponse : function(o, success, response){
67390                             // this is overridden on before load..
67391                             
67392                             Roo.log("our code?");       
67393                             //Roo.log(success);
67394                             //Roo.log(response)
67395                             delete this.activeRequest;
67396                             if(!success){
67397                                 this.fireEvent("loadexception", this, o, response);
67398                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67399                                 return;
67400                             }
67401                             var result;
67402                             try {
67403                                 result = o.reader.read(response);
67404                             }catch(e){
67405                                 Roo.log("load exception?");
67406                                 this.fireEvent("loadexception", this, o, response, e);
67407                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67408                                 return;
67409                             }
67410                             Roo.log("ready...");        
67411                             // loop through result.records;
67412                             // and set this.tdate[date] = [] << array of records..
67413                             _this.tdata  = {};
67414                             Roo.each(result.records, function(r){
67415                                 //Roo.log(r.data);
67416                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
67417                                     _this.tdata[r.data.when_dt.format('j')] = [];
67418                                 }
67419                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
67420                             });
67421                             
67422                             //Roo.log(_this.tdata);
67423                             
67424                             result.records = [];
67425                             result.totalRecords = 6;
67426                     
67427                             // let's generate some duumy records for the rows.
67428                             //var st = _this.dateField.getValue();
67429                             
67430                             // work out monday..
67431                             //st = st.add(Date.DAY, -1 * st.format('w'));
67432                             
67433                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67434                             
67435                             var firstOfMonth = date.getFirstDayOfMonth();
67436                             var days = date.getDaysInMonth();
67437                             var d = 1;
67438                             var firstAdded = false;
67439                             for (var i = 0; i < result.totalRecords ; i++) {
67440                                 //var d= st.add(Date.DAY, i);
67441                                 var row = {};
67442                                 var added = 0;
67443                                 for(var w = 0 ; w < 7 ; w++){
67444                                     if(!firstAdded && firstOfMonth != w){
67445                                         continue;
67446                                     }
67447                                     if(d > days){
67448                                         continue;
67449                                     }
67450                                     firstAdded = true;
67451                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
67452                                     row['weekday'+w] = String.format(
67453                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
67454                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
67455                                                     d,
67456                                                     date.format('Y-m-')+dd
67457                                                 );
67458                                     added++;
67459                                     if(typeof(_this.tdata[d]) != 'undefined'){
67460                                         Roo.each(_this.tdata[d], function(r){
67461                                             var is_sub = '';
67462                                             var deactive = '';
67463                                             var id = r.id;
67464                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
67465                                             if(r.parent_id*1>0){
67466                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
67467                                                 id = r.parent_id;
67468                                             }
67469                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
67470                                                 deactive = 'de-act-link';
67471                                             }
67472                                             
67473                                             row['weekday'+w] += String.format(
67474                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
67475                                                     id, //0
67476                                                     r.product_id_name, //1
67477                                                     r.when_dt.format('h:ia'), //2
67478                                                     is_sub, //3
67479                                                     deactive, //4
67480                                                     desc // 5
67481                                             );
67482                                         });
67483                                     }
67484                                     d++;
67485                                 }
67486                                 
67487                                 // only do this if something added..
67488                                 if(added > 0){ 
67489                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
67490                                 }
67491                                 
67492                                 
67493                                 // push it twice. (second one with an hour..
67494                                 
67495                             }
67496                             //Roo.log(result);
67497                             this.fireEvent("load", this, o, o.request.arg);
67498                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
67499                         },
67500                     sortInfo : {field: 'when_dt', direction : 'ASC' },
67501                     proxy : {
67502                         xtype: 'HttpProxy',
67503                         xns: Roo.data,
67504                         method : 'GET',
67505                         url : baseURL + '/Roo/Shop_course.php'
67506                     },
67507                     reader : {
67508                         xtype: 'JsonReader',
67509                         xns: Roo.data,
67510                         id : 'id',
67511                         fields : [
67512                             {
67513                                 'name': 'id',
67514                                 'type': 'int'
67515                             },
67516                             {
67517                                 'name': 'when_dt',
67518                                 'type': 'string'
67519                             },
67520                             {
67521                                 'name': 'end_dt',
67522                                 'type': 'string'
67523                             },
67524                             {
67525                                 'name': 'parent_id',
67526                                 'type': 'int'
67527                             },
67528                             {
67529                                 'name': 'product_id',
67530                                 'type': 'int'
67531                             },
67532                             {
67533                                 'name': 'productitem_id',
67534                                 'type': 'int'
67535                             },
67536                             {
67537                                 'name': 'guid',
67538                                 'type': 'int'
67539                             }
67540                         ]
67541                     }
67542                 },
67543                 toolbar : {
67544                     xtype: 'Toolbar',
67545                     xns: Roo,
67546                     items : [
67547                         {
67548                             xtype: 'Button',
67549                             xns: Roo.Toolbar,
67550                             listeners : {
67551                                 click : function (_self, e)
67552                                 {
67553                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67554                                     sd.setMonth(sd.getMonth()-1);
67555                                     _this.monthField.setValue(sd.format('Y-m-d'));
67556                                     _this.grid.ds.load({});
67557                                 }
67558                             },
67559                             text : "Back"
67560                         },
67561                         {
67562                             xtype: 'Separator',
67563                             xns: Roo.Toolbar
67564                         },
67565                         {
67566                             xtype: 'MonthField',
67567                             xns: Roo.form,
67568                             listeners : {
67569                                 render : function (_self)
67570                                 {
67571                                     _this.monthField = _self;
67572                                    // _this.monthField.set  today
67573                                 },
67574                                 select : function (combo, date)
67575                                 {
67576                                     _this.grid.ds.load({});
67577                                 }
67578                             },
67579                             value : (function() { return new Date(); })()
67580                         },
67581                         {
67582                             xtype: 'Separator',
67583                             xns: Roo.Toolbar
67584                         },
67585                         {
67586                             xtype: 'TextItem',
67587                             xns: Roo.Toolbar,
67588                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
67589                         },
67590                         {
67591                             xtype: 'Fill',
67592                             xns: Roo.Toolbar
67593                         },
67594                         {
67595                             xtype: 'Button',
67596                             xns: Roo.Toolbar,
67597                             listeners : {
67598                                 click : function (_self, e)
67599                                 {
67600                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67601                                     sd.setMonth(sd.getMonth()+1);
67602                                     _this.monthField.setValue(sd.format('Y-m-d'));
67603                                     _this.grid.ds.load({});
67604                                 }
67605                             },
67606                             text : "Next"
67607                         }
67608                     ]
67609                 },
67610                  
67611             }
67612         };
67613         
67614         *//*
67615  * Based on:
67616  * Ext JS Library 1.1.1
67617  * Copyright(c) 2006-2007, Ext JS, LLC.
67618  *
67619  * Originally Released Under LGPL - original licence link has changed is not relivant.
67620  *
67621  * Fork - LGPL
67622  * <script type="text/javascript">
67623  */
67624  
67625 /**
67626  * @class Roo.LoadMask
67627  * A simple utility class for generically masking elements while loading data.  If the element being masked has
67628  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
67629  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
67630  * element's UpdateManager load indicator and will be destroyed after the initial load.
67631  * @constructor
67632  * Create a new LoadMask
67633  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
67634  * @param {Object} config The config object
67635  */
67636 Roo.LoadMask = function(el, config){
67637     this.el = Roo.get(el);
67638     Roo.apply(this, config);
67639     if(this.store){
67640         this.store.on('beforeload', this.onBeforeLoad, this);
67641         this.store.on('load', this.onLoad, this);
67642         this.store.on('loadexception', this.onLoadException, this);
67643         this.removeMask = false;
67644     }else{
67645         var um = this.el.getUpdateManager();
67646         um.showLoadIndicator = false; // disable the default indicator
67647         um.on('beforeupdate', this.onBeforeLoad, this);
67648         um.on('update', this.onLoad, this);
67649         um.on('failure', this.onLoad, this);
67650         this.removeMask = true;
67651     }
67652 };
67653
67654 Roo.LoadMask.prototype = {
67655     /**
67656      * @cfg {Boolean} removeMask
67657      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
67658      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
67659      */
67660     removeMask : false,
67661     /**
67662      * @cfg {String} msg
67663      * The text to display in a centered loading message box (defaults to 'Loading...')
67664      */
67665     msg : 'Loading...',
67666     /**
67667      * @cfg {String} msgCls
67668      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
67669      */
67670     msgCls : 'x-mask-loading',
67671
67672     /**
67673      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
67674      * @type Boolean
67675      */
67676     disabled: false,
67677
67678     /**
67679      * Disables the mask to prevent it from being displayed
67680      */
67681     disable : function(){
67682        this.disabled = true;
67683     },
67684
67685     /**
67686      * Enables the mask so that it can be displayed
67687      */
67688     enable : function(){
67689         this.disabled = false;
67690     },
67691     
67692     onLoadException : function()
67693     {
67694         Roo.log(arguments);
67695         
67696         if (typeof(arguments[3]) != 'undefined') {
67697             Roo.MessageBox.alert("Error loading",arguments[3]);
67698         } 
67699         /*
67700         try {
67701             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
67702                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
67703             }   
67704         } catch(e) {
67705             
67706         }
67707         */
67708     
67709         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67710     },
67711     // private
67712     onLoad : function()
67713     {
67714         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67715     },
67716
67717     // private
67718     onBeforeLoad : function(){
67719         if(!this.disabled){
67720             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
67721         }
67722     },
67723
67724     // private
67725     destroy : function(){
67726         if(this.store){
67727             this.store.un('beforeload', this.onBeforeLoad, this);
67728             this.store.un('load', this.onLoad, this);
67729             this.store.un('loadexception', this.onLoadException, this);
67730         }else{
67731             var um = this.el.getUpdateManager();
67732             um.un('beforeupdate', this.onBeforeLoad, this);
67733             um.un('update', this.onLoad, this);
67734             um.un('failure', this.onLoad, this);
67735         }
67736     }
67737 };/*
67738  * Based on:
67739  * Ext JS Library 1.1.1
67740  * Copyright(c) 2006-2007, Ext JS, LLC.
67741  *
67742  * Originally Released Under LGPL - original licence link has changed is not relivant.
67743  *
67744  * Fork - LGPL
67745  * <script type="text/javascript">
67746  */
67747
67748
67749 /**
67750  * @class Roo.XTemplate
67751  * @extends Roo.Template
67752  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
67753 <pre><code>
67754 var t = new Roo.XTemplate(
67755         '&lt;select name="{name}"&gt;',
67756                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
67757         '&lt;/select&gt;'
67758 );
67759  
67760 // then append, applying the master template values
67761  </code></pre>
67762  *
67763  * Supported features:
67764  *
67765  *  Tags:
67766
67767 <pre><code>
67768       {a_variable} - output encoded.
67769       {a_variable.format:("Y-m-d")} - call a method on the variable
67770       {a_variable:raw} - unencoded output
67771       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
67772       {a_variable:this.method_on_template(...)} - call a method on the template object.
67773  
67774 </code></pre>
67775  *  The tpl tag:
67776 <pre><code>
67777         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
67778         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
67779         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
67780         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
67781   
67782         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
67783         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
67784 </code></pre>
67785  *      
67786  */
67787 Roo.XTemplate = function()
67788 {
67789     Roo.XTemplate.superclass.constructor.apply(this, arguments);
67790     if (this.html) {
67791         this.compile();
67792     }
67793 };
67794
67795
67796 Roo.extend(Roo.XTemplate, Roo.Template, {
67797
67798     /**
67799      * The various sub templates
67800      */
67801     tpls : false,
67802     /**
67803      *
67804      * basic tag replacing syntax
67805      * WORD:WORD()
67806      *
67807      * // you can fake an object call by doing this
67808      *  x.t:(test,tesT) 
67809      * 
67810      */
67811     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
67812
67813     /**
67814      * compile the template
67815      *
67816      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
67817      *
67818      */
67819     compile: function()
67820     {
67821         var s = this.html;
67822      
67823         s = ['<tpl>', s, '</tpl>'].join('');
67824     
67825         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
67826             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
67827             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
67828             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
67829             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
67830             m,
67831             id     = 0,
67832             tpls   = [];
67833     
67834         while(true == !!(m = s.match(re))){
67835             var forMatch   = m[0].match(nameRe),
67836                 ifMatch   = m[0].match(ifRe),
67837                 execMatch   = m[0].match(execRe),
67838                 namedMatch   = m[0].match(namedRe),
67839                 
67840                 exp  = null, 
67841                 fn   = null,
67842                 exec = null,
67843                 name = forMatch && forMatch[1] ? forMatch[1] : '';
67844                 
67845             if (ifMatch) {
67846                 // if - puts fn into test..
67847                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
67848                 if(exp){
67849                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
67850                 }
67851             }
67852             
67853             if (execMatch) {
67854                 // exec - calls a function... returns empty if true is  returned.
67855                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
67856                 if(exp){
67857                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
67858                 }
67859             }
67860             
67861             
67862             if (name) {
67863                 // for = 
67864                 switch(name){
67865                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
67866                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
67867                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
67868                 }
67869             }
67870             var uid = namedMatch ? namedMatch[1] : id;
67871             
67872             
67873             tpls.push({
67874                 id:     namedMatch ? namedMatch[1] : id,
67875                 target: name,
67876                 exec:   exec,
67877                 test:   fn,
67878                 body:   m[1] || ''
67879             });
67880             if (namedMatch) {
67881                 s = s.replace(m[0], '');
67882             } else { 
67883                 s = s.replace(m[0], '{xtpl'+ id + '}');
67884             }
67885             ++id;
67886         }
67887         this.tpls = [];
67888         for(var i = tpls.length-1; i >= 0; --i){
67889             this.compileTpl(tpls[i]);
67890             this.tpls[tpls[i].id] = tpls[i];
67891         }
67892         this.master = tpls[tpls.length-1];
67893         return this;
67894     },
67895     /**
67896      * same as applyTemplate, except it's done to one of the subTemplates
67897      * when using named templates, you can do:
67898      *
67899      * var str = pl.applySubTemplate('your-name', values);
67900      *
67901      * 
67902      * @param {Number} id of the template
67903      * @param {Object} values to apply to template
67904      * @param {Object} parent (normaly the instance of this object)
67905      */
67906     applySubTemplate : function(id, values, parent)
67907     {
67908         
67909         
67910         var t = this.tpls[id];
67911         
67912         
67913         try { 
67914             if(t.test && !t.test.call(this, values, parent)){
67915                 return '';
67916             }
67917         } catch(e) {
67918             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
67919             Roo.log(e.toString());
67920             Roo.log(t.test);
67921             return ''
67922         }
67923         try { 
67924             
67925             if(t.exec && t.exec.call(this, values, parent)){
67926                 return '';
67927             }
67928         } catch(e) {
67929             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
67930             Roo.log(e.toString());
67931             Roo.log(t.exec);
67932             return ''
67933         }
67934         try {
67935             var vs = t.target ? t.target.call(this, values, parent) : values;
67936             parent = t.target ? values : parent;
67937             if(t.target && vs instanceof Array){
67938                 var buf = [];
67939                 for(var i = 0, len = vs.length; i < len; i++){
67940                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
67941                 }
67942                 return buf.join('');
67943             }
67944             return t.compiled.call(this, vs, parent);
67945         } catch (e) {
67946             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
67947             Roo.log(e.toString());
67948             Roo.log(t.compiled);
67949             return '';
67950         }
67951     },
67952
67953     compileTpl : function(tpl)
67954     {
67955         var fm = Roo.util.Format;
67956         var useF = this.disableFormats !== true;
67957         var sep = Roo.isGecko ? "+" : ",";
67958         var undef = function(str) {
67959             Roo.log("Property not found :"  + str);
67960             return '';
67961         };
67962         
67963         var fn = function(m, name, format, args)
67964         {
67965             //Roo.log(arguments);
67966             args = args ? args.replace(/\\'/g,"'") : args;
67967             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
67968             if (typeof(format) == 'undefined') {
67969                 format= 'htmlEncode';
67970             }
67971             if (format == 'raw' ) {
67972                 format = false;
67973             }
67974             
67975             if(name.substr(0, 4) == 'xtpl'){
67976                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
67977             }
67978             
67979             // build an array of options to determine if value is undefined..
67980             
67981             // basically get 'xxxx.yyyy' then do
67982             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
67983             //    (function () { Roo.log("Property not found"); return ''; })() :
67984             //    ......
67985             
67986             var udef_ar = [];
67987             var lookfor = '';
67988             Roo.each(name.split('.'), function(st) {
67989                 lookfor += (lookfor.length ? '.': '') + st;
67990                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
67991             });
67992             
67993             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
67994             
67995             
67996             if(format && useF){
67997                 
67998                 args = args ? ',' + args : "";
67999                  
68000                 if(format.substr(0, 5) != "this."){
68001                     format = "fm." + format + '(';
68002                 }else{
68003                     format = 'this.call("'+ format.substr(5) + '", ';
68004                     args = ", values";
68005                 }
68006                 
68007                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
68008             }
68009              
68010             if (args.length) {
68011                 // called with xxyx.yuu:(test,test)
68012                 // change to ()
68013                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
68014             }
68015             // raw.. - :raw modifier..
68016             return "'"+ sep + udef_st  + name + ")"+sep+"'";
68017             
68018         };
68019         var body;
68020         // branched to use + in gecko and [].join() in others
68021         if(Roo.isGecko){
68022             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
68023                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
68024                     "';};};";
68025         }else{
68026             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
68027             body.push(tpl.body.replace(/(\r\n|\n)/g,
68028                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
68029             body.push("'].join('');};};");
68030             body = body.join('');
68031         }
68032         
68033         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
68034        
68035         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
68036         eval(body);
68037         
68038         return this;
68039     },
68040
68041     applyTemplate : function(values){
68042         return this.master.compiled.call(this, values, {});
68043         //var s = this.subs;
68044     },
68045
68046     apply : function(){
68047         return this.applyTemplate.apply(this, arguments);
68048     }
68049
68050  });
68051
68052 Roo.XTemplate.from = function(el){
68053     el = Roo.getDom(el);
68054     return new Roo.XTemplate(el.value || el.innerHTML);
68055 };