roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux" 
704                );
705 /*
706  * Based on:
707  * Ext JS Library 1.1.1
708  * Copyright(c) 2006-2007, Ext JS, LLC.
709  *
710  * Originally Released Under LGPL - original licence link has changed is not relivant.
711  *
712  * Fork - LGPL
713  * <script type="text/javascript">
714  */
715
716 (function() {    
717     // wrappedn so fnCleanup is not in global scope...
718     if(Roo.isIE) {
719         function fnCleanUp() {
720             var p = Function.prototype;
721             delete p.createSequence;
722             delete p.defer;
723             delete p.createDelegate;
724             delete p.createCallback;
725             delete p.createInterceptor;
726
727             window.detachEvent("onunload", fnCleanUp);
728         }
729         window.attachEvent("onunload", fnCleanUp);
730     }
731 })();
732
733
734 /**
735  * @class Function
736  * These functions are available on every Function object (any JavaScript function).
737  */
738 Roo.apply(Function.prototype, {
739      /**
740      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742      * Will create a function that is bound to those 2 args.
743      * @return {Function} The new function
744     */
745     createCallback : function(/*args...*/){
746         // make args available, in function below
747         var args = arguments;
748         var method = this;
749         return function() {
750             return method.apply(window, args);
751         };
752     },
753
754     /**
755      * Creates a delegate (callback) that sets the scope to obj.
756      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757      * Will create a function that is automatically scoped to this.
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Function} The new function
763      */
764     createDelegate : function(obj, args, appendArgs){
765         var method = this;
766         return function() {
767             var callArgs = args || arguments;
768             if(appendArgs === true){
769                 callArgs = Array.prototype.slice.call(arguments, 0);
770                 callArgs = callArgs.concat(args);
771             }else if(typeof appendArgs == "number"){
772                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
775             }
776             return method.apply(obj || window, callArgs);
777         };
778     },
779
780     /**
781      * Calls this function after the number of millseconds specified.
782      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783      * @param {Object} obj (optional) The object for which the scope is set
784      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786      *                                             if a number the args are inserted at the specified position
787      * @return {Number} The timeout id that can be used with clearTimeout
788      */
789     defer : function(millis, obj, args, appendArgs){
790         var fn = this.createDelegate(obj, args, appendArgs);
791         if(millis){
792             return setTimeout(fn, millis);
793         }
794         fn();
795         return 0;
796     },
797     /**
798      * Create a combined function call sequence of the original function + the passed function.
799      * The resulting function returns the results of the original function.
800      * The passed fcn is called with the parameters of the original function
801      * @param {Function} fcn The function to sequence
802      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803      * @return {Function} The new function
804      */
805     createSequence : function(fcn, scope){
806         if(typeof fcn != "function"){
807             return this;
808         }
809         var method = this;
810         return function() {
811             var retval = method.apply(this || window, arguments);
812             fcn.apply(scope || this || window, arguments);
813             return retval;
814         };
815     },
816
817     /**
818      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819      * The resulting function returns the results of the original function.
820      * The passed fcn is called with the parameters of the original function.
821      * @addon
822      * @param {Function} fcn The function to call before the original
823      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824      * @return {Function} The new function
825      */
826     createInterceptor : function(fcn, scope){
827         if(typeof fcn != "function"){
828             return this;
829         }
830         var method = this;
831         return function() {
832             fcn.target = this;
833             fcn.method = method;
834             if(fcn.apply(scope || this || window, arguments) === false){
835                 return;
836             }
837             return method.apply(this || window, arguments);
838         };
839     }
840 });
841 /*
842  * Based on:
843  * Ext JS Library 1.1.1
844  * Copyright(c) 2006-2007, Ext JS, LLC.
845  *
846  * Originally Released Under LGPL - original licence link has changed is not relivant.
847  *
848  * Fork - LGPL
849  * <script type="text/javascript">
850  */
851
852 Roo.applyIf(String, {
853     
854     /** @scope String */
855     
856     /**
857      * Escapes the passed string for ' and \
858      * @param {String} string The string to escape
859      * @return {String} The escaped string
860      * @static
861      */
862     escape : function(string) {
863         return string.replace(/('|\\)/g, "\\$1");
864     },
865
866     /**
867      * Pads the left side of a string with a specified character.  This is especially useful
868      * for normalizing number and date strings.  Example usage:
869      * <pre><code>
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
872 </code></pre>
873      * @param {String} string The original string
874      * @param {Number} size The total length of the output string
875      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876      * @return {String} The padded string
877      * @static
878      */
879     leftPad : function (val, size, ch) {
880         var result = new String(val);
881         if(ch === null || ch === undefined || ch === '') {
882             ch = " ";
883         }
884         while (result.length < size) {
885             result = ch + result;
886         }
887         return result;
888     },
889
890     /**
891      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
892      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
893      * <pre><code>
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
897 </code></pre>
898      * @param {String} string The tokenized string to be formatted
899      * @param {String} value1 The value to replace token {0}
900      * @param {String} value2 Etc...
901      * @return {String} The formatted string
902      * @static
903      */
904     format : function(format){
905         var args = Array.prototype.slice.call(arguments, 1);
906         return format.replace(/\{(\d+)\}/g, function(m, i){
907             return Roo.util.Format.htmlEncode(args[i]);
908         });
909     }
910   
911     
912 });
913
914 /**
915  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
916  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
917  * they are already different, the first value passed in is returned.  Note that this method returns the new value
918  * but does not change the current string.
919  * <pre><code>
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
922
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
925 </code></pre>
926  * @param {String} value The value to compare to the current string
927  * @param {String} other The new value to use if the string already equals the first value passed in
928  * @return {String} The new value
929  */
930  
931 String.prototype.toggle = function(value, other){
932     return this == value ? other : value;
933 };
934
935
936 /**
937   * Remove invalid unicode characters from a string 
938   *
939   * @return {String} The clean string
940   */
941 String.prototype.unicodeClean = function () {
942     return this.replace(/[\s\S]/g,
943         function(character) {
944             if (character.charCodeAt()< 256) {
945               return character;
946            }
947            try {
948                 encodeURIComponent(character);
949            } catch(e) { 
950               return '';
951            }
952            return character;
953         }
954     );
955 };
956   
957 /*
958  * Based on:
959  * Ext JS Library 1.1.1
960  * Copyright(c) 2006-2007, Ext JS, LLC.
961  *
962  * Originally Released Under LGPL - original licence link has changed is not relivant.
963  *
964  * Fork - LGPL
965  * <script type="text/javascript">
966  */
967
968  /**
969  * @class Number
970  */
971 Roo.applyIf(Number.prototype, {
972     /**
973      * Checks whether or not the current number is within a desired range.  If the number is already within the
974      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
975      * exceeded.  Note that this method returns the constrained value but does not change the current number.
976      * @param {Number} min The minimum number in the range
977      * @param {Number} max The maximum number in the range
978      * @return {Number} The constrained value if outside the range, otherwise the current value
979      */
980     constrain : function(min, max){
981         return Math.min(Math.max(this, min), max);
982     }
983 });/*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993  /**
994  * @class Array
995  */
996 Roo.applyIf(Array.prototype, {
997     /**
998      * 
999      * Checks whether or not the specified object exists in the array.
1000      * @param {Object} o The object to check for
1001      * @return {Number} The index of o in the array (or -1 if it is not found)
1002      */
1003     indexOf : function(o){
1004        for (var i = 0, len = this.length; i < len; i++){
1005               if(this[i] == o) { return i; }
1006        }
1007            return -1;
1008     },
1009
1010     /**
1011      * Removes the specified object from the array.  If the object is not found nothing happens.
1012      * @param {Object} o The object to remove
1013      */
1014     remove : function(o){
1015        var index = this.indexOf(o);
1016        if(index != -1){
1017            this.splice(index, 1);
1018        }
1019     },
1020     /**
1021      * Map (JS 1.6 compatibility)
1022      * @param {Function} function  to call
1023      */
1024     map : function(fun )
1025     {
1026         var len = this.length >>> 0;
1027         if (typeof fun != "function") {
1028             throw new TypeError();
1029         }
1030         var res = new Array(len);
1031         var thisp = arguments[1];
1032         for (var i = 0; i < len; i++)
1033         {
1034             if (i in this) {
1035                 res[i] = fun.call(thisp, this[i], i, this);
1036             }
1037         }
1038
1039         return res;
1040     },
1041     /**
1042      * equals
1043      * @param {Array} o The array to compare to
1044      * @returns {Boolean} true if the same
1045      */
1046     equals : function(b)
1047     {
1048             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1049         if (this === b) {
1050             return true;
1051         }
1052         if (b == null) {
1053             return false;
1054         }
1055         if (this.length !== b.length) {
1056             return false;
1057         }
1058           
1059         // sort?? a.sort().equals(b.sort());
1060           
1061         for (var i = 0; i < this.length; ++i) {
1062             if (this[i] !== b[i]) {
1063             return false;
1064             }
1065         }
1066         return true;
1067     } 
1068     
1069     
1070     
1071     
1072 });
1073
1074 Roo.applyIf(Array, {
1075  /**
1076      * from
1077      * @static
1078      * @param {Array} o Or Array like object (eg. nodelist)
1079      * @returns {Array} 
1080      */
1081     from : function(o)
1082     {
1083         var ret= [];
1084     
1085         for (var i =0; i < o.length; i++) { 
1086             ret[i] = o[i];
1087         }
1088         return ret;
1089       
1090     }
1091 });
1092 /*
1093  * Based on:
1094  * Ext JS Library 1.1.1
1095  * Copyright(c) 2006-2007, Ext JS, LLC.
1096  *
1097  * Originally Released Under LGPL - original licence link has changed is not relivant.
1098  *
1099  * Fork - LGPL
1100  * <script type="text/javascript">
1101  */
1102
1103 /**
1104  * @class Date
1105  *
1106  * The date parsing and format syntax is a subset of
1107  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1108  * supported will provide results equivalent to their PHP versions.
1109  *
1110  * Following is the list of all currently supported formats:
1111  *<pre>
1112 Sample date:
1113 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1114
1115 Format  Output      Description
1116 ------  ----------  --------------------------------------------------------------
1117   d      10         Day of the month, 2 digits with leading zeros
1118   D      Wed        A textual representation of a day, three letters
1119   j      10         Day of the month without leading zeros
1120   l      Wednesday  A full textual representation of the day of the week
1121   S      th         English ordinal day of month suffix, 2 chars (use with j)
1122   w      3          Numeric representation of the day of the week
1123   z      9          The julian date, or day of the year (0-365)
1124   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1125   F      January    A full textual representation of the month
1126   m      01         Numeric representation of a month, with leading zeros
1127   M      Jan        Month name abbreviation, three letters
1128   n      1          Numeric representation of a month, without leading zeros
1129   t      31         Number of days in the given month
1130   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1131   Y      2007       A full numeric representation of a year, 4 digits
1132   y      07         A two digit representation of a year
1133   a      pm         Lowercase Ante meridiem and Post meridiem
1134   A      PM         Uppercase Ante meridiem and Post meridiem
1135   g      3          12-hour format of an hour without leading zeros
1136   G      15         24-hour format of an hour without leading zeros
1137   h      03         12-hour format of an hour with leading zeros
1138   H      15         24-hour format of an hour with leading zeros
1139   i      05         Minutes with leading zeros
1140   s      01         Seconds, with leading zeros
1141   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1142   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1143   T      CST        Timezone setting of the machine running the code
1144   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1145 </pre>
1146  *
1147  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1148  * <pre><code>
1149 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1150 document.write(dt.format('Y-m-d'));                         //2007-01-10
1151 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1152 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1153  </code></pre>
1154  *
1155  * Here are some standard date/time patterns that you might find helpful.  They
1156  * are not part of the source of Date.js, but to use them you can simply copy this
1157  * block of code into any script that is included after Date.js and they will also become
1158  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1159  * <pre><code>
1160 Date.patterns = {
1161     ISO8601Long:"Y-m-d H:i:s",
1162     ISO8601Short:"Y-m-d",
1163     ShortDate: "n/j/Y",
1164     LongDate: "l, F d, Y",
1165     FullDateTime: "l, F d, Y g:i:s A",
1166     MonthDay: "F d",
1167     ShortTime: "g:i A",
1168     LongTime: "g:i:s A",
1169     SortableDateTime: "Y-m-d\\TH:i:s",
1170     UniversalSortableDateTime: "Y-m-d H:i:sO",
1171     YearMonth: "F, Y"
1172 };
1173 </code></pre>
1174  *
1175  * Example usage:
1176  * <pre><code>
1177 var dt = new Date();
1178 document.write(dt.format(Date.patterns.ShortDate));
1179  </code></pre>
1180  */
1181
1182 /*
1183  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1184  * They generate precompiled functions from date formats instead of parsing and
1185  * processing the pattern every time you format a date.  These functions are available
1186  * on every Date object (any javascript function).
1187  *
1188  * The original article and download are here:
1189  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1190  *
1191  */
1192  
1193  
1194  // was in core
1195 /**
1196  Returns the number of milliseconds between this date and date
1197  @param {Date} date (optional) Defaults to now
1198  @return {Number} The diff in milliseconds
1199  @member Date getElapsed
1200  */
1201 Date.prototype.getElapsed = function(date) {
1202         return Math.abs((date || new Date()).getTime()-this.getTime());
1203 };
1204 // was in date file..
1205
1206
1207 // private
1208 Date.parseFunctions = {count:0};
1209 // private
1210 Date.parseRegexes = [];
1211 // private
1212 Date.formatFunctions = {count:0};
1213
1214 // private
1215 Date.prototype.dateFormat = function(format) {
1216     if (Date.formatFunctions[format] == null) {
1217         Date.createNewFormat(format);
1218     }
1219     var func = Date.formatFunctions[format];
1220     return this[func]();
1221 };
1222
1223
1224 /**
1225  * Formats a date given the supplied format string
1226  * @param {String} format The format string
1227  * @return {String} The formatted date
1228  * @method
1229  */
1230 Date.prototype.format = Date.prototype.dateFormat;
1231
1232 // private
1233 Date.createNewFormat = function(format) {
1234     var funcName = "format" + Date.formatFunctions.count++;
1235     Date.formatFunctions[format] = funcName;
1236     var code = "Date.prototype." + funcName + " = function(){return ";
1237     var special = false;
1238     var ch = '';
1239     for (var i = 0; i < format.length; ++i) {
1240         ch = format.charAt(i);
1241         if (!special && ch == "\\") {
1242             special = true;
1243         }
1244         else if (special) {
1245             special = false;
1246             code += "'" + String.escape(ch) + "' + ";
1247         }
1248         else {
1249             code += Date.getFormatCode(ch);
1250         }
1251     }
1252     /** eval:var:zzzzzzzzzzzzz */
1253     eval(code.substring(0, code.length - 3) + ";}");
1254 };
1255
1256 // private
1257 Date.getFormatCode = function(character) {
1258     switch (character) {
1259     case "d":
1260         return "String.leftPad(this.getDate(), 2, '0') + ";
1261     case "D":
1262         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1263     case "j":
1264         return "this.getDate() + ";
1265     case "l":
1266         return "Date.dayNames[this.getDay()] + ";
1267     case "S":
1268         return "this.getSuffix() + ";
1269     case "w":
1270         return "this.getDay() + ";
1271     case "z":
1272         return "this.getDayOfYear() + ";
1273     case "W":
1274         return "this.getWeekOfYear() + ";
1275     case "F":
1276         return "Date.monthNames[this.getMonth()] + ";
1277     case "m":
1278         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1279     case "M":
1280         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1281     case "n":
1282         return "(this.getMonth() + 1) + ";
1283     case "t":
1284         return "this.getDaysInMonth() + ";
1285     case "L":
1286         return "(this.isLeapYear() ? 1 : 0) + ";
1287     case "Y":
1288         return "this.getFullYear() + ";
1289     case "y":
1290         return "('' + this.getFullYear()).substring(2, 4) + ";
1291     case "a":
1292         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1293     case "A":
1294         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1295     case "g":
1296         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1297     case "G":
1298         return "this.getHours() + ";
1299     case "h":
1300         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1301     case "H":
1302         return "String.leftPad(this.getHours(), 2, '0') + ";
1303     case "i":
1304         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1305     case "s":
1306         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1307     case "O":
1308         return "this.getGMTOffset() + ";
1309     case "P":
1310         return "this.getGMTColonOffset() + ";
1311     case "T":
1312         return "this.getTimezone() + ";
1313     case "Z":
1314         return "(this.getTimezoneOffset() * -60) + ";
1315     default:
1316         return "'" + String.escape(character) + "' + ";
1317     }
1318 };
1319
1320 /**
1321  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1322  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1323  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1324  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1325  * string or the parse operation will fail.
1326  * Example Usage:
1327 <pre><code>
1328 //dt = Fri May 25 2007 (current date)
1329 var dt = new Date();
1330
1331 //dt = Thu May 25 2006 (today's month/day in 2006)
1332 dt = Date.parseDate("2006", "Y");
1333
1334 //dt = Sun Jan 15 2006 (all date parts specified)
1335 dt = Date.parseDate("2006-1-15", "Y-m-d");
1336
1337 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1338 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1339 </code></pre>
1340  * @param {String} input The unparsed date as a string
1341  * @param {String} format The format the date is in
1342  * @return {Date} The parsed date
1343  * @static
1344  */
1345 Date.parseDate = function(input, format) {
1346     if (Date.parseFunctions[format] == null) {
1347         Date.createParser(format);
1348     }
1349     var func = Date.parseFunctions[format];
1350     return Date[func](input);
1351 };
1352 /**
1353  * @private
1354  */
1355
1356 Date.createParser = function(format) {
1357     var funcName = "parse" + Date.parseFunctions.count++;
1358     var regexNum = Date.parseRegexes.length;
1359     var currentGroup = 1;
1360     Date.parseFunctions[format] = funcName;
1361
1362     var code = "Date." + funcName + " = function(input){\n"
1363         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1364         + "var d = new Date();\n"
1365         + "y = d.getFullYear();\n"
1366         + "m = d.getMonth();\n"
1367         + "d = d.getDate();\n"
1368         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1369         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1370         + "if (results && results.length > 0) {";
1371     var regex = "";
1372
1373     var special = false;
1374     var ch = '';
1375     for (var i = 0; i < format.length; ++i) {
1376         ch = format.charAt(i);
1377         if (!special && ch == "\\") {
1378             special = true;
1379         }
1380         else if (special) {
1381             special = false;
1382             regex += String.escape(ch);
1383         }
1384         else {
1385             var obj = Date.formatCodeToRegex(ch, currentGroup);
1386             currentGroup += obj.g;
1387             regex += obj.s;
1388             if (obj.g && obj.c) {
1389                 code += obj.c;
1390             }
1391         }
1392     }
1393
1394     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1395         + "{v = new Date(y, m, d, h, i, s);}\n"
1396         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1397         + "{v = new Date(y, m, d, h, i);}\n"
1398         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1399         + "{v = new Date(y, m, d, h);}\n"
1400         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1401         + "{v = new Date(y, m, d);}\n"
1402         + "else if (y >= 0 && m >= 0)\n"
1403         + "{v = new Date(y, m);}\n"
1404         + "else if (y >= 0)\n"
1405         + "{v = new Date(y);}\n"
1406         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1407         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1408         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1409         + ";}";
1410
1411     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1412     /** eval:var:zzzzzzzzzzzzz */
1413     eval(code);
1414 };
1415
1416 // private
1417 Date.formatCodeToRegex = function(character, currentGroup) {
1418     switch (character) {
1419     case "D":
1420         return {g:0,
1421         c:null,
1422         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1423     case "j":
1424         return {g:1,
1425             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1426             s:"(\\d{1,2})"}; // day of month without leading zeroes
1427     case "d":
1428         return {g:1,
1429             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1430             s:"(\\d{2})"}; // day of month with leading zeroes
1431     case "l":
1432         return {g:0,
1433             c:null,
1434             s:"(?:" + Date.dayNames.join("|") + ")"};
1435     case "S":
1436         return {g:0,
1437             c:null,
1438             s:"(?:st|nd|rd|th)"};
1439     case "w":
1440         return {g:0,
1441             c:null,
1442             s:"\\d"};
1443     case "z":
1444         return {g:0,
1445             c:null,
1446             s:"(?:\\d{1,3})"};
1447     case "W":
1448         return {g:0,
1449             c:null,
1450             s:"(?:\\d{2})"};
1451     case "F":
1452         return {g:1,
1453             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1454             s:"(" + Date.monthNames.join("|") + ")"};
1455     case "M":
1456         return {g:1,
1457             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1458             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1459     case "n":
1460         return {g:1,
1461             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1462             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1463     case "m":
1464         return {g:1,
1465             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1466             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1467     case "t":
1468         return {g:0,
1469             c:null,
1470             s:"\\d{1,2}"};
1471     case "L":
1472         return {g:0,
1473             c:null,
1474             s:"(?:1|0)"};
1475     case "Y":
1476         return {g:1,
1477             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1478             s:"(\\d{4})"};
1479     case "y":
1480         return {g:1,
1481             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1482                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1483             s:"(\\d{1,2})"};
1484     case "a":
1485         return {g:1,
1486             c:"if (results[" + currentGroup + "] == 'am') {\n"
1487                 + "if (h == 12) { h = 0; }\n"
1488                 + "} else { if (h < 12) { h += 12; }}",
1489             s:"(am|pm)"};
1490     case "A":
1491         return {g:1,
1492             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1493                 + "if (h == 12) { h = 0; }\n"
1494                 + "} else { if (h < 12) { h += 12; }}",
1495             s:"(AM|PM)"};
1496     case "g":
1497     case "G":
1498         return {g:1,
1499             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1500             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1501     case "h":
1502     case "H":
1503         return {g:1,
1504             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1505             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1506     case "i":
1507         return {g:1,
1508             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1509             s:"(\\d{2})"};
1510     case "s":
1511         return {g:1,
1512             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1513             s:"(\\d{2})"};
1514     case "O":
1515         return {g:1,
1516             c:[
1517                 "o = results[", currentGroup, "];\n",
1518                 "var sn = o.substring(0,1);\n", // get + / - sign
1519                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1520                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1521                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1522                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1523             ].join(""),
1524             s:"([+\-]\\d{2,4})"};
1525     
1526     
1527     case "P":
1528         return {g:1,
1529                 c:[
1530                    "o = results[", currentGroup, "];\n",
1531                    "var sn = o.substring(0,1);\n",
1532                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1533                    "var mn = o.substring(4,6) % 60;\n",
1534                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1535                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1536             ].join(""),
1537             s:"([+\-]\\d{4})"};
1538     case "T":
1539         return {g:0,
1540             c:null,
1541             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1542     case "Z":
1543         return {g:1,
1544             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1545                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1546             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1547     default:
1548         return {g:0,
1549             c:null,
1550             s:String.escape(character)};
1551     }
1552 };
1553
1554 /**
1555  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1556  * @return {String} The abbreviated timezone name (e.g. 'CST')
1557  */
1558 Date.prototype.getTimezone = function() {
1559     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1560 };
1561
1562 /**
1563  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1564  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1565  */
1566 Date.prototype.getGMTOffset = function() {
1567     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1568         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1569         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1570 };
1571
1572 /**
1573  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1574  * @return {String} 2-characters representing hours and 2-characters representing minutes
1575  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1576  */
1577 Date.prototype.getGMTColonOffset = function() {
1578         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1579                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1580                 + ":"
1581                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1582 }
1583
1584 /**
1585  * Get the numeric day number of the year, adjusted for leap year.
1586  * @return {Number} 0 through 364 (365 in leap years)
1587  */
1588 Date.prototype.getDayOfYear = function() {
1589     var num = 0;
1590     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1591     for (var i = 0; i < this.getMonth(); ++i) {
1592         num += Date.daysInMonth[i];
1593     }
1594     return num + this.getDate() - 1;
1595 };
1596
1597 /**
1598  * Get the string representation of the numeric week number of the year
1599  * (equivalent to the format specifier 'W').
1600  * @return {String} '00' through '52'
1601  */
1602 Date.prototype.getWeekOfYear = function() {
1603     // Skip to Thursday of this week
1604     var now = this.getDayOfYear() + (4 - this.getDay());
1605     // Find the first Thursday of the year
1606     var jan1 = new Date(this.getFullYear(), 0, 1);
1607     var then = (7 - jan1.getDay() + 4);
1608     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1609 };
1610
1611 /**
1612  * Whether or not the current date is in a leap year.
1613  * @return {Boolean} True if the current date is in a leap year, else false
1614  */
1615 Date.prototype.isLeapYear = function() {
1616     var year = this.getFullYear();
1617     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1618 };
1619
1620 /**
1621  * Get the first day of the current month, adjusted for leap year.  The returned value
1622  * is the numeric day index within the week (0-6) which can be used in conjunction with
1623  * the {@link #monthNames} array to retrieve the textual day name.
1624  * Example:
1625  *<pre><code>
1626 var dt = new Date('1/10/2007');
1627 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1628 </code></pre>
1629  * @return {Number} The day number (0-6)
1630  */
1631 Date.prototype.getFirstDayOfMonth = function() {
1632     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1633     return (day < 0) ? (day + 7) : day;
1634 };
1635
1636 /**
1637  * Get the last day of the current month, adjusted for leap year.  The returned value
1638  * is the numeric day index within the week (0-6) which can be used in conjunction with
1639  * the {@link #monthNames} array to retrieve the textual day name.
1640  * Example:
1641  *<pre><code>
1642 var dt = new Date('1/10/2007');
1643 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1644 </code></pre>
1645  * @return {Number} The day number (0-6)
1646  */
1647 Date.prototype.getLastDayOfMonth = function() {
1648     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1649     return (day < 0) ? (day + 7) : day;
1650 };
1651
1652
1653 /**
1654  * Get the first date of this date's month
1655  * @return {Date}
1656  */
1657 Date.prototype.getFirstDateOfMonth = function() {
1658     return new Date(this.getFullYear(), this.getMonth(), 1);
1659 };
1660
1661 /**
1662  * Get the last date of this date's month
1663  * @return {Date}
1664  */
1665 Date.prototype.getLastDateOfMonth = function() {
1666     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1667 };
1668 /**
1669  * Get the number of days in the current month, adjusted for leap year.
1670  * @return {Number} The number of days in the month
1671  */
1672 Date.prototype.getDaysInMonth = function() {
1673     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1674     return Date.daysInMonth[this.getMonth()];
1675 };
1676
1677 /**
1678  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1679  * @return {String} 'st, 'nd', 'rd' or 'th'
1680  */
1681 Date.prototype.getSuffix = function() {
1682     switch (this.getDate()) {
1683         case 1:
1684         case 21:
1685         case 31:
1686             return "st";
1687         case 2:
1688         case 22:
1689             return "nd";
1690         case 3:
1691         case 23:
1692             return "rd";
1693         default:
1694             return "th";
1695     }
1696 };
1697
1698 // private
1699 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1700
1701 /**
1702  * An array of textual month names.
1703  * Override these values for international dates, for example...
1704  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1705  * @type Array
1706  * @static
1707  */
1708 Date.monthNames =
1709    ["January",
1710     "February",
1711     "March",
1712     "April",
1713     "May",
1714     "June",
1715     "July",
1716     "August",
1717     "September",
1718     "October",
1719     "November",
1720     "December"];
1721
1722 /**
1723  * An array of textual day names.
1724  * Override these values for international dates, for example...
1725  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1726  * @type Array
1727  * @static
1728  */
1729 Date.dayNames =
1730    ["Sunday",
1731     "Monday",
1732     "Tuesday",
1733     "Wednesday",
1734     "Thursday",
1735     "Friday",
1736     "Saturday"];
1737
1738 // private
1739 Date.y2kYear = 50;
1740 // private
1741 Date.monthNumbers = {
1742     Jan:0,
1743     Feb:1,
1744     Mar:2,
1745     Apr:3,
1746     May:4,
1747     Jun:5,
1748     Jul:6,
1749     Aug:7,
1750     Sep:8,
1751     Oct:9,
1752     Nov:10,
1753     Dec:11};
1754
1755 /**
1756  * Creates and returns a new Date instance with the exact same date value as the called instance.
1757  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1758  * variable will also be changed.  When the intention is to create a new variable that will not
1759  * modify the original instance, you should create a clone.
1760  *
1761  * Example of correctly cloning a date:
1762  * <pre><code>
1763 //wrong way:
1764 var orig = new Date('10/1/2006');
1765 var copy = orig;
1766 copy.setDate(5);
1767 document.write(orig);  //returns 'Thu Oct 05 2006'!
1768
1769 //correct way:
1770 var orig = new Date('10/1/2006');
1771 var copy = orig.clone();
1772 copy.setDate(5);
1773 document.write(orig);  //returns 'Thu Oct 01 2006'
1774 </code></pre>
1775  * @return {Date} The new Date instance
1776  */
1777 Date.prototype.clone = function() {
1778         return new Date(this.getTime());
1779 };
1780
1781 /**
1782  * Clears any time information from this date
1783  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1784  @return {Date} this or the clone
1785  */
1786 Date.prototype.clearTime = function(clone){
1787     if(clone){
1788         return this.clone().clearTime();
1789     }
1790     this.setHours(0);
1791     this.setMinutes(0);
1792     this.setSeconds(0);
1793     this.setMilliseconds(0);
1794     return this;
1795 };
1796
1797 // private
1798 // safari setMonth is broken -- check that this is only donw once...
1799 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1800     Date.brokenSetMonth = Date.prototype.setMonth;
1801         Date.prototype.setMonth = function(num){
1802                 if(num <= -1){
1803                         var n = Math.ceil(-num);
1804                         var back_year = Math.ceil(n/12);
1805                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1806                         this.setFullYear(this.getFullYear() - back_year);
1807                         return Date.brokenSetMonth.call(this, month);
1808                 } else {
1809                         return Date.brokenSetMonth.apply(this, arguments);
1810                 }
1811         };
1812 }
1813
1814 /** Date interval constant 
1815 * @static 
1816 * @type String */
1817 Date.MILLI = "ms";
1818 /** Date interval constant 
1819 * @static 
1820 * @type String */
1821 Date.SECOND = "s";
1822 /** Date interval constant 
1823 * @static 
1824 * @type String */
1825 Date.MINUTE = "mi";
1826 /** Date interval constant 
1827 * @static 
1828 * @type String */
1829 Date.HOUR = "h";
1830 /** Date interval constant 
1831 * @static 
1832 * @type String */
1833 Date.DAY = "d";
1834 /** Date interval constant 
1835 * @static 
1836 * @type String */
1837 Date.MONTH = "mo";
1838 /** Date interval constant 
1839 * @static 
1840 * @type String */
1841 Date.YEAR = "y";
1842
1843 /**
1844  * Provides a convenient method of performing basic date arithmetic.  This method
1845  * does not modify the Date instance being called - it creates and returns
1846  * a new Date instance containing the resulting date value.
1847  *
1848  * Examples:
1849  * <pre><code>
1850 //Basic usage:
1851 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1852 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1853
1854 //Negative values will subtract correctly:
1855 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1856 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1857
1858 //You can even chain several calls together in one line!
1859 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1860 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1861  </code></pre>
1862  *
1863  * @param {String} interval   A valid date interval enum value
1864  * @param {Number} value      The amount to add to the current date
1865  * @return {Date} The new Date instance
1866  */
1867 Date.prototype.add = function(interval, value){
1868   var d = this.clone();
1869   if (!interval || value === 0) { return d; }
1870   switch(interval.toLowerCase()){
1871     case Date.MILLI:
1872       d.setMilliseconds(this.getMilliseconds() + value);
1873       break;
1874     case Date.SECOND:
1875       d.setSeconds(this.getSeconds() + value);
1876       break;
1877     case Date.MINUTE:
1878       d.setMinutes(this.getMinutes() + value);
1879       break;
1880     case Date.HOUR:
1881       d.setHours(this.getHours() + value);
1882       break;
1883     case Date.DAY:
1884       d.setDate(this.getDate() + value);
1885       break;
1886     case Date.MONTH:
1887       var day = this.getDate();
1888       if(day > 28){
1889           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1890       }
1891       d.setDate(day);
1892       d.setMonth(this.getMonth() + value);
1893       break;
1894     case Date.YEAR:
1895       d.setFullYear(this.getFullYear() + value);
1896       break;
1897   }
1898   return d;
1899 };
1900 /**
1901  * @class Roo.lib.Dom
1902  * @licence LGPL
1903  * @static
1904  * 
1905  * Dom utils (from YIU afaik)
1906  *
1907  * 
1908  **/
1909 Roo.lib.Dom = {
1910     /**
1911      * Get the view width
1912      * @param {Boolean} full True will get the full document, otherwise it's the view width
1913      * @return {Number} The width
1914      */
1915      
1916     getViewWidth : function(full) {
1917         return full ? this.getDocumentWidth() : this.getViewportWidth();
1918     },
1919     /**
1920      * Get the view height
1921      * @param {Boolean} full True will get the full document, otherwise it's the view height
1922      * @return {Number} The height
1923      */
1924     getViewHeight : function(full) {
1925         return full ? this.getDocumentHeight() : this.getViewportHeight();
1926     },
1927     /**
1928      * Get the Full Document height 
1929      * @return {Number} The height
1930      */
1931     getDocumentHeight: function() {
1932         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1933         return Math.max(scrollHeight, this.getViewportHeight());
1934     },
1935     /**
1936      * Get the Full Document width
1937      * @return {Number} The width
1938      */
1939     getDocumentWidth: function() {
1940         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1941         return Math.max(scrollWidth, this.getViewportWidth());
1942     },
1943     /**
1944      * Get the Window Viewport height
1945      * @return {Number} The height
1946      */
1947     getViewportHeight: function() {
1948         var height = self.innerHeight;
1949         var mode = document.compatMode;
1950
1951         if ((mode || Roo.isIE) && !Roo.isOpera) {
1952             height = (mode == "CSS1Compat") ?
1953                      document.documentElement.clientHeight :
1954                      document.body.clientHeight;
1955         }
1956
1957         return height;
1958     },
1959     /**
1960      * Get the Window Viewport width
1961      * @return {Number} The width
1962      */
1963     getViewportWidth: function() {
1964         var width = self.innerWidth;
1965         var mode = document.compatMode;
1966
1967         if (mode || Roo.isIE) {
1968             width = (mode == "CSS1Compat") ?
1969                     document.documentElement.clientWidth :
1970                     document.body.clientWidth;
1971         }
1972         return width;
1973     },
1974
1975     isAncestor : function(p, c) {
1976         p = Roo.getDom(p);
1977         c = Roo.getDom(c);
1978         if (!p || !c) {
1979             return false;
1980         }
1981
1982         if (p.contains && !Roo.isSafari) {
1983             return p.contains(c);
1984         } else if (p.compareDocumentPosition) {
1985             return !!(p.compareDocumentPosition(c) & 16);
1986         } else {
1987             var parent = c.parentNode;
1988             while (parent) {
1989                 if (parent == p) {
1990                     return true;
1991                 }
1992                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1993                     return false;
1994                 }
1995                 parent = parent.parentNode;
1996             }
1997             return false;
1998         }
1999     },
2000
2001     getRegion : function(el) {
2002         return Roo.lib.Region.getRegion(el);
2003     },
2004
2005     getY : function(el) {
2006         return this.getXY(el)[1];
2007     },
2008
2009     getX : function(el) {
2010         return this.getXY(el)[0];
2011     },
2012
2013     getXY : function(el) {
2014         var p, pe, b, scroll, bd = document.body;
2015         el = Roo.getDom(el);
2016         var fly = Roo.lib.AnimBase.fly;
2017         if (el.getBoundingClientRect) {
2018             b = el.getBoundingClientRect();
2019             scroll = fly(document).getScroll();
2020             return [b.left + scroll.left, b.top + scroll.top];
2021         }
2022         var x = 0, y = 0;
2023
2024         p = el;
2025
2026         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2027
2028         while (p) {
2029
2030             x += p.offsetLeft;
2031             y += p.offsetTop;
2032
2033             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2034                 hasAbsolute = true;
2035             }
2036
2037             if (Roo.isGecko) {
2038                 pe = fly(p);
2039
2040                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2041                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2042
2043
2044                 x += bl;
2045                 y += bt;
2046
2047
2048                 if (p != el && pe.getStyle('overflow') != 'visible') {
2049                     x += bl;
2050                     y += bt;
2051                 }
2052             }
2053             p = p.offsetParent;
2054         }
2055
2056         if (Roo.isSafari && hasAbsolute) {
2057             x -= bd.offsetLeft;
2058             y -= bd.offsetTop;
2059         }
2060
2061         if (Roo.isGecko && !hasAbsolute) {
2062             var dbd = fly(bd);
2063             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2064             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2065         }
2066
2067         p = el.parentNode;
2068         while (p && p != bd) {
2069             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2070                 x -= p.scrollLeft;
2071                 y -= p.scrollTop;
2072             }
2073             p = p.parentNode;
2074         }
2075         return [x, y];
2076     },
2077  
2078   
2079
2080
2081     setXY : function(el, xy) {
2082         el = Roo.fly(el, '_setXY');
2083         el.position();
2084         var pts = el.translatePoints(xy);
2085         if (xy[0] !== false) {
2086             el.dom.style.left = pts.left + "px";
2087         }
2088         if (xy[1] !== false) {
2089             el.dom.style.top = pts.top + "px";
2090         }
2091     },
2092
2093     setX : function(el, x) {
2094         this.setXY(el, [x, false]);
2095     },
2096
2097     setY : function(el, y) {
2098         this.setXY(el, [false, y]);
2099     }
2100 };
2101 /*
2102  * Portions of this file are based on pieces of Yahoo User Interface Library
2103  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2104  * YUI licensed under the BSD License:
2105  * http://developer.yahoo.net/yui/license.txt
2106  * <script type="text/javascript">
2107  *
2108  */
2109
2110 Roo.lib.Event = function() {
2111     var loadComplete = false;
2112     var listeners = [];
2113     var unloadListeners = [];
2114     var retryCount = 0;
2115     var onAvailStack = [];
2116     var counter = 0;
2117     var lastError = null;
2118
2119     return {
2120         POLL_RETRYS: 200,
2121         POLL_INTERVAL: 20,
2122         EL: 0,
2123         TYPE: 1,
2124         FN: 2,
2125         WFN: 3,
2126         OBJ: 3,
2127         ADJ_SCOPE: 4,
2128         _interval: null,
2129
2130         startInterval: function() {
2131             if (!this._interval) {
2132                 var self = this;
2133                 var callback = function() {
2134                     self._tryPreloadAttach();
2135                 };
2136                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2137
2138             }
2139         },
2140
2141         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2142             onAvailStack.push({ id:         p_id,
2143                 fn:         p_fn,
2144                 obj:        p_obj,
2145                 override:   p_override,
2146                 checkReady: false    });
2147
2148             retryCount = this.POLL_RETRYS;
2149             this.startInterval();
2150         },
2151
2152
2153         addListener: function(el, eventName, fn) {
2154             el = Roo.getDom(el);
2155             if (!el || !fn) {
2156                 return false;
2157             }
2158
2159             if ("unload" == eventName) {
2160                 unloadListeners[unloadListeners.length] =
2161                 [el, eventName, fn];
2162                 return true;
2163             }
2164
2165             var wrappedFn = function(e) {
2166                 return fn(Roo.lib.Event.getEvent(e));
2167             };
2168
2169             var li = [el, eventName, fn, wrappedFn];
2170
2171             var index = listeners.length;
2172             listeners[index] = li;
2173
2174             this.doAdd(el, eventName, wrappedFn, false);
2175             return true;
2176
2177         },
2178
2179
2180         removeListener: function(el, eventName, fn) {
2181             var i, len;
2182
2183             el = Roo.getDom(el);
2184
2185             if(!fn) {
2186                 return this.purgeElement(el, false, eventName);
2187             }
2188
2189
2190             if ("unload" == eventName) {
2191
2192                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2193                     var li = unloadListeners[i];
2194                     if (li &&
2195                         li[0] == el &&
2196                         li[1] == eventName &&
2197                         li[2] == fn) {
2198                         unloadListeners.splice(i, 1);
2199                         return true;
2200                     }
2201                 }
2202
2203                 return false;
2204             }
2205
2206             var cacheItem = null;
2207
2208
2209             var index = arguments[3];
2210
2211             if ("undefined" == typeof index) {
2212                 index = this._getCacheIndex(el, eventName, fn);
2213             }
2214
2215             if (index >= 0) {
2216                 cacheItem = listeners[index];
2217             }
2218
2219             if (!el || !cacheItem) {
2220                 return false;
2221             }
2222
2223             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2224
2225             delete listeners[index][this.WFN];
2226             delete listeners[index][this.FN];
2227             listeners.splice(index, 1);
2228
2229             return true;
2230
2231         },
2232
2233
2234         getTarget: function(ev, resolveTextNode) {
2235             ev = ev.browserEvent || ev;
2236             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2237             var t = ev.target || ev.srcElement;
2238             return this.resolveTextNode(t);
2239         },
2240
2241
2242         resolveTextNode: function(node) {
2243             if (Roo.isSafari && node && 3 == node.nodeType) {
2244                 return node.parentNode;
2245             } else {
2246                 return node;
2247             }
2248         },
2249
2250
2251         getPageX: function(ev) {
2252             ev = ev.browserEvent || ev;
2253             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2254             var x = ev.pageX;
2255             if (!x && 0 !== x) {
2256                 x = ev.clientX || 0;
2257
2258                 if (Roo.isIE) {
2259                     x += this.getScroll()[1];
2260                 }
2261             }
2262
2263             return x;
2264         },
2265
2266
2267         getPageY: function(ev) {
2268             ev = ev.browserEvent || ev;
2269             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2270             var y = ev.pageY;
2271             if (!y && 0 !== y) {
2272                 y = ev.clientY || 0;
2273
2274                 if (Roo.isIE) {
2275                     y += this.getScroll()[0];
2276                 }
2277             }
2278
2279
2280             return y;
2281         },
2282
2283
2284         getXY: function(ev) {
2285             ev = ev.browserEvent || ev;
2286             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2287             return [this.getPageX(ev), this.getPageY(ev)];
2288         },
2289
2290
2291         getRelatedTarget: function(ev) {
2292             ev = ev.browserEvent || ev;
2293             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2294             var t = ev.relatedTarget;
2295             if (!t) {
2296                 if (ev.type == "mouseout") {
2297                     t = ev.toElement;
2298                 } else if (ev.type == "mouseover") {
2299                     t = ev.fromElement;
2300                 }
2301             }
2302
2303             return this.resolveTextNode(t);
2304         },
2305
2306
2307         getTime: function(ev) {
2308             ev = ev.browserEvent || ev;
2309             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2310             if (!ev.time) {
2311                 var t = new Date().getTime();
2312                 try {
2313                     ev.time = t;
2314                 } catch(ex) {
2315                     this.lastError = ex;
2316                     return t;
2317                 }
2318             }
2319
2320             return ev.time;
2321         },
2322
2323
2324         stopEvent: function(ev) {
2325             this.stopPropagation(ev);
2326             this.preventDefault(ev);
2327         },
2328
2329
2330         stopPropagation: function(ev) {
2331             ev = ev.browserEvent || ev;
2332             if (ev.stopPropagation) {
2333                 ev.stopPropagation();
2334             } else {
2335                 ev.cancelBubble = true;
2336             }
2337         },
2338
2339
2340         preventDefault: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             if(ev.preventDefault) {
2343                 ev.preventDefault();
2344             } else {
2345                 ev.returnValue = false;
2346             }
2347         },
2348
2349
2350         getEvent: function(e) {
2351             var ev = e || window.event;
2352             if (!ev) {
2353                 var c = this.getEvent.caller;
2354                 while (c) {
2355                     ev = c.arguments[0];
2356                     if (ev && Event == ev.constructor) {
2357                         break;
2358                     }
2359                     c = c.caller;
2360                 }
2361             }
2362             return ev;
2363         },
2364
2365
2366         getCharCode: function(ev) {
2367             ev = ev.browserEvent || ev;
2368             return ev.charCode || ev.keyCode || 0;
2369         },
2370
2371
2372         _getCacheIndex: function(el, eventName, fn) {
2373             for (var i = 0,len = listeners.length; i < len; ++i) {
2374                 var li = listeners[i];
2375                 if (li &&
2376                     li[this.FN] == fn &&
2377                     li[this.EL] == el &&
2378                     li[this.TYPE] == eventName) {
2379                     return i;
2380                 }
2381             }
2382
2383             return -1;
2384         },
2385
2386
2387         elCache: {},
2388
2389
2390         getEl: function(id) {
2391             return document.getElementById(id);
2392         },
2393
2394
2395         clearCache: function() {
2396         },
2397
2398
2399         _load: function(e) {
2400             loadComplete = true;
2401             var EU = Roo.lib.Event;
2402
2403
2404             if (Roo.isIE) {
2405                 EU.doRemove(window, "load", EU._load);
2406             }
2407         },
2408
2409
2410         _tryPreloadAttach: function() {
2411
2412             if (this.locked) {
2413                 return false;
2414             }
2415
2416             this.locked = true;
2417
2418
2419             var tryAgain = !loadComplete;
2420             if (!tryAgain) {
2421                 tryAgain = (retryCount > 0);
2422             }
2423
2424
2425             var notAvail = [];
2426             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2427                 var item = onAvailStack[i];
2428                 if (item) {
2429                     var el = this.getEl(item.id);
2430
2431                     if (el) {
2432                         if (!item.checkReady ||
2433                             loadComplete ||
2434                             el.nextSibling ||
2435                             (document && document.body)) {
2436
2437                             var scope = el;
2438                             if (item.override) {
2439                                 if (item.override === true) {
2440                                     scope = item.obj;
2441                                 } else {
2442                                     scope = item.override;
2443                                 }
2444                             }
2445                             item.fn.call(scope, item.obj);
2446                             onAvailStack[i] = null;
2447                         }
2448                     } else {
2449                         notAvail.push(item);
2450                     }
2451                 }
2452             }
2453
2454             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2455
2456             if (tryAgain) {
2457
2458                 this.startInterval();
2459             } else {
2460                 clearInterval(this._interval);
2461                 this._interval = null;
2462             }
2463
2464             this.locked = false;
2465
2466             return true;
2467
2468         },
2469
2470
2471         purgeElement: function(el, recurse, eventName) {
2472             var elListeners = this.getListeners(el, eventName);
2473             if (elListeners) {
2474                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2475                     var l = elListeners[i];
2476                     this.removeListener(el, l.type, l.fn);
2477                 }
2478             }
2479
2480             if (recurse && el && el.childNodes) {
2481                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2482                     this.purgeElement(el.childNodes[i], recurse, eventName);
2483                 }
2484             }
2485         },
2486
2487
2488         getListeners: function(el, eventName) {
2489             var results = [], searchLists;
2490             if (!eventName) {
2491                 searchLists = [listeners, unloadListeners];
2492             } else if (eventName == "unload") {
2493                 searchLists = [unloadListeners];
2494             } else {
2495                 searchLists = [listeners];
2496             }
2497
2498             for (var j = 0; j < searchLists.length; ++j) {
2499                 var searchList = searchLists[j];
2500                 if (searchList && searchList.length > 0) {
2501                     for (var i = 0,len = searchList.length; i < len; ++i) {
2502                         var l = searchList[i];
2503                         if (l && l[this.EL] === el &&
2504                             (!eventName || eventName === l[this.TYPE])) {
2505                             results.push({
2506                                 type:   l[this.TYPE],
2507                                 fn:     l[this.FN],
2508                                 obj:    l[this.OBJ],
2509                                 adjust: l[this.ADJ_SCOPE],
2510                                 index:  i
2511                             });
2512                         }
2513                     }
2514                 }
2515             }
2516
2517             return (results.length) ? results : null;
2518         },
2519
2520
2521         _unload: function(e) {
2522
2523             var EU = Roo.lib.Event, i, j, l, len, index;
2524
2525             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2526                 l = unloadListeners[i];
2527                 if (l) {
2528                     var scope = window;
2529                     if (l[EU.ADJ_SCOPE]) {
2530                         if (l[EU.ADJ_SCOPE] === true) {
2531                             scope = l[EU.OBJ];
2532                         } else {
2533                             scope = l[EU.ADJ_SCOPE];
2534                         }
2535                     }
2536                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2537                     unloadListeners[i] = null;
2538                     l = null;
2539                     scope = null;
2540                 }
2541             }
2542
2543             unloadListeners = null;
2544
2545             if (listeners && listeners.length > 0) {
2546                 j = listeners.length;
2547                 while (j) {
2548                     index = j - 1;
2549                     l = listeners[index];
2550                     if (l) {
2551                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2552                                 l[EU.FN], index);
2553                     }
2554                     j = j - 1;
2555                 }
2556                 l = null;
2557
2558                 EU.clearCache();
2559             }
2560
2561             EU.doRemove(window, "unload", EU._unload);
2562
2563         },
2564
2565
2566         getScroll: function() {
2567             var dd = document.documentElement, db = document.body;
2568             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2569                 return [dd.scrollTop, dd.scrollLeft];
2570             } else if (db) {
2571                 return [db.scrollTop, db.scrollLeft];
2572             } else {
2573                 return [0, 0];
2574             }
2575         },
2576
2577
2578         doAdd: function () {
2579             if (window.addEventListener) {
2580                 return function(el, eventName, fn, capture) {
2581                     el.addEventListener(eventName, fn, (capture));
2582                 };
2583             } else if (window.attachEvent) {
2584                 return function(el, eventName, fn, capture) {
2585                     el.attachEvent("on" + eventName, fn);
2586                 };
2587             } else {
2588                 return function() {
2589                 };
2590             }
2591         }(),
2592
2593
2594         doRemove: function() {
2595             if (window.removeEventListener) {
2596                 return function (el, eventName, fn, capture) {
2597                     el.removeEventListener(eventName, fn, (capture));
2598                 };
2599             } else if (window.detachEvent) {
2600                 return function (el, eventName, fn) {
2601                     el.detachEvent("on" + eventName, fn);
2602                 };
2603             } else {
2604                 return function() {
2605                 };
2606             }
2607         }()
2608     };
2609     
2610 }();
2611 (function() {     
2612    
2613     var E = Roo.lib.Event;
2614     E.on = E.addListener;
2615     E.un = E.removeListener;
2616
2617     if (document && document.body) {
2618         E._load();
2619     } else {
2620         E.doAdd(window, "load", E._load);
2621     }
2622     E.doAdd(window, "unload", E._unload);
2623     E._tryPreloadAttach();
2624 })();
2625
2626  
2627
2628 (function() {
2629     /**
2630      * @class Roo.lib.Ajax
2631      *
2632      * provide a simple Ajax request utility functions
2633      * 
2634      * Portions of this file are based on pieces of Yahoo User Interface Library
2635     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2636     * YUI licensed under the BSD License:
2637     * http://developer.yahoo.net/yui/license.txt
2638     * <script type="text/javascript">
2639     *
2640      *
2641      */
2642     Roo.lib.Ajax = {
2643         /**
2644          * @static 
2645          */
2646         request : function(method, uri, cb, data, options) {
2647             if(options){
2648                 var hs = options.headers;
2649                 if(hs){
2650                     for(var h in hs){
2651                         if(hs.hasOwnProperty(h)){
2652                             this.initHeader(h, hs[h], false);
2653                         }
2654                     }
2655                 }
2656                 if(options.xmlData){
2657                     this.initHeader('Content-Type', 'text/xml', false);
2658                     method = 'POST';
2659                     data = options.xmlData;
2660                 }
2661             }
2662
2663             return this.asyncRequest(method, uri, cb, data);
2664         },
2665         /**
2666          * serialize a form
2667          *
2668          * @static
2669          * @param {DomForm} form element
2670          * @return {String} urlencode form output.
2671          */
2672         serializeForm : function(form) {
2673             if(typeof form == 'string') {
2674                 form = (document.getElementById(form) || document.forms[form]);
2675             }
2676
2677             var el, name, val, disabled, data = '', hasSubmit = false;
2678             for (var i = 0; i < form.elements.length; i++) {
2679                 el = form.elements[i];
2680                 disabled = form.elements[i].disabled;
2681                 name = form.elements[i].name;
2682                 val = form.elements[i].value;
2683
2684                 if (!disabled && name){
2685                     switch (el.type)
2686                             {
2687                         case 'select-one':
2688                         case 'select-multiple':
2689                             for (var j = 0; j < el.options.length; j++) {
2690                                 if (el.options[j].selected) {
2691                                     if (Roo.isIE) {
2692                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2693                                     }
2694                                     else {
2695                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2696                                     }
2697                                 }
2698                             }
2699                             break;
2700                         case 'radio':
2701                         case 'checkbox':
2702                             if (el.checked) {
2703                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2704                             }
2705                             break;
2706                         case 'file':
2707
2708                         case undefined:
2709
2710                         case 'reset':
2711
2712                         case 'button':
2713
2714                             break;
2715                         case 'submit':
2716                             if(hasSubmit == false) {
2717                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2718                                 hasSubmit = true;
2719                             }
2720                             break;
2721                         default:
2722                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2723                             break;
2724                     }
2725                 }
2726             }
2727             data = data.substr(0, data.length - 1);
2728             return data;
2729         },
2730
2731         headers:{},
2732
2733         hasHeaders:false,
2734
2735         useDefaultHeader:true,
2736
2737         defaultPostHeader:'application/x-www-form-urlencoded',
2738
2739         useDefaultXhrHeader:true,
2740
2741         defaultXhrHeader:'XMLHttpRequest',
2742
2743         hasDefaultHeaders:true,
2744
2745         defaultHeaders:{},
2746
2747         poll:{},
2748
2749         timeout:{},
2750
2751         pollInterval:50,
2752
2753         transactionId:0,
2754
2755         setProgId:function(id)
2756         {
2757             this.activeX.unshift(id);
2758         },
2759
2760         setDefaultPostHeader:function(b)
2761         {
2762             this.useDefaultHeader = b;
2763         },
2764
2765         setDefaultXhrHeader:function(b)
2766         {
2767             this.useDefaultXhrHeader = b;
2768         },
2769
2770         setPollingInterval:function(i)
2771         {
2772             if (typeof i == 'number' && isFinite(i)) {
2773                 this.pollInterval = i;
2774             }
2775         },
2776
2777         createXhrObject:function(transactionId)
2778         {
2779             var obj,http;
2780             try
2781             {
2782
2783                 http = new XMLHttpRequest();
2784
2785                 obj = { conn:http, tId:transactionId };
2786             }
2787             catch(e)
2788             {
2789                 for (var i = 0; i < this.activeX.length; ++i) {
2790                     try
2791                     {
2792
2793                         http = new ActiveXObject(this.activeX[i]);
2794
2795                         obj = { conn:http, tId:transactionId };
2796                         break;
2797                     }
2798                     catch(e) {
2799                     }
2800                 }
2801             }
2802             finally
2803             {
2804                 return obj;
2805             }
2806         },
2807
2808         getConnectionObject:function()
2809         {
2810             var o;
2811             var tId = this.transactionId;
2812
2813             try
2814             {
2815                 o = this.createXhrObject(tId);
2816                 if (o) {
2817                     this.transactionId++;
2818                 }
2819             }
2820             catch(e) {
2821             }
2822             finally
2823             {
2824                 return o;
2825             }
2826         },
2827
2828         asyncRequest:function(method, uri, callback, postData)
2829         {
2830             var o = this.getConnectionObject();
2831
2832             if (!o) {
2833                 return null;
2834             }
2835             else {
2836                 o.conn.open(method, uri, true);
2837
2838                 if (this.useDefaultXhrHeader) {
2839                     if (!this.defaultHeaders['X-Requested-With']) {
2840                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2841                     }
2842                 }
2843
2844                 if(postData && this.useDefaultHeader){
2845                     this.initHeader('Content-Type', this.defaultPostHeader);
2846                 }
2847
2848                  if (this.hasDefaultHeaders || this.hasHeaders) {
2849                     this.setHeader(o);
2850                 }
2851
2852                 this.handleReadyState(o, callback);
2853                 o.conn.send(postData || null);
2854
2855                 return o;
2856             }
2857         },
2858
2859         handleReadyState:function(o, callback)
2860         {
2861             var oConn = this;
2862
2863             if (callback && callback.timeout) {
2864                 
2865                 this.timeout[o.tId] = window.setTimeout(function() {
2866                     oConn.abort(o, callback, true);
2867                 }, callback.timeout);
2868             }
2869
2870             this.poll[o.tId] = window.setInterval(
2871                     function() {
2872                         if (o.conn && o.conn.readyState == 4) {
2873                             window.clearInterval(oConn.poll[o.tId]);
2874                             delete oConn.poll[o.tId];
2875
2876                             if(callback && callback.timeout) {
2877                                 window.clearTimeout(oConn.timeout[o.tId]);
2878                                 delete oConn.timeout[o.tId];
2879                             }
2880
2881                             oConn.handleTransactionResponse(o, callback);
2882                         }
2883                     }
2884                     , this.pollInterval);
2885         },
2886
2887         handleTransactionResponse:function(o, callback, isAbort)
2888         {
2889
2890             if (!callback) {
2891                 this.releaseObject(o);
2892                 return;
2893             }
2894
2895             var httpStatus, responseObject;
2896
2897             try
2898             {
2899                 if (o.conn.status !== undefined && o.conn.status != 0) {
2900                     httpStatus = o.conn.status;
2901                 }
2902                 else {
2903                     httpStatus = 13030;
2904                 }
2905             }
2906             catch(e) {
2907
2908
2909                 httpStatus = 13030;
2910             }
2911
2912             if (httpStatus >= 200 && httpStatus < 300) {
2913                 responseObject = this.createResponseObject(o, callback.argument);
2914                 if (callback.success) {
2915                     if (!callback.scope) {
2916                         callback.success(responseObject);
2917                     }
2918                     else {
2919
2920
2921                         callback.success.apply(callback.scope, [responseObject]);
2922                     }
2923                 }
2924             }
2925             else {
2926                 switch (httpStatus) {
2927
2928                     case 12002:
2929                     case 12029:
2930                     case 12030:
2931                     case 12031:
2932                     case 12152:
2933                     case 13030:
2934                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2935                         if (callback.failure) {
2936                             if (!callback.scope) {
2937                                 callback.failure(responseObject);
2938                             }
2939                             else {
2940                                 callback.failure.apply(callback.scope, [responseObject]);
2941                             }
2942                         }
2943                         break;
2944                     default:
2945                         responseObject = this.createResponseObject(o, callback.argument);
2946                         if (callback.failure) {
2947                             if (!callback.scope) {
2948                                 callback.failure(responseObject);
2949                             }
2950                             else {
2951                                 callback.failure.apply(callback.scope, [responseObject]);
2952                             }
2953                         }
2954                 }
2955             }
2956
2957             this.releaseObject(o);
2958             responseObject = null;
2959         },
2960
2961         createResponseObject:function(o, callbackArg)
2962         {
2963             var obj = {};
2964             var headerObj = {};
2965
2966             try
2967             {
2968                 var headerStr = o.conn.getAllResponseHeaders();
2969                 var header = headerStr.split('\n');
2970                 for (var i = 0; i < header.length; i++) {
2971                     var delimitPos = header[i].indexOf(':');
2972                     if (delimitPos != -1) {
2973                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2974                     }
2975                 }
2976             }
2977             catch(e) {
2978             }
2979
2980             obj.tId = o.tId;
2981             obj.status = o.conn.status;
2982             obj.statusText = o.conn.statusText;
2983             obj.getResponseHeader = headerObj;
2984             obj.getAllResponseHeaders = headerStr;
2985             obj.responseText = o.conn.responseText;
2986             obj.responseXML = o.conn.responseXML;
2987
2988             if (typeof callbackArg !== undefined) {
2989                 obj.argument = callbackArg;
2990             }
2991
2992             return obj;
2993         },
2994
2995         createExceptionObject:function(tId, callbackArg, isAbort)
2996         {
2997             var COMM_CODE = 0;
2998             var COMM_ERROR = 'communication failure';
2999             var ABORT_CODE = -1;
3000             var ABORT_ERROR = 'transaction aborted';
3001
3002             var obj = {};
3003
3004             obj.tId = tId;
3005             if (isAbort) {
3006                 obj.status = ABORT_CODE;
3007                 obj.statusText = ABORT_ERROR;
3008             }
3009             else {
3010                 obj.status = COMM_CODE;
3011                 obj.statusText = COMM_ERROR;
3012             }
3013
3014             if (callbackArg) {
3015                 obj.argument = callbackArg;
3016             }
3017
3018             return obj;
3019         },
3020
3021         initHeader:function(label, value, isDefault)
3022         {
3023             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3024
3025             if (headerObj[label] === undefined) {
3026                 headerObj[label] = value;
3027             }
3028             else {
3029
3030
3031                 headerObj[label] = value + "," + headerObj[label];
3032             }
3033
3034             if (isDefault) {
3035                 this.hasDefaultHeaders = true;
3036             }
3037             else {
3038                 this.hasHeaders = true;
3039             }
3040         },
3041
3042
3043         setHeader:function(o)
3044         {
3045             if (this.hasDefaultHeaders) {
3046                 for (var prop in this.defaultHeaders) {
3047                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3048                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3049                     }
3050                 }
3051             }
3052
3053             if (this.hasHeaders) {
3054                 for (var prop in this.headers) {
3055                     if (this.headers.hasOwnProperty(prop)) {
3056                         o.conn.setRequestHeader(prop, this.headers[prop]);
3057                     }
3058                 }
3059                 this.headers = {};
3060                 this.hasHeaders = false;
3061             }
3062         },
3063
3064         resetDefaultHeaders:function() {
3065             delete this.defaultHeaders;
3066             this.defaultHeaders = {};
3067             this.hasDefaultHeaders = false;
3068         },
3069
3070         abort:function(o, callback, isTimeout)
3071         {
3072             if(this.isCallInProgress(o)) {
3073                 o.conn.abort();
3074                 window.clearInterval(this.poll[o.tId]);
3075                 delete this.poll[o.tId];
3076                 if (isTimeout) {
3077                     delete this.timeout[o.tId];
3078                 }
3079
3080                 this.handleTransactionResponse(o, callback, true);
3081
3082                 return true;
3083             }
3084             else {
3085                 return false;
3086             }
3087         },
3088
3089
3090         isCallInProgress:function(o)
3091         {
3092             if (o && o.conn) {
3093                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3094             }
3095             else {
3096
3097                 return false;
3098             }
3099         },
3100
3101
3102         releaseObject:function(o)
3103         {
3104
3105             o.conn = null;
3106
3107             o = null;
3108         },
3109
3110         activeX:[
3111         'MSXML2.XMLHTTP.3.0',
3112         'MSXML2.XMLHTTP',
3113         'Microsoft.XMLHTTP'
3114         ]
3115
3116
3117     };
3118 })();/*
3119  * Portions of this file are based on pieces of Yahoo User Interface Library
3120  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3121  * YUI licensed under the BSD License:
3122  * http://developer.yahoo.net/yui/license.txt
3123  * <script type="text/javascript">
3124  *
3125  */
3126
3127 Roo.lib.Region = function(t, r, b, l) {
3128     this.top = t;
3129     this[1] = t;
3130     this.right = r;
3131     this.bottom = b;
3132     this.left = l;
3133     this[0] = l;
3134 };
3135
3136
3137 Roo.lib.Region.prototype = {
3138     contains : function(region) {
3139         return ( region.left >= this.left &&
3140                  region.right <= this.right &&
3141                  region.top >= this.top &&
3142                  region.bottom <= this.bottom    );
3143
3144     },
3145
3146     getArea : function() {
3147         return ( (this.bottom - this.top) * (this.right - this.left) );
3148     },
3149
3150     intersect : function(region) {
3151         var t = Math.max(this.top, region.top);
3152         var r = Math.min(this.right, region.right);
3153         var b = Math.min(this.bottom, region.bottom);
3154         var l = Math.max(this.left, region.left);
3155
3156         if (b >= t && r >= l) {
3157             return new Roo.lib.Region(t, r, b, l);
3158         } else {
3159             return null;
3160         }
3161     },
3162     union : function(region) {
3163         var t = Math.min(this.top, region.top);
3164         var r = Math.max(this.right, region.right);
3165         var b = Math.max(this.bottom, region.bottom);
3166         var l = Math.min(this.left, region.left);
3167
3168         return new Roo.lib.Region(t, r, b, l);
3169     },
3170
3171     adjust : function(t, l, b, r) {
3172         this.top += t;
3173         this.left += l;
3174         this.right += r;
3175         this.bottom += b;
3176         return this;
3177     }
3178 };
3179
3180 Roo.lib.Region.getRegion = function(el) {
3181     var p = Roo.lib.Dom.getXY(el);
3182
3183     var t = p[1];
3184     var r = p[0] + el.offsetWidth;
3185     var b = p[1] + el.offsetHeight;
3186     var l = p[0];
3187
3188     return new Roo.lib.Region(t, r, b, l);
3189 };
3190 /*
3191  * Portions of this file are based on pieces of Yahoo User Interface Library
3192  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3193  * YUI licensed under the BSD License:
3194  * http://developer.yahoo.net/yui/license.txt
3195  * <script type="text/javascript">
3196  *
3197  */
3198 //@@dep Roo.lib.Region
3199
3200
3201 Roo.lib.Point = function(x, y) {
3202     if (x instanceof Array) {
3203         y = x[1];
3204         x = x[0];
3205     }
3206     this.x = this.right = this.left = this[0] = x;
3207     this.y = this.top = this.bottom = this[1] = y;
3208 };
3209
3210 Roo.lib.Point.prototype = new Roo.lib.Region();
3211 /*
3212  * Portions of this file are based on pieces of Yahoo User Interface Library
3213  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3214  * YUI licensed under the BSD License:
3215  * http://developer.yahoo.net/yui/license.txt
3216  * <script type="text/javascript">
3217  *
3218  */
3219  
3220 (function() {   
3221
3222     Roo.lib.Anim = {
3223         scroll : function(el, args, duration, easing, cb, scope) {
3224             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3225         },
3226
3227         motion : function(el, args, duration, easing, cb, scope) {
3228             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3229         },
3230
3231         color : function(el, args, duration, easing, cb, scope) {
3232             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3233         },
3234
3235         run : function(el, args, duration, easing, cb, scope, type) {
3236             type = type || Roo.lib.AnimBase;
3237             if (typeof easing == "string") {
3238                 easing = Roo.lib.Easing[easing];
3239             }
3240             var anim = new type(el, args, duration, easing);
3241             anim.animateX(function() {
3242                 Roo.callback(cb, scope);
3243             });
3244             return anim;
3245         }
3246     };
3247 })();/*
3248  * Portions of this file are based on pieces of Yahoo User Interface Library
3249  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250  * YUI licensed under the BSD License:
3251  * http://developer.yahoo.net/yui/license.txt
3252  * <script type="text/javascript">
3253  *
3254  */
3255
3256 (function() {    
3257     var libFlyweight;
3258     
3259     function fly(el) {
3260         if (!libFlyweight) {
3261             libFlyweight = new Roo.Element.Flyweight();
3262         }
3263         libFlyweight.dom = el;
3264         return libFlyweight;
3265     }
3266
3267     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3268     
3269    
3270     
3271     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3272         if (el) {
3273             this.init(el, attributes, duration, method);
3274         }
3275     };
3276
3277     Roo.lib.AnimBase.fly = fly;
3278     
3279     
3280     
3281     Roo.lib.AnimBase.prototype = {
3282
3283         toString: function() {
3284             var el = this.getEl();
3285             var id = el.id || el.tagName;
3286             return ("Anim " + id);
3287         },
3288
3289         patterns: {
3290             noNegatives:        /width|height|opacity|padding/i,
3291             offsetAttribute:  /^((width|height)|(top|left))$/,
3292             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3293             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3294         },
3295
3296
3297         doMethod: function(attr, start, end) {
3298             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3299         },
3300
3301
3302         setAttribute: function(attr, val, unit) {
3303             if (this.patterns.noNegatives.test(attr)) {
3304                 val = (val > 0) ? val : 0;
3305             }
3306
3307             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3308         },
3309
3310
3311         getAttribute: function(attr) {
3312             var el = this.getEl();
3313             var val = fly(el).getStyle(attr);
3314
3315             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3316                 return parseFloat(val);
3317             }
3318
3319             var a = this.patterns.offsetAttribute.exec(attr) || [];
3320             var pos = !!( a[3] );
3321             var box = !!( a[2] );
3322
3323
3324             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3325                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3326             } else {
3327                 val = 0;
3328             }
3329
3330             return val;
3331         },
3332
3333
3334         getDefaultUnit: function(attr) {
3335             if (this.patterns.defaultUnit.test(attr)) {
3336                 return 'px';
3337             }
3338
3339             return '';
3340         },
3341
3342         animateX : function(callback, scope) {
3343             var f = function() {
3344                 this.onComplete.removeListener(f);
3345                 if (typeof callback == "function") {
3346                     callback.call(scope || this, this);
3347                 }
3348             };
3349             this.onComplete.addListener(f, this);
3350             this.animate();
3351         },
3352
3353
3354         setRuntimeAttribute: function(attr) {
3355             var start;
3356             var end;
3357             var attributes = this.attributes;
3358
3359             this.runtimeAttributes[attr] = {};
3360
3361             var isset = function(prop) {
3362                 return (typeof prop !== 'undefined');
3363             };
3364
3365             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3366                 return false;
3367             }
3368
3369             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3370
3371
3372             if (isset(attributes[attr]['to'])) {
3373                 end = attributes[attr]['to'];
3374             } else if (isset(attributes[attr]['by'])) {
3375                 if (start.constructor == Array) {
3376                     end = [];
3377                     for (var i = 0, len = start.length; i < len; ++i) {
3378                         end[i] = start[i] + attributes[attr]['by'][i];
3379                     }
3380                 } else {
3381                     end = start + attributes[attr]['by'];
3382                 }
3383             }
3384
3385             this.runtimeAttributes[attr].start = start;
3386             this.runtimeAttributes[attr].end = end;
3387
3388
3389             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3390         },
3391
3392
3393         init: function(el, attributes, duration, method) {
3394
3395             var isAnimated = false;
3396
3397
3398             var startTime = null;
3399
3400
3401             var actualFrames = 0;
3402
3403
3404             el = Roo.getDom(el);
3405
3406
3407             this.attributes = attributes || {};
3408
3409
3410             this.duration = duration || 1;
3411
3412
3413             this.method = method || Roo.lib.Easing.easeNone;
3414
3415
3416             this.useSeconds = true;
3417
3418
3419             this.currentFrame = 0;
3420
3421
3422             this.totalFrames = Roo.lib.AnimMgr.fps;
3423
3424
3425             this.getEl = function() {
3426                 return el;
3427             };
3428
3429
3430             this.isAnimated = function() {
3431                 return isAnimated;
3432             };
3433
3434
3435             this.getStartTime = function() {
3436                 return startTime;
3437             };
3438
3439             this.runtimeAttributes = {};
3440
3441
3442             this.animate = function() {
3443                 if (this.isAnimated()) {
3444                     return false;
3445                 }
3446
3447                 this.currentFrame = 0;
3448
3449                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3450
3451                 Roo.lib.AnimMgr.registerElement(this);
3452             };
3453
3454
3455             this.stop = function(finish) {
3456                 if (finish) {
3457                     this.currentFrame = this.totalFrames;
3458                     this._onTween.fire();
3459                 }
3460                 Roo.lib.AnimMgr.stop(this);
3461             };
3462
3463             var onStart = function() {
3464                 this.onStart.fire();
3465
3466                 this.runtimeAttributes = {};
3467                 for (var attr in this.attributes) {
3468                     this.setRuntimeAttribute(attr);
3469                 }
3470
3471                 isAnimated = true;
3472                 actualFrames = 0;
3473                 startTime = new Date();
3474             };
3475
3476
3477             var onTween = function() {
3478                 var data = {
3479                     duration: new Date() - this.getStartTime(),
3480                     currentFrame: this.currentFrame
3481                 };
3482
3483                 data.toString = function() {
3484                     return (
3485                             'duration: ' + data.duration +
3486                             ', currentFrame: ' + data.currentFrame
3487                             );
3488                 };
3489
3490                 this.onTween.fire(data);
3491
3492                 var runtimeAttributes = this.runtimeAttributes;
3493
3494                 for (var attr in runtimeAttributes) {
3495                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3496                 }
3497
3498                 actualFrames += 1;
3499             };
3500
3501             var onComplete = function() {
3502                 var actual_duration = (new Date() - startTime) / 1000 ;
3503
3504                 var data = {
3505                     duration: actual_duration,
3506                     frames: actualFrames,
3507                     fps: actualFrames / actual_duration
3508                 };
3509
3510                 data.toString = function() {
3511                     return (
3512                             'duration: ' + data.duration +
3513                             ', frames: ' + data.frames +
3514                             ', fps: ' + data.fps
3515                             );
3516                 };
3517
3518                 isAnimated = false;
3519                 actualFrames = 0;
3520                 this.onComplete.fire(data);
3521             };
3522
3523
3524             this._onStart = new Roo.util.Event(this);
3525             this.onStart = new Roo.util.Event(this);
3526             this.onTween = new Roo.util.Event(this);
3527             this._onTween = new Roo.util.Event(this);
3528             this.onComplete = new Roo.util.Event(this);
3529             this._onComplete = new Roo.util.Event(this);
3530             this._onStart.addListener(onStart);
3531             this._onTween.addListener(onTween);
3532             this._onComplete.addListener(onComplete);
3533         }
3534     };
3535 })();
3536 /*
3537  * Portions of this file are based on pieces of Yahoo User Interface Library
3538  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3539  * YUI licensed under the BSD License:
3540  * http://developer.yahoo.net/yui/license.txt
3541  * <script type="text/javascript">
3542  *
3543  */
3544
3545 Roo.lib.AnimMgr = new function() {
3546
3547     var thread = null;
3548
3549
3550     var queue = [];
3551
3552
3553     var tweenCount = 0;
3554
3555
3556     this.fps = 1000;
3557
3558
3559     this.delay = 1;
3560
3561
3562     this.registerElement = function(tween) {
3563         queue[queue.length] = tween;
3564         tweenCount += 1;
3565         tween._onStart.fire();
3566         this.start();
3567     };
3568
3569
3570     this.unRegister = function(tween, index) {
3571         tween._onComplete.fire();
3572         index = index || getIndex(tween);
3573         if (index != -1) {
3574             queue.splice(index, 1);
3575         }
3576
3577         tweenCount -= 1;
3578         if (tweenCount <= 0) {
3579             this.stop();
3580         }
3581     };
3582
3583
3584     this.start = function() {
3585         if (thread === null) {
3586             thread = setInterval(this.run, this.delay);
3587         }
3588     };
3589
3590
3591     this.stop = function(tween) {
3592         if (!tween) {
3593             clearInterval(thread);
3594
3595             for (var i = 0, len = queue.length; i < len; ++i) {
3596                 if (queue[0].isAnimated()) {
3597                     this.unRegister(queue[0], 0);
3598                 }
3599             }
3600
3601             queue = [];
3602             thread = null;
3603             tweenCount = 0;
3604         }
3605         else {
3606             this.unRegister(tween);
3607         }
3608     };
3609
3610
3611     this.run = function() {
3612         for (var i = 0, len = queue.length; i < len; ++i) {
3613             var tween = queue[i];
3614             if (!tween || !tween.isAnimated()) {
3615                 continue;
3616             }
3617
3618             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3619             {
3620                 tween.currentFrame += 1;
3621
3622                 if (tween.useSeconds) {
3623                     correctFrame(tween);
3624                 }
3625                 tween._onTween.fire();
3626             }
3627             else {
3628                 Roo.lib.AnimMgr.stop(tween, i);
3629             }
3630         }
3631     };
3632
3633     var getIndex = function(anim) {
3634         for (var i = 0, len = queue.length; i < len; ++i) {
3635             if (queue[i] == anim) {
3636                 return i;
3637             }
3638         }
3639         return -1;
3640     };
3641
3642
3643     var correctFrame = function(tween) {
3644         var frames = tween.totalFrames;
3645         var frame = tween.currentFrame;
3646         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3647         var elapsed = (new Date() - tween.getStartTime());
3648         var tweak = 0;
3649
3650         if (elapsed < tween.duration * 1000) {
3651             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3652         } else {
3653             tweak = frames - (frame + 1);
3654         }
3655         if (tweak > 0 && isFinite(tweak)) {
3656             if (tween.currentFrame + tweak >= frames) {
3657                 tweak = frames - (frame + 1);
3658             }
3659
3660             tween.currentFrame += tweak;
3661         }
3662     };
3663 };
3664
3665     /*
3666  * Portions of this file are based on pieces of Yahoo User Interface Library
3667  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3668  * YUI licensed under the BSD License:
3669  * http://developer.yahoo.net/yui/license.txt
3670  * <script type="text/javascript">
3671  *
3672  */
3673 Roo.lib.Bezier = new function() {
3674
3675         this.getPosition = function(points, t) {
3676             var n = points.length;
3677             var tmp = [];
3678
3679             for (var i = 0; i < n; ++i) {
3680                 tmp[i] = [points[i][0], points[i][1]];
3681             }
3682
3683             for (var j = 1; j < n; ++j) {
3684                 for (i = 0; i < n - j; ++i) {
3685                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3686                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3687                 }
3688             }
3689
3690             return [ tmp[0][0], tmp[0][1] ];
3691
3692         };
3693     }; 
3694
3695 /**
3696  * @class Roo.lib.Color
3697  * @constructor
3698  * An abstract Color implementation. Concrete Color implementations should use
3699  * an instance of this function as their prototype, and implement the getRGB and
3700  * getHSL functions. getRGB should return an object representing the RGB
3701  * components of this Color, with the red, green, and blue components in the
3702  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3703  * return an object representing the HSL components of this Color, with the hue
3704  * component in the range [0,360), the saturation and lightness components in
3705  * the range [0,100], and the alpha component in the range [0,1].
3706  *
3707  *
3708  * Color.js
3709  *
3710  * Functions for Color handling and processing.
3711  *
3712  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3713  *
3714  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3715  * rights to this program, with the intention of it becoming part of the public
3716  * domain. Because this program is released into the public domain, it comes with
3717  * no warranty either expressed or implied, to the extent permitted by law.
3718  * 
3719  * For more free and public domain JavaScript code by the same author, visit:
3720  * http://www.safalra.com/web-design/javascript/
3721  * 
3722  */
3723 Roo.lib.Color = function() { }
3724
3725
3726 Roo.apply(Roo.lib.Color.prototype, {
3727   
3728   rgb : null,
3729   hsv : null,
3730   hsl : null,
3731   
3732   /**
3733    * getIntegerRGB
3734    * @return {Object} an object representing the RGBA components of this Color. The red,
3735    * green, and blue components are converted to integers in the range [0,255].
3736    * The alpha is a value in the range [0,1].
3737    */
3738   getIntegerRGB : function(){
3739
3740     // get the RGB components of this Color
3741     var rgb = this.getRGB();
3742
3743     // return the integer components
3744     return {
3745       'r' : Math.round(rgb.r),
3746       'g' : Math.round(rgb.g),
3747       'b' : Math.round(rgb.b),
3748       'a' : rgb.a
3749     };
3750
3751   },
3752
3753   /**
3754    * getPercentageRGB
3755    * @return {Object} an object representing the RGBA components of this Color. The red,
3756    * green, and blue components are converted to numbers in the range [0,100].
3757    * The alpha is a value in the range [0,1].
3758    */
3759   getPercentageRGB : function(){
3760
3761     // get the RGB components of this Color
3762     var rgb = this.getRGB();
3763
3764     // return the percentage components
3765     return {
3766       'r' : 100 * rgb.r / 255,
3767       'g' : 100 * rgb.g / 255,
3768       'b' : 100 * rgb.b / 255,
3769       'a' : rgb.a
3770     };
3771
3772   },
3773
3774   /**
3775    * getCSSHexadecimalRGB
3776    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3777    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3778    * are two-digit hexadecimal numbers.
3779    */
3780   getCSSHexadecimalRGB : function()
3781   {
3782
3783     // get the integer RGB components
3784     var rgb = this.getIntegerRGB();
3785
3786     // determine the hexadecimal equivalents
3787     var r16 = rgb.r.toString(16);
3788     var g16 = rgb.g.toString(16);
3789     var b16 = rgb.b.toString(16);
3790
3791     // return the CSS RGB Color value
3792     return '#'
3793         + (r16.length == 2 ? r16 : '0' + r16)
3794         + (g16.length == 2 ? g16 : '0' + g16)
3795         + (b16.length == 2 ? b16 : '0' + b16);
3796
3797   },
3798
3799   /**
3800    * getCSSIntegerRGB
3801    * @return {String} a string representing this Color as a CSS integer RGB Color
3802    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3803    * are integers in the range [0,255].
3804    */
3805   getCSSIntegerRGB : function(){
3806
3807     // get the integer RGB components
3808     var rgb = this.getIntegerRGB();
3809
3810     // return the CSS RGB Color value
3811     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3812
3813   },
3814
3815   /**
3816    * getCSSIntegerRGBA
3817    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3818    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3819    * b are integers in the range [0,255] and a is in the range [0,1].
3820    */
3821   getCSSIntegerRGBA : function(){
3822
3823     // get the integer RGB components
3824     var rgb = this.getIntegerRGB();
3825
3826     // return the CSS integer RGBA Color value
3827     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3828
3829   },
3830
3831   /**
3832    * getCSSPercentageRGB
3833    * @return {String} a string representing this Color as a CSS percentage RGB Color
3834    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3835    * b are in the range [0,100].
3836    */
3837   getCSSPercentageRGB : function(){
3838
3839     // get the percentage RGB components
3840     var rgb = this.getPercentageRGB();
3841
3842     // return the CSS RGB Color value
3843     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3844
3845   },
3846
3847   /**
3848    * getCSSPercentageRGBA
3849    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3850    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3851    * and b are in the range [0,100] and a is in the range [0,1].
3852    */
3853   getCSSPercentageRGBA : function(){
3854
3855     // get the percentage RGB components
3856     var rgb = this.getPercentageRGB();
3857
3858     // return the CSS percentage RGBA Color value
3859     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3860
3861   },
3862
3863   /**
3864    * getCSSHSL
3865    * @return {String} a string representing this Color as a CSS HSL Color value - that
3866    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3867    * s and l are in the range [0,100].
3868    */
3869   getCSSHSL : function(){
3870
3871     // get the HSL components
3872     var hsl = this.getHSL();
3873
3874     // return the CSS HSL Color value
3875     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3876
3877   },
3878
3879   /**
3880    * getCSSHSLA
3881    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3882    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3883    * s and l are in the range [0,100], and a is in the range [0,1].
3884    */
3885   getCSSHSLA : function(){
3886
3887     // get the HSL components
3888     var hsl = this.getHSL();
3889
3890     // return the CSS HSL Color value
3891     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3892
3893   },
3894
3895   /**
3896    * Sets the Color of the specified node to this Color. This functions sets
3897    * the CSS 'color' property for the node. The parameter is:
3898    * 
3899    * @param {DomElement} node - the node whose Color should be set
3900    */
3901   setNodeColor : function(node){
3902
3903     // set the Color of the node
3904     node.style.color = this.getCSSHexadecimalRGB();
3905
3906   },
3907
3908   /**
3909    * Sets the background Color of the specified node to this Color. This
3910    * functions sets the CSS 'background-color' property for the node. The
3911    * parameter is:
3912    *
3913    * @param {DomElement} node - the node whose background Color should be set
3914    */
3915   setNodeBackgroundColor : function(node){
3916
3917     // set the background Color of the node
3918     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3919
3920   },
3921   // convert between formats..
3922   toRGB: function()
3923   {
3924     var r = this.getIntegerRGB();
3925     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3926     
3927   },
3928   toHSL : function()
3929   {
3930      var hsl = this.getHSL();
3931   // return the CSS HSL Color value
3932     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3933     
3934   },
3935   
3936   toHSV : function()
3937   {
3938     var rgb = this.toRGB();
3939     var hsv = rgb.getHSV();
3940    // return the CSS HSL Color value
3941     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3942     
3943   },
3944   
3945   // modify  v = 0 ... 1 (eg. 0.5)
3946   saturate : function(v)
3947   {
3948       var rgb = this.toRGB();
3949       var hsv = rgb.getHSV();
3950       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3951       
3952   
3953   },
3954   
3955    
3956   /**
3957    * getRGB
3958    * @return {Object} the RGB and alpha components of this Color as an object with r,
3959    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3960    * the range [0,1].
3961    */
3962   getRGB: function(){
3963    
3964     // return the RGB components
3965     return {
3966       'r' : this.rgb.r,
3967       'g' : this.rgb.g,
3968       'b' : this.rgb.b,
3969       'a' : this.alpha
3970     };
3971
3972   },
3973
3974   /**
3975    * getHSV
3976    * @return {Object} the HSV and alpha components of this Color as an object with h,
3977    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3978    * [0,100], and a is in the range [0,1].
3979    */
3980   getHSV : function()
3981   {
3982     
3983     // calculate the HSV components if necessary
3984     if (this.hsv == null) {
3985       this.calculateHSV();
3986     }
3987
3988     // return the HSV components
3989     return {
3990       'h' : this.hsv.h,
3991       's' : this.hsv.s,
3992       'v' : this.hsv.v,
3993       'a' : this.alpha
3994     };
3995
3996   },
3997
3998   /**
3999    * getHSL
4000    * @return {Object} the HSL and alpha components of this Color as an object with h,
4001    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4002    * [0,100], and a is in the range [0,1].
4003    */
4004   getHSL : function(){
4005     
4006      
4007     // calculate the HSV components if necessary
4008     if (this.hsl == null) { this.calculateHSL(); }
4009
4010     // return the HSL components
4011     return {
4012       'h' : this.hsl.h,
4013       's' : this.hsl.s,
4014       'l' : this.hsl.l,
4015       'a' : this.alpha
4016     };
4017
4018   }
4019   
4020
4021 });
4022
4023
4024 /**
4025  * @class Roo.lib.RGBColor
4026  * @extends Roo.lib.Color
4027  * Creates a Color specified in the RGB Color space, with an optional alpha
4028  * component. The parameters are:
4029  * @constructor
4030  * 
4031
4032  * @param {Number} r - the red component, clipped to the range [0,255]
4033  * @param {Number} g - the green component, clipped to the range [0,255]
4034  * @param {Number} b - the blue component, clipped to the range [0,255]
4035  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4036  *     optional and defaults to 1
4037  */
4038 Roo.lib.RGBColor = function (r, g, b, a){
4039
4040   // store the alpha component after clipping it if necessary
4041   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4042
4043   // store the RGB components after clipping them if necessary
4044   this.rgb =
4045       {
4046         'r' : Math.max(0, Math.min(255, r)),
4047         'g' : Math.max(0, Math.min(255, g)),
4048         'b' : Math.max(0, Math.min(255, b))
4049       };
4050
4051   // initialise the HSV and HSL components to null
4052   
4053
4054   /* 
4055    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4056    * range [0,360). The parameters are:
4057    *
4058    * maximum - the maximum of the RGB component values
4059    * range   - the range of the RGB component values
4060    */
4061    
4062
4063 }
4064 // this does an 'exteds'
4065 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4066
4067   
4068     getHue  : function(maximum, range)
4069     {
4070       var rgb = this.rgb;
4071        
4072       // check whether the range is zero
4073       if (range == 0){
4074   
4075         // set the hue to zero (any hue is acceptable as the Color is grey)
4076         var hue = 0;
4077   
4078       }else{
4079   
4080         // determine which of the components has the highest value and set the hue
4081         switch (maximum){
4082   
4083           // red has the highest value
4084           case rgb.r:
4085             var hue = (rgb.g - rgb.b) / range * 60;
4086             if (hue < 0) { hue += 360; }
4087             break;
4088   
4089           // green has the highest value
4090           case rgb.g:
4091             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4092             break;
4093   
4094           // blue has the highest value
4095           case rgb.b:
4096             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4097             break;
4098   
4099         }
4100   
4101       }
4102   
4103       // return the hue
4104       return hue;
4105   
4106     },
4107
4108   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4109    * be returned be the getHSV function.
4110    */
4111    calculateHSV : function(){
4112     var rgb = this.rgb;
4113     // get the maximum and range of the RGB component values
4114     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4115     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4116
4117     // store the HSV components
4118     this.hsv =
4119         {
4120           'h' : this.getHue(maximum, range),
4121           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4122           'v' : maximum / 2.55
4123         };
4124
4125   },
4126
4127   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4128    * be returned be the getHSL function.
4129    */
4130    calculateHSL : function(){
4131     var rgb = this.rgb;
4132     // get the maximum and range of the RGB component values
4133     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4134     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4135
4136     // determine the lightness in the range [0,1]
4137     var l = maximum / 255 - range / 510;
4138
4139     // store the HSL components
4140     this.hsl =
4141         {
4142           'h' : this.getHue(maximum, range),
4143           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4144           'l' : 100 * l
4145         };
4146
4147   }
4148
4149 });
4150
4151 /**
4152  * @class Roo.lib.HSVColor
4153  * @extends Roo.lib.Color
4154  * Creates a Color specified in the HSV Color space, with an optional alpha
4155  * component. The parameters are:
4156  * @constructor
4157  *
4158  * @param {Number} h - the hue component, wrapped to the range [0,360)
4159  * @param {Number} s - the saturation component, clipped to the range [0,100]
4160  * @param {Number} v - the value component, clipped to the range [0,100]
4161  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4162  *     optional and defaults to 1
4163  */
4164 Roo.lib.HSVColor = function (h, s, v, a){
4165
4166   // store the alpha component after clipping it if necessary
4167   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4168
4169   // store the HSV components after clipping or wrapping them if necessary
4170   this.hsv =
4171       {
4172         'h' : (h % 360 + 360) % 360,
4173         's' : Math.max(0, Math.min(100, s)),
4174         'v' : Math.max(0, Math.min(100, v))
4175       };
4176
4177   // initialise the RGB and HSL components to null
4178   this.rgb = null;
4179   this.hsl = null;
4180 }
4181
4182 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4183   /* Calculates and stores the RGB components of this HSVColor so that they can
4184    * be returned be the getRGB function.
4185    */
4186   calculateRGB: function ()
4187   {
4188     var hsv = this.hsv;
4189     // check whether the saturation is zero
4190     if (hsv.s == 0){
4191
4192       // set the Color to the appropriate shade of grey
4193       var r = hsv.v;
4194       var g = hsv.v;
4195       var b = hsv.v;
4196
4197     }else{
4198
4199       // set some temporary values
4200       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4201       var p  = hsv.v * (1 - hsv.s / 100);
4202       var q  = hsv.v * (1 - hsv.s / 100 * f);
4203       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4204
4205       // set the RGB Color components to their temporary values
4206       switch (Math.floor(hsv.h / 60)){
4207         case 0: var r = hsv.v; var g = t; var b = p; break;
4208         case 1: var r = q; var g = hsv.v; var b = p; break;
4209         case 2: var r = p; var g = hsv.v; var b = t; break;
4210         case 3: var r = p; var g = q; var b = hsv.v; break;
4211         case 4: var r = t; var g = p; var b = hsv.v; break;
4212         case 5: var r = hsv.v; var g = p; var b = q; break;
4213       }
4214
4215     }
4216
4217     // store the RGB components
4218     this.rgb =
4219         {
4220           'r' : r * 2.55,
4221           'g' : g * 2.55,
4222           'b' : b * 2.55
4223         };
4224
4225   },
4226
4227   /* Calculates and stores the HSL components of this HSVColor so that they can
4228    * be returned be the getHSL function.
4229    */
4230   calculateHSL : function (){
4231
4232     var hsv = this.hsv;
4233     // determine the lightness in the range [0,100]
4234     var l = (2 - hsv.s / 100) * hsv.v / 2;
4235
4236     // store the HSL components
4237     this.hsl =
4238         {
4239           'h' : hsv.h,
4240           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4241           'l' : l
4242         };
4243
4244     // correct a division-by-zero error
4245     if (isNaN(hsl.s)) { hsl.s = 0; }
4246
4247   } 
4248  
4249
4250 });
4251  
4252
4253 /**
4254  * @class Roo.lib.HSLColor
4255  * @extends Roo.lib.Color
4256  *
4257  * @constructor
4258  * Creates a Color specified in the HSL Color space, with an optional alpha
4259  * component. The parameters are:
4260  *
4261  * @param {Number} h - the hue component, wrapped to the range [0,360)
4262  * @param {Number} s - the saturation component, clipped to the range [0,100]
4263  * @param {Number} l - the lightness component, clipped to the range [0,100]
4264  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4265  *     optional and defaults to 1
4266  */
4267
4268 Roo.lib.HSLColor = function(h, s, l, a){
4269
4270   // store the alpha component after clipping it if necessary
4271   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4272
4273   // store the HSL components after clipping or wrapping them if necessary
4274   this.hsl =
4275       {
4276         'h' : (h % 360 + 360) % 360,
4277         's' : Math.max(0, Math.min(100, s)),
4278         'l' : Math.max(0, Math.min(100, l))
4279       };
4280
4281   // initialise the RGB and HSV components to null
4282 }
4283
4284 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4285
4286   /* Calculates and stores the RGB components of this HSLColor so that they can
4287    * be returned be the getRGB function.
4288    */
4289   calculateRGB: function (){
4290
4291     // check whether the saturation is zero
4292     if (this.hsl.s == 0){
4293
4294       // store the RGB components representing the appropriate shade of grey
4295       this.rgb =
4296           {
4297             'r' : this.hsl.l * 2.55,
4298             'g' : this.hsl.l * 2.55,
4299             'b' : this.hsl.l * 2.55
4300           };
4301
4302     }else{
4303
4304       // set some temporary values
4305       var p = this.hsl.l < 50
4306             ? this.hsl.l * (1 + hsl.s / 100)
4307             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4308       var q = 2 * hsl.l - p;
4309
4310       // initialise the RGB components
4311       this.rgb =
4312           {
4313             'r' : (h + 120) / 60 % 6,
4314             'g' : h / 60,
4315             'b' : (h + 240) / 60 % 6
4316           };
4317
4318       // loop over the RGB components
4319       for (var key in this.rgb){
4320
4321         // ensure that the property is not inherited from the root object
4322         if (this.rgb.hasOwnProperty(key)){
4323
4324           // set the component to its value in the range [0,100]
4325           if (this.rgb[key] < 1){
4326             this.rgb[key] = q + (p - q) * this.rgb[key];
4327           }else if (this.rgb[key] < 3){
4328             this.rgb[key] = p;
4329           }else if (this.rgb[key] < 4){
4330             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4331           }else{
4332             this.rgb[key] = q;
4333           }
4334
4335           // set the component to its value in the range [0,255]
4336           this.rgb[key] *= 2.55;
4337
4338         }
4339
4340       }
4341
4342     }
4343
4344   },
4345
4346   /* Calculates and stores the HSV components of this HSLColor so that they can
4347    * be returned be the getHSL function.
4348    */
4349    calculateHSV : function(){
4350
4351     // set a temporary value
4352     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4353
4354     // store the HSV components
4355     this.hsv =
4356         {
4357           'h' : this.hsl.h,
4358           's' : 200 * t / (this.hsl.l + t),
4359           'v' : t + this.hsl.l
4360         };
4361
4362     // correct a division-by-zero error
4363     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4364
4365   }
4366  
4367
4368 });
4369 /*
4370  * Portions of this file are based on pieces of Yahoo User Interface Library
4371  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4372  * YUI licensed under the BSD License:
4373  * http://developer.yahoo.net/yui/license.txt
4374  * <script type="text/javascript">
4375  *
4376  */
4377 (function() {
4378
4379     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4380         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4381     };
4382
4383     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4384
4385     var fly = Roo.lib.AnimBase.fly;
4386     var Y = Roo.lib;
4387     var superclass = Y.ColorAnim.superclass;
4388     var proto = Y.ColorAnim.prototype;
4389
4390     proto.toString = function() {
4391         var el = this.getEl();
4392         var id = el.id || el.tagName;
4393         return ("ColorAnim " + id);
4394     };
4395
4396     proto.patterns.color = /color$/i;
4397     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4398     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4399     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4400     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4401
4402
4403     proto.parseColor = function(s) {
4404         if (s.length == 3) {
4405             return s;
4406         }
4407
4408         var c = this.patterns.hex.exec(s);
4409         if (c && c.length == 4) {
4410             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4411         }
4412
4413         c = this.patterns.rgb.exec(s);
4414         if (c && c.length == 4) {
4415             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4416         }
4417
4418         c = this.patterns.hex3.exec(s);
4419         if (c && c.length == 4) {
4420             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4421         }
4422
4423         return null;
4424     };
4425     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4426     proto.getAttribute = function(attr) {
4427         var el = this.getEl();
4428         if (this.patterns.color.test(attr)) {
4429             var val = fly(el).getStyle(attr);
4430
4431             if (this.patterns.transparent.test(val)) {
4432                 var parent = el.parentNode;
4433                 val = fly(parent).getStyle(attr);
4434
4435                 while (parent && this.patterns.transparent.test(val)) {
4436                     parent = parent.parentNode;
4437                     val = fly(parent).getStyle(attr);
4438                     if (parent.tagName.toUpperCase() == 'HTML') {
4439                         val = '#fff';
4440                     }
4441                 }
4442             }
4443         } else {
4444             val = superclass.getAttribute.call(this, attr);
4445         }
4446
4447         return val;
4448     };
4449     proto.getAttribute = function(attr) {
4450         var el = this.getEl();
4451         if (this.patterns.color.test(attr)) {
4452             var val = fly(el).getStyle(attr);
4453
4454             if (this.patterns.transparent.test(val)) {
4455                 var parent = el.parentNode;
4456                 val = fly(parent).getStyle(attr);
4457
4458                 while (parent && this.patterns.transparent.test(val)) {
4459                     parent = parent.parentNode;
4460                     val = fly(parent).getStyle(attr);
4461                     if (parent.tagName.toUpperCase() == 'HTML') {
4462                         val = '#fff';
4463                     }
4464                 }
4465             }
4466         } else {
4467             val = superclass.getAttribute.call(this, attr);
4468         }
4469
4470         return val;
4471     };
4472
4473     proto.doMethod = function(attr, start, end) {
4474         var val;
4475
4476         if (this.patterns.color.test(attr)) {
4477             val = [];
4478             for (var i = 0, len = start.length; i < len; ++i) {
4479                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4480             }
4481
4482             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4483         }
4484         else {
4485             val = superclass.doMethod.call(this, attr, start, end);
4486         }
4487
4488         return val;
4489     };
4490
4491     proto.setRuntimeAttribute = function(attr) {
4492         superclass.setRuntimeAttribute.call(this, attr);
4493
4494         if (this.patterns.color.test(attr)) {
4495             var attributes = this.attributes;
4496             var start = this.parseColor(this.runtimeAttributes[attr].start);
4497             var end = this.parseColor(this.runtimeAttributes[attr].end);
4498
4499             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4500                 end = this.parseColor(attributes[attr].by);
4501
4502                 for (var i = 0, len = start.length; i < len; ++i) {
4503                     end[i] = start[i] + end[i];
4504                 }
4505             }
4506
4507             this.runtimeAttributes[attr].start = start;
4508             this.runtimeAttributes[attr].end = end;
4509         }
4510     };
4511 })();
4512
4513 /*
4514  * Portions of this file are based on pieces of Yahoo User Interface Library
4515  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4516  * YUI licensed under the BSD License:
4517  * http://developer.yahoo.net/yui/license.txt
4518  * <script type="text/javascript">
4519  *
4520  */
4521 Roo.lib.Easing = {
4522
4523
4524     easeNone: function (t, b, c, d) {
4525         return c * t / d + b;
4526     },
4527
4528
4529     easeIn: function (t, b, c, d) {
4530         return c * (t /= d) * t + b;
4531     },
4532
4533
4534     easeOut: function (t, b, c, d) {
4535         return -c * (t /= d) * (t - 2) + b;
4536     },
4537
4538
4539     easeBoth: function (t, b, c, d) {
4540         if ((t /= d / 2) < 1) {
4541             return c / 2 * t * t + b;
4542         }
4543
4544         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4545     },
4546
4547
4548     easeInStrong: function (t, b, c, d) {
4549         return c * (t /= d) * t * t * t + b;
4550     },
4551
4552
4553     easeOutStrong: function (t, b, c, d) {
4554         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4555     },
4556
4557
4558     easeBothStrong: function (t, b, c, d) {
4559         if ((t /= d / 2) < 1) {
4560             return c / 2 * t * t * t * t + b;
4561         }
4562
4563         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4564     },
4565
4566
4567
4568     elasticIn: function (t, b, c, d, a, p) {
4569         if (t == 0) {
4570             return b;
4571         }
4572         if ((t /= d) == 1) {
4573             return b + c;
4574         }
4575         if (!p) {
4576             p = d * .3;
4577         }
4578
4579         if (!a || a < Math.abs(c)) {
4580             a = c;
4581             var s = p / 4;
4582         }
4583         else {
4584             var s = p / (2 * Math.PI) * Math.asin(c / a);
4585         }
4586
4587         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4588     },
4589
4590
4591     elasticOut: function (t, b, c, d, a, p) {
4592         if (t == 0) {
4593             return b;
4594         }
4595         if ((t /= d) == 1) {
4596             return b + c;
4597         }
4598         if (!p) {
4599             p = d * .3;
4600         }
4601
4602         if (!a || a < Math.abs(c)) {
4603             a = c;
4604             var s = p / 4;
4605         }
4606         else {
4607             var s = p / (2 * Math.PI) * Math.asin(c / a);
4608         }
4609
4610         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4611     },
4612
4613
4614     elasticBoth: function (t, b, c, d, a, p) {
4615         if (t == 0) {
4616             return b;
4617         }
4618
4619         if ((t /= d / 2) == 2) {
4620             return b + c;
4621         }
4622
4623         if (!p) {
4624             p = d * (.3 * 1.5);
4625         }
4626
4627         if (!a || a < Math.abs(c)) {
4628             a = c;
4629             var s = p / 4;
4630         }
4631         else {
4632             var s = p / (2 * Math.PI) * Math.asin(c / a);
4633         }
4634
4635         if (t < 1) {
4636             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4637                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4638         }
4639         return a * Math.pow(2, -10 * (t -= 1)) *
4640                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4641     },
4642
4643
4644
4645     backIn: function (t, b, c, d, s) {
4646         if (typeof s == 'undefined') {
4647             s = 1.70158;
4648         }
4649         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4650     },
4651
4652
4653     backOut: function (t, b, c, d, s) {
4654         if (typeof s == 'undefined') {
4655             s = 1.70158;
4656         }
4657         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4658     },
4659
4660
4661     backBoth: function (t, b, c, d, s) {
4662         if (typeof s == 'undefined') {
4663             s = 1.70158;
4664         }
4665
4666         if ((t /= d / 2 ) < 1) {
4667             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4668         }
4669         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4670     },
4671
4672
4673     bounceIn: function (t, b, c, d) {
4674         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4675     },
4676
4677
4678     bounceOut: function (t, b, c, d) {
4679         if ((t /= d) < (1 / 2.75)) {
4680             return c * (7.5625 * t * t) + b;
4681         } else if (t < (2 / 2.75)) {
4682             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4683         } else if (t < (2.5 / 2.75)) {
4684             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4685         }
4686         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4687     },
4688
4689
4690     bounceBoth: function (t, b, c, d) {
4691         if (t < d / 2) {
4692             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4693         }
4694         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4695     }
4696 };/*
4697  * Portions of this file are based on pieces of Yahoo User Interface Library
4698  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4699  * YUI licensed under the BSD License:
4700  * http://developer.yahoo.net/yui/license.txt
4701  * <script type="text/javascript">
4702  *
4703  */
4704     (function() {
4705         Roo.lib.Motion = function(el, attributes, duration, method) {
4706             if (el) {
4707                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4708             }
4709         };
4710
4711         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4712
4713
4714         var Y = Roo.lib;
4715         var superclass = Y.Motion.superclass;
4716         var proto = Y.Motion.prototype;
4717
4718         proto.toString = function() {
4719             var el = this.getEl();
4720             var id = el.id || el.tagName;
4721             return ("Motion " + id);
4722         };
4723
4724         proto.patterns.points = /^points$/i;
4725
4726         proto.setAttribute = function(attr, val, unit) {
4727             if (this.patterns.points.test(attr)) {
4728                 unit = unit || 'px';
4729                 superclass.setAttribute.call(this, 'left', val[0], unit);
4730                 superclass.setAttribute.call(this, 'top', val[1], unit);
4731             } else {
4732                 superclass.setAttribute.call(this, attr, val, unit);
4733             }
4734         };
4735
4736         proto.getAttribute = function(attr) {
4737             if (this.patterns.points.test(attr)) {
4738                 var val = [
4739                         superclass.getAttribute.call(this, 'left'),
4740                         superclass.getAttribute.call(this, 'top')
4741                         ];
4742             } else {
4743                 val = superclass.getAttribute.call(this, attr);
4744             }
4745
4746             return val;
4747         };
4748
4749         proto.doMethod = function(attr, start, end) {
4750             var val = null;
4751
4752             if (this.patterns.points.test(attr)) {
4753                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4754                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4755             } else {
4756                 val = superclass.doMethod.call(this, attr, start, end);
4757             }
4758             return val;
4759         };
4760
4761         proto.setRuntimeAttribute = function(attr) {
4762             if (this.patterns.points.test(attr)) {
4763                 var el = this.getEl();
4764                 var attributes = this.attributes;
4765                 var start;
4766                 var control = attributes['points']['control'] || [];
4767                 var end;
4768                 var i, len;
4769
4770                 if (control.length > 0 && !(control[0] instanceof Array)) {
4771                     control = [control];
4772                 } else {
4773                     var tmp = [];
4774                     for (i = 0,len = control.length; i < len; ++i) {
4775                         tmp[i] = control[i];
4776                     }
4777                     control = tmp;
4778                 }
4779
4780                 Roo.fly(el).position();
4781
4782                 if (isset(attributes['points']['from'])) {
4783                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4784                 }
4785                 else {
4786                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4787                 }
4788
4789                 start = this.getAttribute('points');
4790
4791
4792                 if (isset(attributes['points']['to'])) {
4793                     end = translateValues.call(this, attributes['points']['to'], start);
4794
4795                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4796                     for (i = 0,len = control.length; i < len; ++i) {
4797                         control[i] = translateValues.call(this, control[i], start);
4798                     }
4799
4800
4801                 } else if (isset(attributes['points']['by'])) {
4802                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4803
4804                     for (i = 0,len = control.length; i < len; ++i) {
4805                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4806                     }
4807                 }
4808
4809                 this.runtimeAttributes[attr] = [start];
4810
4811                 if (control.length > 0) {
4812                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4813                 }
4814
4815                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4816             }
4817             else {
4818                 superclass.setRuntimeAttribute.call(this, attr);
4819             }
4820         };
4821
4822         var translateValues = function(val, start) {
4823             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4824             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4825
4826             return val;
4827         };
4828
4829         var isset = function(prop) {
4830             return (typeof prop !== 'undefined');
4831         };
4832     })();
4833 /*
4834  * Portions of this file are based on pieces of Yahoo User Interface Library
4835  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4836  * YUI licensed under the BSD License:
4837  * http://developer.yahoo.net/yui/license.txt
4838  * <script type="text/javascript">
4839  *
4840  */
4841     (function() {
4842         Roo.lib.Scroll = function(el, attributes, duration, method) {
4843             if (el) {
4844                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4845             }
4846         };
4847
4848         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4849
4850
4851         var Y = Roo.lib;
4852         var superclass = Y.Scroll.superclass;
4853         var proto = Y.Scroll.prototype;
4854
4855         proto.toString = function() {
4856             var el = this.getEl();
4857             var id = el.id || el.tagName;
4858             return ("Scroll " + id);
4859         };
4860
4861         proto.doMethod = function(attr, start, end) {
4862             var val = null;
4863
4864             if (attr == 'scroll') {
4865                 val = [
4866                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4867                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4868                         ];
4869
4870             } else {
4871                 val = superclass.doMethod.call(this, attr, start, end);
4872             }
4873             return val;
4874         };
4875
4876         proto.getAttribute = function(attr) {
4877             var val = null;
4878             var el = this.getEl();
4879
4880             if (attr == 'scroll') {
4881                 val = [ el.scrollLeft, el.scrollTop ];
4882             } else {
4883                 val = superclass.getAttribute.call(this, attr);
4884             }
4885
4886             return val;
4887         };
4888
4889         proto.setAttribute = function(attr, val, unit) {
4890             var el = this.getEl();
4891
4892             if (attr == 'scroll') {
4893                 el.scrollLeft = val[0];
4894                 el.scrollTop = val[1];
4895             } else {
4896                 superclass.setAttribute.call(this, attr, val, unit);
4897             }
4898         };
4899     })();
4900 /*
4901  * Based on:
4902  * Ext JS Library 1.1.1
4903  * Copyright(c) 2006-2007, Ext JS, LLC.
4904  *
4905  * Originally Released Under LGPL - original licence link has changed is not relivant.
4906  *
4907  * Fork - LGPL
4908  * <script type="text/javascript">
4909  */
4910
4911
4912 // nasty IE9 hack - what a pile of crap that is..
4913
4914  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4915     Range.prototype.createContextualFragment = function (html) {
4916         var doc = window.document;
4917         var container = doc.createElement("div");
4918         container.innerHTML = html;
4919         var frag = doc.createDocumentFragment(), n;
4920         while ((n = container.firstChild)) {
4921             frag.appendChild(n);
4922         }
4923         return frag;
4924     };
4925 }
4926
4927 /**
4928  * @class Roo.DomHelper
4929  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4930  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4931  * @static
4932  */
4933 Roo.DomHelper = function(){
4934     var tempTableEl = null;
4935     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4936     var tableRe = /^table|tbody|tr|td$/i;
4937     var xmlns = {};
4938     // build as innerHTML where available
4939     /** @ignore */
4940     var createHtml = function(o){
4941         if(typeof o == 'string'){
4942             return o;
4943         }
4944         var b = "";
4945         if(!o.tag){
4946             o.tag = "div";
4947         }
4948         b += "<" + o.tag;
4949         for(var attr in o){
4950             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4951             if(attr == "style"){
4952                 var s = o["style"];
4953                 if(typeof s == "function"){
4954                     s = s.call();
4955                 }
4956                 if(typeof s == "string"){
4957                     b += ' style="' + s + '"';
4958                 }else if(typeof s == "object"){
4959                     b += ' style="';
4960                     for(var key in s){
4961                         if(typeof s[key] != "function"){
4962                             b += key + ":" + s[key] + ";";
4963                         }
4964                     }
4965                     b += '"';
4966                 }
4967             }else{
4968                 if(attr == "cls"){
4969                     b += ' class="' + o["cls"] + '"';
4970                 }else if(attr == "htmlFor"){
4971                     b += ' for="' + o["htmlFor"] + '"';
4972                 }else{
4973                     b += " " + attr + '="' + o[attr] + '"';
4974                 }
4975             }
4976         }
4977         if(emptyTags.test(o.tag)){
4978             b += "/>";
4979         }else{
4980             b += ">";
4981             var cn = o.children || o.cn;
4982             if(cn){
4983                 //http://bugs.kde.org/show_bug.cgi?id=71506
4984                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4985                     for(var i = 0, len = cn.length; i < len; i++) {
4986                         b += createHtml(cn[i], b);
4987                     }
4988                 }else{
4989                     b += createHtml(cn, b);
4990                 }
4991             }
4992             if(o.html){
4993                 b += o.html;
4994             }
4995             b += "</" + o.tag + ">";
4996         }
4997         return b;
4998     };
4999
5000     // build as dom
5001     /** @ignore */
5002     var createDom = function(o, parentNode){
5003          
5004         // defininition craeted..
5005         var ns = false;
5006         if (o.ns && o.ns != 'html') {
5007                
5008             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5009                 xmlns[o.ns] = o.xmlns;
5010                 ns = o.xmlns;
5011             }
5012             if (typeof(xmlns[o.ns]) == 'undefined') {
5013                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5014             }
5015             ns = xmlns[o.ns];
5016         }
5017         
5018         
5019         if (typeof(o) == 'string') {
5020             return parentNode.appendChild(document.createTextNode(o));
5021         }
5022         o.tag = o.tag || div;
5023         if (o.ns && Roo.isIE) {
5024             ns = false;
5025             o.tag = o.ns + ':' + o.tag;
5026             
5027         }
5028         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5029         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5030         for(var attr in o){
5031             
5032             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5033                     attr == "style" || typeof o[attr] == "function") { continue; }
5034                     
5035             if(attr=="cls" && Roo.isIE){
5036                 el.className = o["cls"];
5037             }else{
5038                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5039                 else { 
5040                     el[attr] = o[attr];
5041                 }
5042             }
5043         }
5044         Roo.DomHelper.applyStyles(el, o.style);
5045         var cn = o.children || o.cn;
5046         if(cn){
5047             //http://bugs.kde.org/show_bug.cgi?id=71506
5048              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5049                 for(var i = 0, len = cn.length; i < len; i++) {
5050                     createDom(cn[i], el);
5051                 }
5052             }else{
5053                 createDom(cn, el);
5054             }
5055         }
5056         if(o.html){
5057             el.innerHTML = o.html;
5058         }
5059         if(parentNode){
5060            parentNode.appendChild(el);
5061         }
5062         return el;
5063     };
5064
5065     var ieTable = function(depth, s, h, e){
5066         tempTableEl.innerHTML = [s, h, e].join('');
5067         var i = -1, el = tempTableEl;
5068         while(++i < depth && el.firstChild){
5069             el = el.firstChild;
5070         }
5071         return el;
5072     };
5073
5074     // kill repeat to save bytes
5075     var ts = '<table>',
5076         te = '</table>',
5077         tbs = ts+'<tbody>',
5078         tbe = '</tbody>'+te,
5079         trs = tbs + '<tr>',
5080         tre = '</tr>'+tbe;
5081
5082     /**
5083      * @ignore
5084      * Nasty code for IE's broken table implementation
5085      */
5086     var insertIntoTable = function(tag, where, el, html){
5087         if(!tempTableEl){
5088             tempTableEl = document.createElement('div');
5089         }
5090         var node;
5091         var before = null;
5092         if(tag == 'td'){
5093             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5094                 return;
5095             }
5096             if(where == 'beforebegin'){
5097                 before = el;
5098                 el = el.parentNode;
5099             } else{
5100                 before = el.nextSibling;
5101                 el = el.parentNode;
5102             }
5103             node = ieTable(4, trs, html, tre);
5104         }
5105         else if(tag == 'tr'){
5106             if(where == 'beforebegin'){
5107                 before = el;
5108                 el = el.parentNode;
5109                 node = ieTable(3, tbs, html, tbe);
5110             } else if(where == 'afterend'){
5111                 before = el.nextSibling;
5112                 el = el.parentNode;
5113                 node = ieTable(3, tbs, html, tbe);
5114             } else{ // INTO a TR
5115                 if(where == 'afterbegin'){
5116                     before = el.firstChild;
5117                 }
5118                 node = ieTable(4, trs, html, tre);
5119             }
5120         } else if(tag == 'tbody'){
5121             if(where == 'beforebegin'){
5122                 before = el;
5123                 el = el.parentNode;
5124                 node = ieTable(2, ts, html, te);
5125             } else if(where == 'afterend'){
5126                 before = el.nextSibling;
5127                 el = el.parentNode;
5128                 node = ieTable(2, ts, html, te);
5129             } else{
5130                 if(where == 'afterbegin'){
5131                     before = el.firstChild;
5132                 }
5133                 node = ieTable(3, tbs, html, tbe);
5134             }
5135         } else{ // TABLE
5136             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5137                 return;
5138             }
5139             if(where == 'afterbegin'){
5140                 before = el.firstChild;
5141             }
5142             node = ieTable(2, ts, html, te);
5143         }
5144         el.insertBefore(node, before);
5145         return node;
5146     };
5147     
5148     // this is a bit like the react update code...
5149     // 
5150     
5151     var updateNode = function(from, to)
5152     {
5153         // should we handle non-standard elements?
5154         Roo.log(["UpdateNode" , from, to]);
5155         if (from.nodeType != to.nodeType) {
5156             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5157             from.parentNode.replaceChild(to, from);
5158         }
5159         
5160         if (from.nodeType == 3) {
5161             // assume it's text?!
5162             if (from.data == to.data) {
5163                 return;
5164             }
5165             from.data = to.data;
5166             return;
5167         }
5168         
5169         // assume 'to' doesnt have '1/3 nodetypes!
5170         if (from.nodeType !=1 || from.tagName != to.tagName) {
5171             Roo.log(["ReplaceChild" , from, to ]);
5172             from.parentNode.replaceChild(to, from);
5173             return;
5174         }
5175         // compare attributes
5176         var ar = Array.from(from.attributes);
5177         for(var i = 0; i< ar.length;i++) {
5178             if (to.hasAttribute(ar[i].name)) {
5179                 continue;
5180             }
5181             if (ar[i].name == 'id') { // always keep ids?
5182                 continue;
5183             }
5184             from.removeAttribute(ar[i].name);
5185         }
5186         ar = to.attributes;
5187         for(var i = 0; i< ar.length;i++) {
5188             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5189                 continue;
5190             }
5191             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5192         }
5193         // children
5194         var far = Array.from(from.childNodes);
5195         var tar = Array.from(to.childNodes);
5196         // if the lengths are different.. then it's probably a editable content change, rather than
5197         // a change of the block definition..
5198         
5199         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5200          /*if (from.innerHTML == to.innerHTML) {
5201             return;
5202         }
5203         if (far.length != tar.length) {
5204             from.innerHTML = to.innerHTML;
5205             return;
5206         }
5207         */
5208         
5209         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5210             if (i >= far.length) {
5211                 from.appendChild(tar[i]);
5212                 Roo.log(["add", tar[i]]);
5213                 
5214             } else if ( i  >= tar.length) {
5215                 from.removeChild(far[i]);
5216                 Roo.log(["remove", far[i]]);
5217             } else {
5218                 
5219                 updateNode(far[i], tar[i]);
5220             }    
5221         }
5222         
5223         
5224         
5225         
5226     };
5227     
5228     
5229
5230     return {
5231         /** True to force the use of DOM instead of html fragments @type Boolean */
5232         useDom : false,
5233     
5234         /**
5235          * Returns the markup for the passed Element(s) config
5236          * @param {Object} o The Dom object spec (and children)
5237          * @return {String}
5238          */
5239         markup : function(o){
5240             return createHtml(o);
5241         },
5242     
5243         /**
5244          * Applies a style specification to an element
5245          * @param {String/HTMLElement} el The element to apply styles to
5246          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5247          * a function which returns such a specification.
5248          */
5249         applyStyles : function(el, styles){
5250             if(styles){
5251                el = Roo.fly(el);
5252                if(typeof styles == "string"){
5253                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5254                    var matches;
5255                    while ((matches = re.exec(styles)) != null){
5256                        el.setStyle(matches[1], matches[2]);
5257                    }
5258                }else if (typeof styles == "object"){
5259                    for (var style in styles){
5260                       el.setStyle(style, styles[style]);
5261                    }
5262                }else if (typeof styles == "function"){
5263                     Roo.DomHelper.applyStyles(el, styles.call());
5264                }
5265             }
5266         },
5267     
5268         /**
5269          * Inserts an HTML fragment into the Dom
5270          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5271          * @param {HTMLElement} el The context element
5272          * @param {String} html The HTML fragmenet
5273          * @return {HTMLElement} The new node
5274          */
5275         insertHtml : function(where, el, html){
5276             where = where.toLowerCase();
5277             if(el.insertAdjacentHTML){
5278                 if(tableRe.test(el.tagName)){
5279                     var rs;
5280                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5281                         return rs;
5282                     }
5283                 }
5284                 switch(where){
5285                     case "beforebegin":
5286                         el.insertAdjacentHTML('BeforeBegin', html);
5287                         return el.previousSibling;
5288                     case "afterbegin":
5289                         el.insertAdjacentHTML('AfterBegin', html);
5290                         return el.firstChild;
5291                     case "beforeend":
5292                         el.insertAdjacentHTML('BeforeEnd', html);
5293                         return el.lastChild;
5294                     case "afterend":
5295                         el.insertAdjacentHTML('AfterEnd', html);
5296                         return el.nextSibling;
5297                 }
5298                 throw 'Illegal insertion point -> "' + where + '"';
5299             }
5300             var range = el.ownerDocument.createRange();
5301             var frag;
5302             switch(where){
5303                  case "beforebegin":
5304                     range.setStartBefore(el);
5305                     frag = range.createContextualFragment(html);
5306                     el.parentNode.insertBefore(frag, el);
5307                     return el.previousSibling;
5308                  case "afterbegin":
5309                     if(el.firstChild){
5310                         range.setStartBefore(el.firstChild);
5311                         frag = range.createContextualFragment(html);
5312                         el.insertBefore(frag, el.firstChild);
5313                         return el.firstChild;
5314                     }else{
5315                         el.innerHTML = html;
5316                         return el.firstChild;
5317                     }
5318                 case "beforeend":
5319                     if(el.lastChild){
5320                         range.setStartAfter(el.lastChild);
5321                         frag = range.createContextualFragment(html);
5322                         el.appendChild(frag);
5323                         return el.lastChild;
5324                     }else{
5325                         el.innerHTML = html;
5326                         return el.lastChild;
5327                     }
5328                 case "afterend":
5329                     range.setStartAfter(el);
5330                     frag = range.createContextualFragment(html);
5331                     el.parentNode.insertBefore(frag, el.nextSibling);
5332                     return el.nextSibling;
5333                 }
5334                 throw 'Illegal insertion point -> "' + where + '"';
5335         },
5336     
5337         /**
5338          * Creates new Dom element(s) and inserts them before el
5339          * @param {String/HTMLElement/Element} el The context element
5340          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5341          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5342          * @return {HTMLElement/Roo.Element} The new node
5343          */
5344         insertBefore : function(el, o, returnElement){
5345             return this.doInsert(el, o, returnElement, "beforeBegin");
5346         },
5347     
5348         /**
5349          * Creates new Dom element(s) and inserts them after el
5350          * @param {String/HTMLElement/Element} el The context element
5351          * @param {Object} o The Dom object spec (and children)
5352          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5353          * @return {HTMLElement/Roo.Element} The new node
5354          */
5355         insertAfter : function(el, o, returnElement){
5356             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5357         },
5358     
5359         /**
5360          * Creates new Dom element(s) and inserts them as the first child of el
5361          * @param {String/HTMLElement/Element} el The context element
5362          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5363          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5364          * @return {HTMLElement/Roo.Element} The new node
5365          */
5366         insertFirst : function(el, o, returnElement){
5367             return this.doInsert(el, o, returnElement, "afterBegin");
5368         },
5369     
5370         // private
5371         doInsert : function(el, o, returnElement, pos, sibling){
5372             el = Roo.getDom(el);
5373             var newNode;
5374             if(this.useDom || o.ns){
5375                 newNode = createDom(o, null);
5376                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5377             }else{
5378                 var html = createHtml(o);
5379                 newNode = this.insertHtml(pos, el, html);
5380             }
5381             return returnElement ? Roo.get(newNode, true) : newNode;
5382         },
5383     
5384         /**
5385          * Creates new Dom element(s) and appends them to el
5386          * @param {String/HTMLElement/Element} el The context element
5387          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5388          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5389          * @return {HTMLElement/Roo.Element} The new node
5390          */
5391         append : function(el, o, returnElement){
5392             el = Roo.getDom(el);
5393             var newNode;
5394             if(this.useDom || o.ns){
5395                 newNode = createDom(o, null);
5396                 el.appendChild(newNode);
5397             }else{
5398                 var html = createHtml(o);
5399                 newNode = this.insertHtml("beforeEnd", el, html);
5400             }
5401             return returnElement ? Roo.get(newNode, true) : newNode;
5402         },
5403     
5404         /**
5405          * Creates new Dom element(s) and overwrites the contents of el with them
5406          * @param {String/HTMLElement/Element} el The context element
5407          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5408          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5409          * @return {HTMLElement/Roo.Element} The new node
5410          */
5411         overwrite : function(el, o, returnElement)
5412         {
5413             el = Roo.getDom(el);
5414             if (o.ns) {
5415               
5416                 while (el.childNodes.length) {
5417                     el.removeChild(el.firstChild);
5418                 }
5419                 createDom(o, el);
5420             } else {
5421                 el.innerHTML = createHtml(o);   
5422             }
5423             
5424             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5425         },
5426     
5427         /**
5428          * Creates a new Roo.DomHelper.Template from the Dom object spec
5429          * @param {Object} o The Dom object spec (and children)
5430          * @return {Roo.DomHelper.Template} The new template
5431          */
5432         createTemplate : function(o){
5433             var html = createHtml(o);
5434             return new Roo.Template(html);
5435         },
5436          /**
5437          * Updates the first element with the spec from the o (replacing if necessary)
5438          * This iterates through the children, and updates attributes / children etc..
5439          * @param {String/HTMLElement/Element} el The context element
5440          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5441          */
5442         
5443         update : function(el, o)
5444         {
5445             updateNode(Roo.getDom(el), createDom(o));
5446             
5447         }
5448         
5449         
5450     };
5451 }();
5452 /*
5453  * Based on:
5454  * Ext JS Library 1.1.1
5455  * Copyright(c) 2006-2007, Ext JS, LLC.
5456  *
5457  * Originally Released Under LGPL - original licence link has changed is not relivant.
5458  *
5459  * Fork - LGPL
5460  * <script type="text/javascript">
5461  */
5462  
5463 /**
5464 * @class Roo.Template
5465 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5466 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5467 * Usage:
5468 <pre><code>
5469 var t = new Roo.Template({
5470     html :  '&lt;div name="{id}"&gt;' + 
5471         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5472         '&lt;/div&gt;',
5473     myformat: function (value, allValues) {
5474         return 'XX' + value;
5475     }
5476 });
5477 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5478 </code></pre>
5479 * For more information see this blog post with examples:
5480 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5481      - Create Elements using DOM, HTML fragments and Templates</a>. 
5482 * @constructor
5483 * @param {Object} cfg - Configuration object.
5484 */
5485 Roo.Template = function(cfg){
5486     // BC!
5487     if(cfg instanceof Array){
5488         cfg = cfg.join("");
5489     }else if(arguments.length > 1){
5490         cfg = Array.prototype.join.call(arguments, "");
5491     }
5492     
5493     
5494     if (typeof(cfg) == 'object') {
5495         Roo.apply(this,cfg)
5496     } else {
5497         // bc
5498         this.html = cfg;
5499     }
5500     if (this.url) {
5501         this.load();
5502     }
5503     
5504 };
5505 Roo.Template.prototype = {
5506     
5507     /**
5508      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5509      */
5510     onLoad : false,
5511     
5512     
5513     /**
5514      * @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..
5515      *                    it should be fixed so that template is observable...
5516      */
5517     url : false,
5518     /**
5519      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5520      */
5521     html : '',
5522     
5523     
5524     compiled : false,
5525     loaded : false,
5526     /**
5527      * Returns an HTML fragment of this template with the specified values applied.
5528      * @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'})
5529      * @return {String} The HTML fragment
5530      */
5531     
5532    
5533     
5534     applyTemplate : function(values){
5535         //Roo.log(["applyTemplate", values]);
5536         try {
5537            
5538             if(this.compiled){
5539                 return this.compiled(values);
5540             }
5541             var useF = this.disableFormats !== true;
5542             var fm = Roo.util.Format, tpl = this;
5543             var fn = function(m, name, format, args){
5544                 if(format && useF){
5545                     if(format.substr(0, 5) == "this."){
5546                         return tpl.call(format.substr(5), values[name], values);
5547                     }else{
5548                         if(args){
5549                             // quoted values are required for strings in compiled templates, 
5550                             // but for non compiled we need to strip them
5551                             // quoted reversed for jsmin
5552                             var re = /^\s*['"](.*)["']\s*$/;
5553                             args = args.split(',');
5554                             for(var i = 0, len = args.length; i < len; i++){
5555                                 args[i] = args[i].replace(re, "$1");
5556                             }
5557                             args = [values[name]].concat(args);
5558                         }else{
5559                             args = [values[name]];
5560                         }
5561                         return fm[format].apply(fm, args);
5562                     }
5563                 }else{
5564                     return values[name] !== undefined ? values[name] : "";
5565                 }
5566             };
5567             return this.html.replace(this.re, fn);
5568         } catch (e) {
5569             Roo.log(e);
5570             throw e;
5571         }
5572          
5573     },
5574     
5575     loading : false,
5576       
5577     load : function ()
5578     {
5579          
5580         if (this.loading) {
5581             return;
5582         }
5583         var _t = this;
5584         
5585         this.loading = true;
5586         this.compiled = false;
5587         
5588         var cx = new Roo.data.Connection();
5589         cx.request({
5590             url : this.url,
5591             method : 'GET',
5592             success : function (response) {
5593                 _t.loading = false;
5594                 _t.url = false;
5595                 
5596                 _t.set(response.responseText,true);
5597                 _t.loaded = true;
5598                 if (_t.onLoad) {
5599                     _t.onLoad();
5600                 }
5601              },
5602             failure : function(response) {
5603                 Roo.log("Template failed to load from " + _t.url);
5604                 _t.loading = false;
5605             }
5606         });
5607     },
5608
5609     /**
5610      * Sets the HTML used as the template and optionally compiles it.
5611      * @param {String} html
5612      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5613      * @return {Roo.Template} this
5614      */
5615     set : function(html, compile){
5616         this.html = html;
5617         this.compiled = false;
5618         if(compile){
5619             this.compile();
5620         }
5621         return this;
5622     },
5623     
5624     /**
5625      * True to disable format functions (defaults to false)
5626      * @type Boolean
5627      */
5628     disableFormats : false,
5629     
5630     /**
5631     * The regular expression used to match template variables 
5632     * @type RegExp
5633     * @property 
5634     */
5635     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5636     
5637     /**
5638      * Compiles the template into an internal function, eliminating the RegEx overhead.
5639      * @return {Roo.Template} this
5640      */
5641     compile : function(){
5642         var fm = Roo.util.Format;
5643         var useF = this.disableFormats !== true;
5644         var sep = Roo.isGecko ? "+" : ",";
5645         var fn = function(m, name, format, args){
5646             if(format && useF){
5647                 args = args ? ',' + args : "";
5648                 if(format.substr(0, 5) != "this."){
5649                     format = "fm." + format + '(';
5650                 }else{
5651                     format = 'this.call("'+ format.substr(5) + '", ';
5652                     args = ", values";
5653                 }
5654             }else{
5655                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5656             }
5657             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5658         };
5659         var body;
5660         // branched to use + in gecko and [].join() in others
5661         if(Roo.isGecko){
5662             body = "this.compiled = function(values){ return '" +
5663                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5664                     "';};";
5665         }else{
5666             body = ["this.compiled = function(values){ return ['"];
5667             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5668             body.push("'].join('');};");
5669             body = body.join('');
5670         }
5671         /**
5672          * eval:var:values
5673          * eval:var:fm
5674          */
5675         eval(body);
5676         return this;
5677     },
5678     
5679     // private function used to call members
5680     call : function(fnName, value, allValues){
5681         return this[fnName](value, allValues);
5682     },
5683     
5684     /**
5685      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5686      * @param {String/HTMLElement/Roo.Element} el The context element
5687      * @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'})
5688      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5689      * @return {HTMLElement/Roo.Element} The new node or Element
5690      */
5691     insertFirst: function(el, values, returnElement){
5692         return this.doInsert('afterBegin', el, values, returnElement);
5693     },
5694
5695     /**
5696      * Applies the supplied values to the template and inserts the new node(s) before el.
5697      * @param {String/HTMLElement/Roo.Element} el The context element
5698      * @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'})
5699      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5700      * @return {HTMLElement/Roo.Element} The new node or Element
5701      */
5702     insertBefore: function(el, values, returnElement){
5703         return this.doInsert('beforeBegin', el, values, returnElement);
5704     },
5705
5706     /**
5707      * Applies the supplied values to the template and inserts the new node(s) after el.
5708      * @param {String/HTMLElement/Roo.Element} el The context element
5709      * @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'})
5710      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5711      * @return {HTMLElement/Roo.Element} The new node or Element
5712      */
5713     insertAfter : function(el, values, returnElement){
5714         return this.doInsert('afterEnd', el, values, returnElement);
5715     },
5716     
5717     /**
5718      * Applies the supplied values to the template and appends the new node(s) to el.
5719      * @param {String/HTMLElement/Roo.Element} el The context element
5720      * @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'})
5721      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5722      * @return {HTMLElement/Roo.Element} The new node or Element
5723      */
5724     append : function(el, values, returnElement){
5725         return this.doInsert('beforeEnd', el, values, returnElement);
5726     },
5727
5728     doInsert : function(where, el, values, returnEl){
5729         el = Roo.getDom(el);
5730         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5731         return returnEl ? Roo.get(newNode, true) : newNode;
5732     },
5733
5734     /**
5735      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5736      * @param {String/HTMLElement/Roo.Element} el The context element
5737      * @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'})
5738      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5739      * @return {HTMLElement/Roo.Element} The new node or Element
5740      */
5741     overwrite : function(el, values, returnElement){
5742         el = Roo.getDom(el);
5743         el.innerHTML = this.applyTemplate(values);
5744         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5745     }
5746 };
5747 /**
5748  * Alias for {@link #applyTemplate}
5749  * @method
5750  */
5751 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5752
5753 // backwards compat
5754 Roo.DomHelper.Template = Roo.Template;
5755
5756 /**
5757  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5758  * @param {String/HTMLElement} el A DOM element or its id
5759  * @returns {Roo.Template} The created template
5760  * @static
5761  */
5762 Roo.Template.from = function(el){
5763     el = Roo.getDom(el);
5764     return new Roo.Template(el.value || el.innerHTML);
5765 };/*
5766  * Based on:
5767  * Ext JS Library 1.1.1
5768  * Copyright(c) 2006-2007, Ext JS, LLC.
5769  *
5770  * Originally Released Under LGPL - original licence link has changed is not relivant.
5771  *
5772  * Fork - LGPL
5773  * <script type="text/javascript">
5774  */
5775  
5776
5777 /*
5778  * This is code is also distributed under MIT license for use
5779  * with jQuery and prototype JavaScript libraries.
5780  */
5781 /**
5782  * @class Roo.DomQuery
5783 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).
5784 <p>
5785 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>
5786
5787 <p>
5788 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.
5789 </p>
5790 <h4>Element Selectors:</h4>
5791 <ul class="list">
5792     <li> <b>*</b> any element</li>
5793     <li> <b>E</b> an element with the tag E</li>
5794     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5795     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5796     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5797     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5798 </ul>
5799 <h4>Attribute Selectors:</h4>
5800 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5801 <ul class="list">
5802     <li> <b>E[foo]</b> has an attribute "foo"</li>
5803     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5804     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5805     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5806     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5807     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5808     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5809 </ul>
5810 <h4>Pseudo Classes:</h4>
5811 <ul class="list">
5812     <li> <b>E:first-child</b> E is the first child of its parent</li>
5813     <li> <b>E:last-child</b> E is the last child of its parent</li>
5814     <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>
5815     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5816     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5817     <li> <b>E:only-child</b> E is the only child of its parent</li>
5818     <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>
5819     <li> <b>E:first</b> the first E in the resultset</li>
5820     <li> <b>E:last</b> the last E in the resultset</li>
5821     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5822     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5823     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5824     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5825     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5826     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5827     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5828     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5829     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5830 </ul>
5831 <h4>CSS Value Selectors:</h4>
5832 <ul class="list">
5833     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5834     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5835     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5836     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5837     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5838     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5839 </ul>
5840  * @static
5841  */
5842 Roo.DomQuery = function(){
5843     var cache = {}, simpleCache = {}, valueCache = {};
5844     var nonSpace = /\S/;
5845     var trimRe = /^\s+|\s+$/g;
5846     var tplRe = /\{(\d+)\}/g;
5847     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5848     var tagTokenRe = /^(#)?([\w-\*]+)/;
5849     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5850
5851     function child(p, index){
5852         var i = 0;
5853         var n = p.firstChild;
5854         while(n){
5855             if(n.nodeType == 1){
5856                if(++i == index){
5857                    return n;
5858                }
5859             }
5860             n = n.nextSibling;
5861         }
5862         return null;
5863     };
5864
5865     function next(n){
5866         while((n = n.nextSibling) && n.nodeType != 1);
5867         return n;
5868     };
5869
5870     function prev(n){
5871         while((n = n.previousSibling) && n.nodeType != 1);
5872         return n;
5873     };
5874
5875     function children(d){
5876         var n = d.firstChild, ni = -1;
5877             while(n){
5878                 var nx = n.nextSibling;
5879                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5880                     d.removeChild(n);
5881                 }else{
5882                     n.nodeIndex = ++ni;
5883                 }
5884                 n = nx;
5885             }
5886             return this;
5887         };
5888
5889     function byClassName(c, a, v){
5890         if(!v){
5891             return c;
5892         }
5893         var r = [], ri = -1, cn;
5894         for(var i = 0, ci; ci = c[i]; i++){
5895             
5896             
5897             if((' '+
5898                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5899                  +' ').indexOf(v) != -1){
5900                 r[++ri] = ci;
5901             }
5902         }
5903         return r;
5904     };
5905
5906     function attrValue(n, attr){
5907         if(!n.tagName && typeof n.length != "undefined"){
5908             n = n[0];
5909         }
5910         if(!n){
5911             return null;
5912         }
5913         if(attr == "for"){
5914             return n.htmlFor;
5915         }
5916         if(attr == "class" || attr == "className"){
5917             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5918         }
5919         return n.getAttribute(attr) || n[attr];
5920
5921     };
5922
5923     function getNodes(ns, mode, tagName){
5924         var result = [], ri = -1, cs;
5925         if(!ns){
5926             return result;
5927         }
5928         tagName = tagName || "*";
5929         if(typeof ns.getElementsByTagName != "undefined"){
5930             ns = [ns];
5931         }
5932         if(!mode){
5933             for(var i = 0, ni; ni = ns[i]; i++){
5934                 cs = ni.getElementsByTagName(tagName);
5935                 for(var j = 0, ci; ci = cs[j]; j++){
5936                     result[++ri] = ci;
5937                 }
5938             }
5939         }else if(mode == "/" || mode == ">"){
5940             var utag = tagName.toUpperCase();
5941             for(var i = 0, ni, cn; ni = ns[i]; i++){
5942                 cn = ni.children || ni.childNodes;
5943                 for(var j = 0, cj; cj = cn[j]; j++){
5944                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5945                         result[++ri] = cj;
5946                     }
5947                 }
5948             }
5949         }else if(mode == "+"){
5950             var utag = tagName.toUpperCase();
5951             for(var i = 0, n; n = ns[i]; i++){
5952                 while((n = n.nextSibling) && n.nodeType != 1);
5953                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5954                     result[++ri] = n;
5955                 }
5956             }
5957         }else if(mode == "~"){
5958             for(var i = 0, n; n = ns[i]; i++){
5959                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5960                 if(n){
5961                     result[++ri] = n;
5962                 }
5963             }
5964         }
5965         return result;
5966     };
5967
5968     function concat(a, b){
5969         if(b.slice){
5970             return a.concat(b);
5971         }
5972         for(var i = 0, l = b.length; i < l; i++){
5973             a[a.length] = b[i];
5974         }
5975         return a;
5976     }
5977
5978     function byTag(cs, tagName){
5979         if(cs.tagName || cs == document){
5980             cs = [cs];
5981         }
5982         if(!tagName){
5983             return cs;
5984         }
5985         var r = [], ri = -1;
5986         tagName = tagName.toLowerCase();
5987         for(var i = 0, ci; ci = cs[i]; i++){
5988             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5989                 r[++ri] = ci;
5990             }
5991         }
5992         return r;
5993     };
5994
5995     function byId(cs, attr, id){
5996         if(cs.tagName || cs == document){
5997             cs = [cs];
5998         }
5999         if(!id){
6000             return cs;
6001         }
6002         var r = [], ri = -1;
6003         for(var i = 0,ci; ci = cs[i]; i++){
6004             if(ci && ci.id == id){
6005                 r[++ri] = ci;
6006                 return r;
6007             }
6008         }
6009         return r;
6010     };
6011
6012     function byAttribute(cs, attr, value, op, custom){
6013         var r = [], ri = -1, st = custom=="{";
6014         var f = Roo.DomQuery.operators[op];
6015         for(var i = 0, ci; ci = cs[i]; i++){
6016             var a;
6017             if(st){
6018                 a = Roo.DomQuery.getStyle(ci, attr);
6019             }
6020             else if(attr == "class" || attr == "className"){
6021                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6022             }else if(attr == "for"){
6023                 a = ci.htmlFor;
6024             }else if(attr == "href"){
6025                 a = ci.getAttribute("href", 2);
6026             }else{
6027                 a = ci.getAttribute(attr);
6028             }
6029             if((f && f(a, value)) || (!f && a)){
6030                 r[++ri] = ci;
6031             }
6032         }
6033         return r;
6034     };
6035
6036     function byPseudo(cs, name, value){
6037         return Roo.DomQuery.pseudos[name](cs, value);
6038     };
6039
6040     // This is for IE MSXML which does not support expandos.
6041     // IE runs the same speed using setAttribute, however FF slows way down
6042     // and Safari completely fails so they need to continue to use expandos.
6043     var isIE = window.ActiveXObject ? true : false;
6044
6045     // this eval is stop the compressor from
6046     // renaming the variable to something shorter
6047     
6048     /** eval:var:batch */
6049     var batch = 30803; 
6050
6051     var key = 30803;
6052
6053     function nodupIEXml(cs){
6054         var d = ++key;
6055         cs[0].setAttribute("_nodup", d);
6056         var r = [cs[0]];
6057         for(var i = 1, len = cs.length; i < len; i++){
6058             var c = cs[i];
6059             if(!c.getAttribute("_nodup") != d){
6060                 c.setAttribute("_nodup", d);
6061                 r[r.length] = c;
6062             }
6063         }
6064         for(var i = 0, len = cs.length; i < len; i++){
6065             cs[i].removeAttribute("_nodup");
6066         }
6067         return r;
6068     }
6069
6070     function nodup(cs){
6071         if(!cs){
6072             return [];
6073         }
6074         var len = cs.length, c, i, r = cs, cj, ri = -1;
6075         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6076             return cs;
6077         }
6078         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6079             return nodupIEXml(cs);
6080         }
6081         var d = ++key;
6082         cs[0]._nodup = d;
6083         for(i = 1; c = cs[i]; i++){
6084             if(c._nodup != d){
6085                 c._nodup = d;
6086             }else{
6087                 r = [];
6088                 for(var j = 0; j < i; j++){
6089                     r[++ri] = cs[j];
6090                 }
6091                 for(j = i+1; cj = cs[j]; j++){
6092                     if(cj._nodup != d){
6093                         cj._nodup = d;
6094                         r[++ri] = cj;
6095                     }
6096                 }
6097                 return r;
6098             }
6099         }
6100         return r;
6101     }
6102
6103     function quickDiffIEXml(c1, c2){
6104         var d = ++key;
6105         for(var i = 0, len = c1.length; i < len; i++){
6106             c1[i].setAttribute("_qdiff", d);
6107         }
6108         var r = [];
6109         for(var i = 0, len = c2.length; i < len; i++){
6110             if(c2[i].getAttribute("_qdiff") != d){
6111                 r[r.length] = c2[i];
6112             }
6113         }
6114         for(var i = 0, len = c1.length; i < len; i++){
6115            c1[i].removeAttribute("_qdiff");
6116         }
6117         return r;
6118     }
6119
6120     function quickDiff(c1, c2){
6121         var len1 = c1.length;
6122         if(!len1){
6123             return c2;
6124         }
6125         if(isIE && c1[0].selectSingleNode){
6126             return quickDiffIEXml(c1, c2);
6127         }
6128         var d = ++key;
6129         for(var i = 0; i < len1; i++){
6130             c1[i]._qdiff = d;
6131         }
6132         var r = [];
6133         for(var i = 0, len = c2.length; i < len; i++){
6134             if(c2[i]._qdiff != d){
6135                 r[r.length] = c2[i];
6136             }
6137         }
6138         return r;
6139     }
6140
6141     function quickId(ns, mode, root, id){
6142         if(ns == root){
6143            var d = root.ownerDocument || root;
6144            return d.getElementById(id);
6145         }
6146         ns = getNodes(ns, mode, "*");
6147         return byId(ns, null, id);
6148     }
6149
6150     return {
6151         getStyle : function(el, name){
6152             return Roo.fly(el).getStyle(name);
6153         },
6154         /**
6155          * Compiles a selector/xpath query into a reusable function. The returned function
6156          * takes one parameter "root" (optional), which is the context node from where the query should start.
6157          * @param {String} selector The selector/xpath query
6158          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6159          * @return {Function}
6160          */
6161         compile : function(path, type){
6162             type = type || "select";
6163             
6164             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6165             var q = path, mode, lq;
6166             var tk = Roo.DomQuery.matchers;
6167             var tklen = tk.length;
6168             var mm;
6169
6170             // accept leading mode switch
6171             var lmode = q.match(modeRe);
6172             if(lmode && lmode[1]){
6173                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6174                 q = q.replace(lmode[1], "");
6175             }
6176             // strip leading slashes
6177             while(path.substr(0, 1)=="/"){
6178                 path = path.substr(1);
6179             }
6180
6181             while(q && lq != q){
6182                 lq = q;
6183                 var tm = q.match(tagTokenRe);
6184                 if(type == "select"){
6185                     if(tm){
6186                         if(tm[1] == "#"){
6187                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6188                         }else{
6189                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6190                         }
6191                         q = q.replace(tm[0], "");
6192                     }else if(q.substr(0, 1) != '@'){
6193                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6194                     }
6195                 }else{
6196                     if(tm){
6197                         if(tm[1] == "#"){
6198                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6199                         }else{
6200                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6201                         }
6202                         q = q.replace(tm[0], "");
6203                     }
6204                 }
6205                 while(!(mm = q.match(modeRe))){
6206                     var matched = false;
6207                     for(var j = 0; j < tklen; j++){
6208                         var t = tk[j];
6209                         var m = q.match(t.re);
6210                         if(m){
6211                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6212                                                     return m[i];
6213                                                 });
6214                             q = q.replace(m[0], "");
6215                             matched = true;
6216                             break;
6217                         }
6218                     }
6219                     // prevent infinite loop on bad selector
6220                     if(!matched){
6221                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6222                     }
6223                 }
6224                 if(mm[1]){
6225                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6226                     q = q.replace(mm[1], "");
6227                 }
6228             }
6229             fn[fn.length] = "return nodup(n);\n}";
6230             
6231              /** 
6232               * list of variables that need from compression as they are used by eval.
6233              *  eval:var:batch 
6234              *  eval:var:nodup
6235              *  eval:var:byTag
6236              *  eval:var:ById
6237              *  eval:var:getNodes
6238              *  eval:var:quickId
6239              *  eval:var:mode
6240              *  eval:var:root
6241              *  eval:var:n
6242              *  eval:var:byClassName
6243              *  eval:var:byPseudo
6244              *  eval:var:byAttribute
6245              *  eval:var:attrValue
6246              * 
6247              **/ 
6248             eval(fn.join(""));
6249             return f;
6250         },
6251
6252         /**
6253          * Selects a group of elements.
6254          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6255          * @param {Node} root (optional) The start of the query (defaults to document).
6256          * @return {Array}
6257          */
6258         select : function(path, root, type){
6259             if(!root || root == document){
6260                 root = document;
6261             }
6262             if(typeof root == "string"){
6263                 root = document.getElementById(root);
6264             }
6265             var paths = path.split(",");
6266             var results = [];
6267             for(var i = 0, len = paths.length; i < len; i++){
6268                 var p = paths[i].replace(trimRe, "");
6269                 if(!cache[p]){
6270                     cache[p] = Roo.DomQuery.compile(p);
6271                     if(!cache[p]){
6272                         throw p + " is not a valid selector";
6273                     }
6274                 }
6275                 var result = cache[p](root);
6276                 if(result && result != document){
6277                     results = results.concat(result);
6278                 }
6279             }
6280             if(paths.length > 1){
6281                 return nodup(results);
6282             }
6283             return results;
6284         },
6285
6286         /**
6287          * Selects a single element.
6288          * @param {String} selector The selector/xpath query
6289          * @param {Node} root (optional) The start of the query (defaults to document).
6290          * @return {Element}
6291          */
6292         selectNode : function(path, root){
6293             return Roo.DomQuery.select(path, root)[0];
6294         },
6295
6296         /**
6297          * Selects the value of a node, optionally replacing null with the defaultValue.
6298          * @param {String} selector The selector/xpath query
6299          * @param {Node} root (optional) The start of the query (defaults to document).
6300          * @param {String} defaultValue
6301          */
6302         selectValue : function(path, root, defaultValue){
6303             path = path.replace(trimRe, "");
6304             if(!valueCache[path]){
6305                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6306             }
6307             var n = valueCache[path](root);
6308             n = n[0] ? n[0] : n;
6309             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6310             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6311         },
6312
6313         /**
6314          * Selects the value of a node, parsing integers and floats.
6315          * @param {String} selector The selector/xpath query
6316          * @param {Node} root (optional) The start of the query (defaults to document).
6317          * @param {Number} defaultValue
6318          * @return {Number}
6319          */
6320         selectNumber : function(path, root, defaultValue){
6321             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6322             return parseFloat(v);
6323         },
6324
6325         /**
6326          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6327          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6328          * @param {String} selector The simple selector to test
6329          * @return {Boolean}
6330          */
6331         is : function(el, ss){
6332             if(typeof el == "string"){
6333                 el = document.getElementById(el);
6334             }
6335             var isArray = (el instanceof Array);
6336             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6337             return isArray ? (result.length == el.length) : (result.length > 0);
6338         },
6339
6340         /**
6341          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6342          * @param {Array} el An array of elements to filter
6343          * @param {String} selector The simple selector to test
6344          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6345          * the selector instead of the ones that match
6346          * @return {Array}
6347          */
6348         filter : function(els, ss, nonMatches){
6349             ss = ss.replace(trimRe, "");
6350             if(!simpleCache[ss]){
6351                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6352             }
6353             var result = simpleCache[ss](els);
6354             return nonMatches ? quickDiff(result, els) : result;
6355         },
6356
6357         /**
6358          * Collection of matching regular expressions and code snippets.
6359          */
6360         matchers : [{
6361                 re: /^\.([\w-]+)/,
6362                 select: 'n = byClassName(n, null, " {1} ");'
6363             }, {
6364                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6365                 select: 'n = byPseudo(n, "{1}", "{2}");'
6366             },{
6367                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6368                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6369             }, {
6370                 re: /^#([\w-]+)/,
6371                 select: 'n = byId(n, null, "{1}");'
6372             },{
6373                 re: /^@([\w-]+)/,
6374                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6375             }
6376         ],
6377
6378         /**
6379          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6380          * 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;.
6381          */
6382         operators : {
6383             "=" : function(a, v){
6384                 return a == v;
6385             },
6386             "!=" : function(a, v){
6387                 return a != v;
6388             },
6389             "^=" : function(a, v){
6390                 return a && a.substr(0, v.length) == v;
6391             },
6392             "$=" : function(a, v){
6393                 return a && a.substr(a.length-v.length) == v;
6394             },
6395             "*=" : function(a, v){
6396                 return a && a.indexOf(v) !== -1;
6397             },
6398             "%=" : function(a, v){
6399                 return (a % v) == 0;
6400             },
6401             "|=" : function(a, v){
6402                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6403             },
6404             "~=" : function(a, v){
6405                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6406             }
6407         },
6408
6409         /**
6410          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6411          * and the argument (if any) supplied in the selector.
6412          */
6413         pseudos : {
6414             "first-child" : function(c){
6415                 var r = [], ri = -1, n;
6416                 for(var i = 0, ci; ci = n = c[i]; i++){
6417                     while((n = n.previousSibling) && n.nodeType != 1);
6418                     if(!n){
6419                         r[++ri] = ci;
6420                     }
6421                 }
6422                 return r;
6423             },
6424
6425             "last-child" : function(c){
6426                 var r = [], ri = -1, n;
6427                 for(var i = 0, ci; ci = n = c[i]; i++){
6428                     while((n = n.nextSibling) && n.nodeType != 1);
6429                     if(!n){
6430                         r[++ri] = ci;
6431                     }
6432                 }
6433                 return r;
6434             },
6435
6436             "nth-child" : function(c, a) {
6437                 var r = [], ri = -1;
6438                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6439                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6440                 for(var i = 0, n; n = c[i]; i++){
6441                     var pn = n.parentNode;
6442                     if (batch != pn._batch) {
6443                         var j = 0;
6444                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6445                             if(cn.nodeType == 1){
6446                                cn.nodeIndex = ++j;
6447                             }
6448                         }
6449                         pn._batch = batch;
6450                     }
6451                     if (f == 1) {
6452                         if (l == 0 || n.nodeIndex == l){
6453                             r[++ri] = n;
6454                         }
6455                     } else if ((n.nodeIndex + l) % f == 0){
6456                         r[++ri] = n;
6457                     }
6458                 }
6459
6460                 return r;
6461             },
6462
6463             "only-child" : function(c){
6464                 var r = [], ri = -1;;
6465                 for(var i = 0, ci; ci = c[i]; i++){
6466                     if(!prev(ci) && !next(ci)){
6467                         r[++ri] = ci;
6468                     }
6469                 }
6470                 return r;
6471             },
6472
6473             "empty" : function(c){
6474                 var r = [], ri = -1;
6475                 for(var i = 0, ci; ci = c[i]; i++){
6476                     var cns = ci.childNodes, j = 0, cn, empty = true;
6477                     while(cn = cns[j]){
6478                         ++j;
6479                         if(cn.nodeType == 1 || cn.nodeType == 3){
6480                             empty = false;
6481                             break;
6482                         }
6483                     }
6484                     if(empty){
6485                         r[++ri] = ci;
6486                     }
6487                 }
6488                 return r;
6489             },
6490
6491             "contains" : function(c, v){
6492                 var r = [], ri = -1;
6493                 for(var i = 0, ci; ci = c[i]; i++){
6494                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6495                         r[++ri] = ci;
6496                     }
6497                 }
6498                 return r;
6499             },
6500
6501             "nodeValue" : function(c, v){
6502                 var r = [], ri = -1;
6503                 for(var i = 0, ci; ci = c[i]; i++){
6504                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6505                         r[++ri] = ci;
6506                     }
6507                 }
6508                 return r;
6509             },
6510
6511             "checked" : function(c){
6512                 var r = [], ri = -1;
6513                 for(var i = 0, ci; ci = c[i]; i++){
6514                     if(ci.checked == true){
6515                         r[++ri] = ci;
6516                     }
6517                 }
6518                 return r;
6519             },
6520
6521             "not" : function(c, ss){
6522                 return Roo.DomQuery.filter(c, ss, true);
6523             },
6524
6525             "odd" : function(c){
6526                 return this["nth-child"](c, "odd");
6527             },
6528
6529             "even" : function(c){
6530                 return this["nth-child"](c, "even");
6531             },
6532
6533             "nth" : function(c, a){
6534                 return c[a-1] || [];
6535             },
6536
6537             "first" : function(c){
6538                 return c[0] || [];
6539             },
6540
6541             "last" : function(c){
6542                 return c[c.length-1] || [];
6543             },
6544
6545             "has" : function(c, ss){
6546                 var s = Roo.DomQuery.select;
6547                 var r = [], ri = -1;
6548                 for(var i = 0, ci; ci = c[i]; i++){
6549                     if(s(ss, ci).length > 0){
6550                         r[++ri] = ci;
6551                     }
6552                 }
6553                 return r;
6554             },
6555
6556             "next" : function(c, ss){
6557                 var is = Roo.DomQuery.is;
6558                 var r = [], ri = -1;
6559                 for(var i = 0, ci; ci = c[i]; i++){
6560                     var n = next(ci);
6561                     if(n && is(n, ss)){
6562                         r[++ri] = ci;
6563                     }
6564                 }
6565                 return r;
6566             },
6567
6568             "prev" : function(c, ss){
6569                 var is = Roo.DomQuery.is;
6570                 var r = [], ri = -1;
6571                 for(var i = 0, ci; ci = c[i]; i++){
6572                     var n = prev(ci);
6573                     if(n && is(n, ss)){
6574                         r[++ri] = ci;
6575                     }
6576                 }
6577                 return r;
6578             }
6579         }
6580     };
6581 }();
6582
6583 /**
6584  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6585  * @param {String} path The selector/xpath query
6586  * @param {Node} root (optional) The start of the query (defaults to document).
6587  * @return {Array}
6588  * @member Roo
6589  * @method query
6590  */
6591 Roo.query = Roo.DomQuery.select;
6592 /*
6593  * Based on:
6594  * Ext JS Library 1.1.1
6595  * Copyright(c) 2006-2007, Ext JS, LLC.
6596  *
6597  * Originally Released Under LGPL - original licence link has changed is not relivant.
6598  *
6599  * Fork - LGPL
6600  * <script type="text/javascript">
6601  */
6602
6603 /**
6604  * @class Roo.util.Observable
6605  * Base class that provides a common interface for publishing events. Subclasses are expected to
6606  * to have a property "events" with all the events defined.<br>
6607  * For example:
6608  * <pre><code>
6609  Employee = function(name){
6610     this.name = name;
6611     this.addEvents({
6612         "fired" : true,
6613         "quit" : true
6614     });
6615  }
6616  Roo.extend(Employee, Roo.util.Observable);
6617 </code></pre>
6618  * @param {Object} config properties to use (incuding events / listeners)
6619  */
6620
6621 Roo.util.Observable = function(cfg){
6622     
6623     cfg = cfg|| {};
6624     this.addEvents(cfg.events || {});
6625     if (cfg.events) {
6626         delete cfg.events; // make sure
6627     }
6628      
6629     Roo.apply(this, cfg);
6630     
6631     if(this.listeners){
6632         this.on(this.listeners);
6633         delete this.listeners;
6634     }
6635 };
6636 Roo.util.Observable.prototype = {
6637     /** 
6638  * @cfg {Object} listeners  list of events and functions to call for this object, 
6639  * For example :
6640  * <pre><code>
6641     listeners :  { 
6642        'click' : function(e) {
6643            ..... 
6644         } ,
6645         .... 
6646     } 
6647   </code></pre>
6648  */
6649     
6650     
6651     /**
6652      * Fires the specified event with the passed parameters (minus the event name).
6653      * @param {String} eventName
6654      * @param {Object...} args Variable number of parameters are passed to handlers
6655      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6656      */
6657     fireEvent : function(){
6658         var ce = this.events[arguments[0].toLowerCase()];
6659         if(typeof ce == "object"){
6660             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6661         }else{
6662             return true;
6663         }
6664     },
6665
6666     // private
6667     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6668
6669     /**
6670      * Appends an event handler to this component
6671      * @param {String}   eventName The type of event to listen for
6672      * @param {Function} handler The method the event invokes
6673      * @param {Object}   scope (optional) The scope in which to execute the handler
6674      * function. The handler function's "this" context.
6675      * @param {Object}   options (optional) An object containing handler configuration
6676      * properties. This may contain any of the following properties:<ul>
6677      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6678      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6679      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6680      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6681      * by the specified number of milliseconds. If the event fires again within that time, the original
6682      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6683      * </ul><br>
6684      * <p>
6685      * <b>Combining Options</b><br>
6686      * Using the options argument, it is possible to combine different types of listeners:<br>
6687      * <br>
6688      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6689                 <pre><code>
6690                 el.on('click', this.onClick, this, {
6691                         single: true,
6692                 delay: 100,
6693                 forumId: 4
6694                 });
6695                 </code></pre>
6696      * <p>
6697      * <b>Attaching multiple handlers in 1 call</b><br>
6698      * The method also allows for a single argument to be passed which is a config object containing properties
6699      * which specify multiple handlers.
6700      * <pre><code>
6701                 el.on({
6702                         'click': {
6703                         fn: this.onClick,
6704                         scope: this,
6705                         delay: 100
6706                 }, 
6707                 'mouseover': {
6708                         fn: this.onMouseOver,
6709                         scope: this
6710                 },
6711                 'mouseout': {
6712                         fn: this.onMouseOut,
6713                         scope: this
6714                 }
6715                 });
6716                 </code></pre>
6717      * <p>
6718      * Or a shorthand syntax which passes the same scope object to all handlers:
6719         <pre><code>
6720                 el.on({
6721                         'click': this.onClick,
6722                 'mouseover': this.onMouseOver,
6723                 'mouseout': this.onMouseOut,
6724                 scope: this
6725                 });
6726                 </code></pre>
6727      */
6728     addListener : function(eventName, fn, scope, o){
6729         if(typeof eventName == "object"){
6730             o = eventName;
6731             for(var e in o){
6732                 if(this.filterOptRe.test(e)){
6733                     continue;
6734                 }
6735                 if(typeof o[e] == "function"){
6736                     // shared options
6737                     this.addListener(e, o[e], o.scope,  o);
6738                 }else{
6739                     // individual options
6740                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6741                 }
6742             }
6743             return;
6744         }
6745         o = (!o || typeof o == "boolean") ? {} : o;
6746         eventName = eventName.toLowerCase();
6747         var ce = this.events[eventName] || true;
6748         if(typeof ce == "boolean"){
6749             ce = new Roo.util.Event(this, eventName);
6750             this.events[eventName] = ce;
6751         }
6752         ce.addListener(fn, scope, o);
6753     },
6754
6755     /**
6756      * Removes a listener
6757      * @param {String}   eventName     The type of event to listen for
6758      * @param {Function} handler        The handler to remove
6759      * @param {Object}   scope  (optional) The scope (this object) for the handler
6760      */
6761     removeListener : function(eventName, fn, scope){
6762         var ce = this.events[eventName.toLowerCase()];
6763         if(typeof ce == "object"){
6764             ce.removeListener(fn, scope);
6765         }
6766     },
6767
6768     /**
6769      * Removes all listeners for this object
6770      */
6771     purgeListeners : function(){
6772         for(var evt in this.events){
6773             if(typeof this.events[evt] == "object"){
6774                  this.events[evt].clearListeners();
6775             }
6776         }
6777     },
6778
6779     relayEvents : function(o, events){
6780         var createHandler = function(ename){
6781             return function(){
6782                  
6783                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6784             };
6785         };
6786         for(var i = 0, len = events.length; i < len; i++){
6787             var ename = events[i];
6788             if(!this.events[ename]){
6789                 this.events[ename] = true;
6790             };
6791             o.on(ename, createHandler(ename), this);
6792         }
6793     },
6794
6795     /**
6796      * Used to define events on this Observable
6797      * @param {Object} object The object with the events defined
6798      */
6799     addEvents : function(o){
6800         if(!this.events){
6801             this.events = {};
6802         }
6803         Roo.applyIf(this.events, o);
6804     },
6805
6806     /**
6807      * Checks to see if this object has any listeners for a specified event
6808      * @param {String} eventName The name of the event to check for
6809      * @return {Boolean} True if the event is being listened for, else false
6810      */
6811     hasListener : function(eventName){
6812         var e = this.events[eventName];
6813         return typeof e == "object" && e.listeners.length > 0;
6814     }
6815 };
6816 /**
6817  * Appends an event handler to this element (shorthand for addListener)
6818  * @param {String}   eventName     The type of event to listen for
6819  * @param {Function} handler        The method the event invokes
6820  * @param {Object}   scope (optional) The scope in which to execute the handler
6821  * function. The handler function's "this" context.
6822  * @param {Object}   options  (optional)
6823  * @method
6824  */
6825 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6826 /**
6827  * Removes a listener (shorthand for removeListener)
6828  * @param {String}   eventName     The type of event to listen for
6829  * @param {Function} handler        The handler to remove
6830  * @param {Object}   scope  (optional) The scope (this object) for the handler
6831  * @method
6832  */
6833 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6834
6835 /**
6836  * Starts capture on the specified Observable. All events will be passed
6837  * to the supplied function with the event name + standard signature of the event
6838  * <b>before</b> the event is fired. If the supplied function returns false,
6839  * the event will not fire.
6840  * @param {Observable} o The Observable to capture
6841  * @param {Function} fn The function to call
6842  * @param {Object} scope (optional) The scope (this object) for the fn
6843  * @static
6844  */
6845 Roo.util.Observable.capture = function(o, fn, scope){
6846     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6847 };
6848
6849 /**
6850  * Removes <b>all</b> added captures from the Observable.
6851  * @param {Observable} o The Observable to release
6852  * @static
6853  */
6854 Roo.util.Observable.releaseCapture = function(o){
6855     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6856 };
6857
6858 (function(){
6859
6860     var createBuffered = function(h, o, scope){
6861         var task = new Roo.util.DelayedTask();
6862         return function(){
6863             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6864         };
6865     };
6866
6867     var createSingle = function(h, e, fn, scope){
6868         return function(){
6869             e.removeListener(fn, scope);
6870             return h.apply(scope, arguments);
6871         };
6872     };
6873
6874     var createDelayed = function(h, o, scope){
6875         return function(){
6876             var args = Array.prototype.slice.call(arguments, 0);
6877             setTimeout(function(){
6878                 h.apply(scope, args);
6879             }, o.delay || 10);
6880         };
6881     };
6882
6883     Roo.util.Event = function(obj, name){
6884         this.name = name;
6885         this.obj = obj;
6886         this.listeners = [];
6887     };
6888
6889     Roo.util.Event.prototype = {
6890         addListener : function(fn, scope, options){
6891             var o = options || {};
6892             scope = scope || this.obj;
6893             if(!this.isListening(fn, scope)){
6894                 var l = {fn: fn, scope: scope, options: o};
6895                 var h = fn;
6896                 if(o.delay){
6897                     h = createDelayed(h, o, scope);
6898                 }
6899                 if(o.single){
6900                     h = createSingle(h, this, fn, scope);
6901                 }
6902                 if(o.buffer){
6903                     h = createBuffered(h, o, scope);
6904                 }
6905                 l.fireFn = h;
6906                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6907                     this.listeners.push(l);
6908                 }else{
6909                     this.listeners = this.listeners.slice(0);
6910                     this.listeners.push(l);
6911                 }
6912             }
6913         },
6914
6915         findListener : function(fn, scope){
6916             scope = scope || this.obj;
6917             var ls = this.listeners;
6918             for(var i = 0, len = ls.length; i < len; i++){
6919                 var l = ls[i];
6920                 if(l.fn == fn && l.scope == scope){
6921                     return i;
6922                 }
6923             }
6924             return -1;
6925         },
6926
6927         isListening : function(fn, scope){
6928             return this.findListener(fn, scope) != -1;
6929         },
6930
6931         removeListener : function(fn, scope){
6932             var index;
6933             if((index = this.findListener(fn, scope)) != -1){
6934                 if(!this.firing){
6935                     this.listeners.splice(index, 1);
6936                 }else{
6937                     this.listeners = this.listeners.slice(0);
6938                     this.listeners.splice(index, 1);
6939                 }
6940                 return true;
6941             }
6942             return false;
6943         },
6944
6945         clearListeners : function(){
6946             this.listeners = [];
6947         },
6948
6949         fire : function(){
6950             var ls = this.listeners, scope, len = ls.length;
6951             if(len > 0){
6952                 this.firing = true;
6953                 var args = Array.prototype.slice.call(arguments, 0);                
6954                 for(var i = 0; i < len; i++){
6955                     var l = ls[i];
6956                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6957                         this.firing = false;
6958                         return false;
6959                     }
6960                 }
6961                 this.firing = false;
6962             }
6963             return true;
6964         }
6965     };
6966 })();/*
6967  * RooJS Library 
6968  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6969  *
6970  * Licence LGPL 
6971  *
6972  */
6973  
6974 /**
6975  * @class Roo.Document
6976  * @extends Roo.util.Observable
6977  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6978  * 
6979  * @param {Object} config the methods and properties of the 'base' class for the application.
6980  * 
6981  *  Generic Page handler - implement this to start your app..
6982  * 
6983  * eg.
6984  *  MyProject = new Roo.Document({
6985         events : {
6986             'load' : true // your events..
6987         },
6988         listeners : {
6989             'ready' : function() {
6990                 // fired on Roo.onReady()
6991             }
6992         }
6993  * 
6994  */
6995 Roo.Document = function(cfg) {
6996      
6997     this.addEvents({ 
6998         'ready' : true
6999     });
7000     Roo.util.Observable.call(this,cfg);
7001     
7002     var _this = this;
7003     
7004     Roo.onReady(function() {
7005         _this.fireEvent('ready');
7006     },null,false);
7007     
7008     
7009 }
7010
7011 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7012  * Based on:
7013  * Ext JS Library 1.1.1
7014  * Copyright(c) 2006-2007, Ext JS, LLC.
7015  *
7016  * Originally Released Under LGPL - original licence link has changed is not relivant.
7017  *
7018  * Fork - LGPL
7019  * <script type="text/javascript">
7020  */
7021
7022 /**
7023  * @class Roo.EventManager
7024  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7025  * several useful events directly.
7026  * See {@link Roo.EventObject} for more details on normalized event objects.
7027  * @static
7028  */
7029 Roo.EventManager = function(){
7030     var docReadyEvent, docReadyProcId, docReadyState = false;
7031     var resizeEvent, resizeTask, textEvent, textSize;
7032     var E = Roo.lib.Event;
7033     var D = Roo.lib.Dom;
7034
7035     
7036     
7037
7038     var fireDocReady = function(){
7039         if(!docReadyState){
7040             docReadyState = true;
7041             Roo.isReady = true;
7042             if(docReadyProcId){
7043                 clearInterval(docReadyProcId);
7044             }
7045             if(Roo.isGecko || Roo.isOpera) {
7046                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7047             }
7048             if(Roo.isIE){
7049                 var defer = document.getElementById("ie-deferred-loader");
7050                 if(defer){
7051                     defer.onreadystatechange = null;
7052                     defer.parentNode.removeChild(defer);
7053                 }
7054             }
7055             if(docReadyEvent){
7056                 docReadyEvent.fire();
7057                 docReadyEvent.clearListeners();
7058             }
7059         }
7060     };
7061     
7062     var initDocReady = function(){
7063         docReadyEvent = new Roo.util.Event();
7064         if(Roo.isGecko || Roo.isOpera) {
7065             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7066         }else if(Roo.isIE){
7067             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7068             var defer = document.getElementById("ie-deferred-loader");
7069             defer.onreadystatechange = function(){
7070                 if(this.readyState == "complete"){
7071                     fireDocReady();
7072                 }
7073             };
7074         }else if(Roo.isSafari){ 
7075             docReadyProcId = setInterval(function(){
7076                 var rs = document.readyState;
7077                 if(rs == "complete") {
7078                     fireDocReady();     
7079                  }
7080             }, 10);
7081         }
7082         // no matter what, make sure it fires on load
7083         E.on(window, "load", fireDocReady);
7084     };
7085
7086     var createBuffered = function(h, o){
7087         var task = new Roo.util.DelayedTask(h);
7088         return function(e){
7089             // create new event object impl so new events don't wipe out properties
7090             e = new Roo.EventObjectImpl(e);
7091             task.delay(o.buffer, h, null, [e]);
7092         };
7093     };
7094
7095     var createSingle = function(h, el, ename, fn){
7096         return function(e){
7097             Roo.EventManager.removeListener(el, ename, fn);
7098             h(e);
7099         };
7100     };
7101
7102     var createDelayed = function(h, o){
7103         return function(e){
7104             // create new event object impl so new events don't wipe out properties
7105             e = new Roo.EventObjectImpl(e);
7106             setTimeout(function(){
7107                 h(e);
7108             }, o.delay || 10);
7109         };
7110     };
7111     var transitionEndVal = false;
7112     
7113     var transitionEnd = function()
7114     {
7115         if (transitionEndVal) {
7116             return transitionEndVal;
7117         }
7118         var el = document.createElement('div');
7119
7120         var transEndEventNames = {
7121             WebkitTransition : 'webkitTransitionEnd',
7122             MozTransition    : 'transitionend',
7123             OTransition      : 'oTransitionEnd otransitionend',
7124             transition       : 'transitionend'
7125         };
7126     
7127         for (var name in transEndEventNames) {
7128             if (el.style[name] !== undefined) {
7129                 transitionEndVal = transEndEventNames[name];
7130                 return  transitionEndVal ;
7131             }
7132         }
7133     }
7134     
7135   
7136
7137     var listen = function(element, ename, opt, fn, scope)
7138     {
7139         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7140         fn = fn || o.fn; scope = scope || o.scope;
7141         var el = Roo.getDom(element);
7142         
7143         
7144         if(!el){
7145             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7146         }
7147         
7148         if (ename == 'transitionend') {
7149             ename = transitionEnd();
7150         }
7151         var h = function(e){
7152             e = Roo.EventObject.setEvent(e);
7153             var t;
7154             if(o.delegate){
7155                 t = e.getTarget(o.delegate, el);
7156                 if(!t){
7157                     return;
7158                 }
7159             }else{
7160                 t = e.target;
7161             }
7162             if(o.stopEvent === true){
7163                 e.stopEvent();
7164             }
7165             if(o.preventDefault === true){
7166                e.preventDefault();
7167             }
7168             if(o.stopPropagation === true){
7169                 e.stopPropagation();
7170             }
7171
7172             if(o.normalized === false){
7173                 e = e.browserEvent;
7174             }
7175
7176             fn.call(scope || el, e, t, o);
7177         };
7178         if(o.delay){
7179             h = createDelayed(h, o);
7180         }
7181         if(o.single){
7182             h = createSingle(h, el, ename, fn);
7183         }
7184         if(o.buffer){
7185             h = createBuffered(h, o);
7186         }
7187         
7188         fn._handlers = fn._handlers || [];
7189         
7190         
7191         fn._handlers.push([Roo.id(el), ename, h]);
7192         
7193         
7194          
7195         E.on(el, ename, h); // this adds the actuall listener to the object..
7196         
7197         
7198         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7199             el.addEventListener("DOMMouseScroll", h, false);
7200             E.on(window, 'unload', function(){
7201                 el.removeEventListener("DOMMouseScroll", h, false);
7202             });
7203         }
7204         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7205             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7206         }
7207         return h;
7208     };
7209
7210     var stopListening = function(el, ename, fn){
7211         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7212         if(hds){
7213             for(var i = 0, len = hds.length; i < len; i++){
7214                 var h = hds[i];
7215                 if(h[0] == id && h[1] == ename){
7216                     hd = h[2];
7217                     hds.splice(i, 1);
7218                     break;
7219                 }
7220             }
7221         }
7222         E.un(el, ename, hd);
7223         el = Roo.getDom(el);
7224         if(ename == "mousewheel" && el.addEventListener){
7225             el.removeEventListener("DOMMouseScroll", hd, false);
7226         }
7227         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7228             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7229         }
7230     };
7231
7232     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7233     
7234     var pub = {
7235         
7236         
7237         /** 
7238          * Fix for doc tools
7239          * @scope Roo.EventManager
7240          */
7241         
7242         
7243         /** 
7244          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7245          * object with a Roo.EventObject
7246          * @param {Function} fn        The method the event invokes
7247          * @param {Object}   scope    An object that becomes the scope of the handler
7248          * @param {boolean}  override If true, the obj passed in becomes
7249          *                             the execution scope of the listener
7250          * @return {Function} The wrapped function
7251          * @deprecated
7252          */
7253         wrap : function(fn, scope, override){
7254             return function(e){
7255                 Roo.EventObject.setEvent(e);
7256                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7257             };
7258         },
7259         
7260         /**
7261      * Appends an event handler to an element (shorthand for addListener)
7262      * @param {String/HTMLElement}   element        The html element or id to assign the
7263      * @param {String}   eventName The type of event to listen for
7264      * @param {Function} handler The method the event invokes
7265      * @param {Object}   scope (optional) The scope in which to execute the handler
7266      * function. The handler function's "this" context.
7267      * @param {Object}   options (optional) An object containing handler configuration
7268      * properties. This may contain any of the following properties:<ul>
7269      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7270      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7271      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7272      * <li>preventDefault {Boolean} True to prevent the default action</li>
7273      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7274      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7275      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7276      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7277      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7278      * by the specified number of milliseconds. If the event fires again within that time, the original
7279      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7280      * </ul><br>
7281      * <p>
7282      * <b>Combining Options</b><br>
7283      * Using the options argument, it is possible to combine different types of listeners:<br>
7284      * <br>
7285      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7286      * Code:<pre><code>
7287 el.on('click', this.onClick, this, {
7288     single: true,
7289     delay: 100,
7290     stopEvent : true,
7291     forumId: 4
7292 });</code></pre>
7293      * <p>
7294      * <b>Attaching multiple handlers in 1 call</b><br>
7295       * The method also allows for a single argument to be passed which is a config object containing properties
7296      * which specify multiple handlers.
7297      * <p>
7298      * Code:<pre><code>
7299 el.on({
7300     'click' : {
7301         fn: this.onClick
7302         scope: this,
7303         delay: 100
7304     },
7305     'mouseover' : {
7306         fn: this.onMouseOver
7307         scope: this
7308     },
7309     'mouseout' : {
7310         fn: this.onMouseOut
7311         scope: this
7312     }
7313 });</code></pre>
7314      * <p>
7315      * Or a shorthand syntax:<br>
7316      * Code:<pre><code>
7317 el.on({
7318     'click' : this.onClick,
7319     'mouseover' : this.onMouseOver,
7320     'mouseout' : this.onMouseOut
7321     scope: this
7322 });</code></pre>
7323      */
7324         addListener : function(element, eventName, fn, scope, options){
7325             if(typeof eventName == "object"){
7326                 var o = eventName;
7327                 for(var e in o){
7328                     if(propRe.test(e)){
7329                         continue;
7330                     }
7331                     if(typeof o[e] == "function"){
7332                         // shared options
7333                         listen(element, e, o, o[e], o.scope);
7334                     }else{
7335                         // individual options
7336                         listen(element, e, o[e]);
7337                     }
7338                 }
7339                 return;
7340             }
7341             return listen(element, eventName, options, fn, scope);
7342         },
7343         
7344         /**
7345          * Removes an event handler
7346          *
7347          * @param {String/HTMLElement}   element        The id or html element to remove the 
7348          *                             event from
7349          * @param {String}   eventName     The type of event
7350          * @param {Function} fn
7351          * @return {Boolean} True if a listener was actually removed
7352          */
7353         removeListener : function(element, eventName, fn){
7354             return stopListening(element, eventName, fn);
7355         },
7356         
7357         /**
7358          * Fires when the document is ready (before onload and before images are loaded). Can be 
7359          * accessed shorthanded Roo.onReady().
7360          * @param {Function} fn        The method the event invokes
7361          * @param {Object}   scope    An  object that becomes the scope of the handler
7362          * @param {boolean}  options
7363          */
7364         onDocumentReady : function(fn, scope, options){
7365             if(docReadyState){ // if it already fired
7366                 docReadyEvent.addListener(fn, scope, options);
7367                 docReadyEvent.fire();
7368                 docReadyEvent.clearListeners();
7369                 return;
7370             }
7371             if(!docReadyEvent){
7372                 initDocReady();
7373             }
7374             docReadyEvent.addListener(fn, scope, options);
7375         },
7376         
7377         /**
7378          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7379          * @param {Function} fn        The method the event invokes
7380          * @param {Object}   scope    An object that becomes the scope of the handler
7381          * @param {boolean}  options
7382          */
7383         onWindowResize : function(fn, scope, options)
7384         {
7385             if(!resizeEvent){
7386                 resizeEvent = new Roo.util.Event();
7387                 resizeTask = new Roo.util.DelayedTask(function(){
7388                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7389                 });
7390                 E.on(window, "resize", function()
7391                 {
7392                     if (Roo.isIE) {
7393                         resizeTask.delay(50);
7394                     } else {
7395                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7396                     }
7397                 });
7398             }
7399             resizeEvent.addListener(fn, scope, options);
7400         },
7401
7402         /**
7403          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7404          * @param {Function} fn        The method the event invokes
7405          * @param {Object}   scope    An object that becomes the scope of the handler
7406          * @param {boolean}  options
7407          */
7408         onTextResize : function(fn, scope, options){
7409             if(!textEvent){
7410                 textEvent = new Roo.util.Event();
7411                 var textEl = new Roo.Element(document.createElement('div'));
7412                 textEl.dom.className = 'x-text-resize';
7413                 textEl.dom.innerHTML = 'X';
7414                 textEl.appendTo(document.body);
7415                 textSize = textEl.dom.offsetHeight;
7416                 setInterval(function(){
7417                     if(textEl.dom.offsetHeight != textSize){
7418                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7419                     }
7420                 }, this.textResizeInterval);
7421             }
7422             textEvent.addListener(fn, scope, options);
7423         },
7424
7425         /**
7426          * Removes the passed window resize listener.
7427          * @param {Function} fn        The method the event invokes
7428          * @param {Object}   scope    The scope of handler
7429          */
7430         removeResizeListener : function(fn, scope){
7431             if(resizeEvent){
7432                 resizeEvent.removeListener(fn, scope);
7433             }
7434         },
7435
7436         // private
7437         fireResize : function(){
7438             if(resizeEvent){
7439                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7440             }   
7441         },
7442         /**
7443          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7444          */
7445         ieDeferSrc : false,
7446         /**
7447          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7448          */
7449         textResizeInterval : 50
7450     };
7451     
7452     /**
7453      * Fix for doc tools
7454      * @scopeAlias pub=Roo.EventManager
7455      */
7456     
7457      /**
7458      * Appends an event handler to an element (shorthand for addListener)
7459      * @param {String/HTMLElement}   element        The html element or id to assign the
7460      * @param {String}   eventName The type of event to listen for
7461      * @param {Function} handler The method the event invokes
7462      * @param {Object}   scope (optional) The scope in which to execute the handler
7463      * function. The handler function's "this" context.
7464      * @param {Object}   options (optional) An object containing handler configuration
7465      * properties. This may contain any of the following properties:<ul>
7466      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7467      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7468      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7469      * <li>preventDefault {Boolean} True to prevent the default action</li>
7470      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7471      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7472      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7473      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7474      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7475      * by the specified number of milliseconds. If the event fires again within that time, the original
7476      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7477      * </ul><br>
7478      * <p>
7479      * <b>Combining Options</b><br>
7480      * Using the options argument, it is possible to combine different types of listeners:<br>
7481      * <br>
7482      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7483      * Code:<pre><code>
7484 el.on('click', this.onClick, this, {
7485     single: true,
7486     delay: 100,
7487     stopEvent : true,
7488     forumId: 4
7489 });</code></pre>
7490      * <p>
7491      * <b>Attaching multiple handlers in 1 call</b><br>
7492       * The method also allows for a single argument to be passed which is a config object containing properties
7493      * which specify multiple handlers.
7494      * <p>
7495      * Code:<pre><code>
7496 el.on({
7497     'click' : {
7498         fn: this.onClick
7499         scope: this,
7500         delay: 100
7501     },
7502     'mouseover' : {
7503         fn: this.onMouseOver
7504         scope: this
7505     },
7506     'mouseout' : {
7507         fn: this.onMouseOut
7508         scope: this
7509     }
7510 });</code></pre>
7511      * <p>
7512      * Or a shorthand syntax:<br>
7513      * Code:<pre><code>
7514 el.on({
7515     'click' : this.onClick,
7516     'mouseover' : this.onMouseOver,
7517     'mouseout' : this.onMouseOut
7518     scope: this
7519 });</code></pre>
7520      */
7521     pub.on = pub.addListener;
7522     pub.un = pub.removeListener;
7523
7524     pub.stoppedMouseDownEvent = new Roo.util.Event();
7525     return pub;
7526 }();
7527 /**
7528   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7529   * @param {Function} fn        The method the event invokes
7530   * @param {Object}   scope    An  object that becomes the scope of the handler
7531   * @param {boolean}  override If true, the obj passed in becomes
7532   *                             the execution scope of the listener
7533   * @member Roo
7534   * @method onReady
7535  */
7536 Roo.onReady = Roo.EventManager.onDocumentReady;
7537
7538 Roo.onReady(function(){
7539     var bd = Roo.get(document.body);
7540     if(!bd){ return; }
7541
7542     var cls = [
7543             Roo.isIE ? "roo-ie"
7544             : Roo.isIE11 ? "roo-ie11"
7545             : Roo.isEdge ? "roo-edge"
7546             : Roo.isGecko ? "roo-gecko"
7547             : Roo.isOpera ? "roo-opera"
7548             : Roo.isSafari ? "roo-safari" : ""];
7549
7550     if(Roo.isMac){
7551         cls.push("roo-mac");
7552     }
7553     if(Roo.isLinux){
7554         cls.push("roo-linux");
7555     }
7556     if(Roo.isIOS){
7557         cls.push("roo-ios");
7558     }
7559     if(Roo.isTouch){
7560         cls.push("roo-touch");
7561     }
7562     if(Roo.isBorderBox){
7563         cls.push('roo-border-box');
7564     }
7565     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7566         var p = bd.dom.parentNode;
7567         if(p){
7568             p.className += ' roo-strict';
7569         }
7570     }
7571     bd.addClass(cls.join(' '));
7572 });
7573
7574 /**
7575  * @class Roo.EventObject
7576  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7577  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7578  * Example:
7579  * <pre><code>
7580  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7581     e.preventDefault();
7582     var target = e.getTarget();
7583     ...
7584  }
7585  var myDiv = Roo.get("myDiv");
7586  myDiv.on("click", handleClick);
7587  //or
7588  Roo.EventManager.on("myDiv", 'click', handleClick);
7589  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7590  </code></pre>
7591  * @static
7592  */
7593 Roo.EventObject = function(){
7594     
7595     var E = Roo.lib.Event;
7596     
7597     // safari keypress events for special keys return bad keycodes
7598     var safariKeys = {
7599         63234 : 37, // left
7600         63235 : 39, // right
7601         63232 : 38, // up
7602         63233 : 40, // down
7603         63276 : 33, // page up
7604         63277 : 34, // page down
7605         63272 : 46, // delete
7606         63273 : 36, // home
7607         63275 : 35  // end
7608     };
7609
7610     // normalize button clicks
7611     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7612                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7613
7614     Roo.EventObjectImpl = function(e){
7615         if(e){
7616             this.setEvent(e.browserEvent || e);
7617         }
7618     };
7619     Roo.EventObjectImpl.prototype = {
7620         /**
7621          * Used to fix doc tools.
7622          * @scope Roo.EventObject.prototype
7623          */
7624             
7625
7626         
7627         
7628         /** The normal browser event */
7629         browserEvent : null,
7630         /** The button pressed in a mouse event */
7631         button : -1,
7632         /** True if the shift key was down during the event */
7633         shiftKey : false,
7634         /** True if the control key was down during the event */
7635         ctrlKey : false,
7636         /** True if the alt key was down during the event */
7637         altKey : false,
7638
7639         /** Key constant 
7640         * @type Number */
7641         BACKSPACE : 8,
7642         /** Key constant 
7643         * @type Number */
7644         TAB : 9,
7645         /** Key constant 
7646         * @type Number */
7647         RETURN : 13,
7648         /** Key constant 
7649         * @type Number */
7650         ENTER : 13,
7651         /** Key constant 
7652         * @type Number */
7653         SHIFT : 16,
7654         /** Key constant 
7655         * @type Number */
7656         CONTROL : 17,
7657         /** Key constant 
7658         * @type Number */
7659         ESC : 27,
7660         /** Key constant 
7661         * @type Number */
7662         SPACE : 32,
7663         /** Key constant 
7664         * @type Number */
7665         PAGEUP : 33,
7666         /** Key constant 
7667         * @type Number */
7668         PAGEDOWN : 34,
7669         /** Key constant 
7670         * @type Number */
7671         END : 35,
7672         /** Key constant 
7673         * @type Number */
7674         HOME : 36,
7675         /** Key constant 
7676         * @type Number */
7677         LEFT : 37,
7678         /** Key constant 
7679         * @type Number */
7680         UP : 38,
7681         /** Key constant 
7682         * @type Number */
7683         RIGHT : 39,
7684         /** Key constant 
7685         * @type Number */
7686         DOWN : 40,
7687         /** Key constant 
7688         * @type Number */
7689         DELETE : 46,
7690         /** Key constant 
7691         * @type Number */
7692         F5 : 116,
7693
7694            /** @private */
7695         setEvent : function(e){
7696             if(e == this || (e && e.browserEvent)){ // already wrapped
7697                 return e;
7698             }
7699             this.browserEvent = e;
7700             if(e){
7701                 // normalize buttons
7702                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7703                 if(e.type == 'click' && this.button == -1){
7704                     this.button = 0;
7705                 }
7706                 this.type = e.type;
7707                 this.shiftKey = e.shiftKey;
7708                 // mac metaKey behaves like ctrlKey
7709                 this.ctrlKey = e.ctrlKey || e.metaKey;
7710                 this.altKey = e.altKey;
7711                 // in getKey these will be normalized for the mac
7712                 this.keyCode = e.keyCode;
7713                 // keyup warnings on firefox.
7714                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7715                 // cache the target for the delayed and or buffered events
7716                 this.target = E.getTarget(e);
7717                 // same for XY
7718                 this.xy = E.getXY(e);
7719             }else{
7720                 this.button = -1;
7721                 this.shiftKey = false;
7722                 this.ctrlKey = false;
7723                 this.altKey = false;
7724                 this.keyCode = 0;
7725                 this.charCode =0;
7726                 this.target = null;
7727                 this.xy = [0, 0];
7728             }
7729             return this;
7730         },
7731
7732         /**
7733          * Stop the event (preventDefault and stopPropagation)
7734          */
7735         stopEvent : function(){
7736             if(this.browserEvent){
7737                 if(this.browserEvent.type == 'mousedown'){
7738                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7739                 }
7740                 E.stopEvent(this.browserEvent);
7741             }
7742         },
7743
7744         /**
7745          * Prevents the browsers default handling of the event.
7746          */
7747         preventDefault : function(){
7748             if(this.browserEvent){
7749                 E.preventDefault(this.browserEvent);
7750             }
7751         },
7752
7753         /** @private */
7754         isNavKeyPress : function(){
7755             var k = this.keyCode;
7756             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7757             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7758         },
7759
7760         isSpecialKey : function(){
7761             var k = this.keyCode;
7762             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7763             (k == 16) || (k == 17) ||
7764             (k >= 18 && k <= 20) ||
7765             (k >= 33 && k <= 35) ||
7766             (k >= 36 && k <= 39) ||
7767             (k >= 44 && k <= 45);
7768         },
7769         /**
7770          * Cancels bubbling of the event.
7771          */
7772         stopPropagation : function(){
7773             if(this.browserEvent){
7774                 if(this.type == 'mousedown'){
7775                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7776                 }
7777                 E.stopPropagation(this.browserEvent);
7778             }
7779         },
7780
7781         /**
7782          * Gets the key code for the event.
7783          * @return {Number}
7784          */
7785         getCharCode : function(){
7786             return this.charCode || this.keyCode;
7787         },
7788
7789         /**
7790          * Returns a normalized keyCode for the event.
7791          * @return {Number} The key code
7792          */
7793         getKey : function(){
7794             var k = this.keyCode || this.charCode;
7795             return Roo.isSafari ? (safariKeys[k] || k) : k;
7796         },
7797
7798         /**
7799          * Gets the x coordinate of the event.
7800          * @return {Number}
7801          */
7802         getPageX : function(){
7803             return this.xy[0];
7804         },
7805
7806         /**
7807          * Gets the y coordinate of the event.
7808          * @return {Number}
7809          */
7810         getPageY : function(){
7811             return this.xy[1];
7812         },
7813
7814         /**
7815          * Gets the time of the event.
7816          * @return {Number}
7817          */
7818         getTime : function(){
7819             if(this.browserEvent){
7820                 return E.getTime(this.browserEvent);
7821             }
7822             return null;
7823         },
7824
7825         /**
7826          * Gets the page coordinates of the event.
7827          * @return {Array} The xy values like [x, y]
7828          */
7829         getXY : function(){
7830             return this.xy;
7831         },
7832
7833         /**
7834          * Gets the target for the event.
7835          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7836          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7837                 search as a number or element (defaults to 10 || document.body)
7838          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7839          * @return {HTMLelement}
7840          */
7841         getTarget : function(selector, maxDepth, returnEl){
7842             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7843         },
7844         /**
7845          * Gets the related target.
7846          * @return {HTMLElement}
7847          */
7848         getRelatedTarget : function(){
7849             if(this.browserEvent){
7850                 return E.getRelatedTarget(this.browserEvent);
7851             }
7852             return null;
7853         },
7854
7855         /**
7856          * Normalizes mouse wheel delta across browsers
7857          * @return {Number} The delta
7858          */
7859         getWheelDelta : function(){
7860             var e = this.browserEvent;
7861             var delta = 0;
7862             if(e.wheelDelta){ /* IE/Opera. */
7863                 delta = e.wheelDelta/120;
7864             }else if(e.detail){ /* Mozilla case. */
7865                 delta = -e.detail/3;
7866             }
7867             return delta;
7868         },
7869
7870         /**
7871          * Returns true if the control, meta, shift or alt key was pressed during this event.
7872          * @return {Boolean}
7873          */
7874         hasModifier : function(){
7875             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7876         },
7877
7878         /**
7879          * Returns true if the target of this event equals el or is a child of el
7880          * @param {String/HTMLElement/Element} el
7881          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7882          * @return {Boolean}
7883          */
7884         within : function(el, related){
7885             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7886             return t && Roo.fly(el).contains(t);
7887         },
7888
7889         getPoint : function(){
7890             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7891         }
7892     };
7893
7894     return new Roo.EventObjectImpl();
7895 }();
7896             
7897     /*
7898  * Based on:
7899  * Ext JS Library 1.1.1
7900  * Copyright(c) 2006-2007, Ext JS, LLC.
7901  *
7902  * Originally Released Under LGPL - original licence link has changed is not relivant.
7903  *
7904  * Fork - LGPL
7905  * <script type="text/javascript">
7906  */
7907
7908  
7909 // was in Composite Element!??!?!
7910  
7911 (function(){
7912     var D = Roo.lib.Dom;
7913     var E = Roo.lib.Event;
7914     var A = Roo.lib.Anim;
7915
7916     // local style camelizing for speed
7917     var propCache = {};
7918     var camelRe = /(-[a-z])/gi;
7919     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7920     var view = document.defaultView;
7921
7922 /**
7923  * @class Roo.Element
7924  * Represents an Element in the DOM.<br><br>
7925  * Usage:<br>
7926 <pre><code>
7927 var el = Roo.get("my-div");
7928
7929 // or with getEl
7930 var el = getEl("my-div");
7931
7932 // or with a DOM element
7933 var el = Roo.get(myDivElement);
7934 </code></pre>
7935  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7936  * each call instead of constructing a new one.<br><br>
7937  * <b>Animations</b><br />
7938  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7939  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7940 <pre>
7941 Option    Default   Description
7942 --------- --------  ---------------------------------------------
7943 duration  .35       The duration of the animation in seconds
7944 easing    easeOut   The YUI easing method
7945 callback  none      A function to execute when the anim completes
7946 scope     this      The scope (this) of the callback function
7947 </pre>
7948 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7949 * manipulate the animation. Here's an example:
7950 <pre><code>
7951 var el = Roo.get("my-div");
7952
7953 // no animation
7954 el.setWidth(100);
7955
7956 // default animation
7957 el.setWidth(100, true);
7958
7959 // animation with some options set
7960 el.setWidth(100, {
7961     duration: 1,
7962     callback: this.foo,
7963     scope: this
7964 });
7965
7966 // using the "anim" property to get the Anim object
7967 var opt = {
7968     duration: 1,
7969     callback: this.foo,
7970     scope: this
7971 };
7972 el.setWidth(100, opt);
7973 ...
7974 if(opt.anim.isAnimated()){
7975     opt.anim.stop();
7976 }
7977 </code></pre>
7978 * <b> Composite (Collections of) Elements</b><br />
7979  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7980  * @constructor Create a new Element directly.
7981  * @param {String/HTMLElement} element
7982  * @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).
7983  */
7984     Roo.Element = function(element, forceNew)
7985     {
7986         var dom = typeof element == "string" ?
7987                 document.getElementById(element) : element;
7988         
7989         this.listeners = {};
7990         
7991         if(!dom){ // invalid id/element
7992             return null;
7993         }
7994         var id = dom.id;
7995         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7996             return Roo.Element.cache[id];
7997         }
7998
7999         /**
8000          * The DOM element
8001          * @type HTMLElement
8002          */
8003         this.dom = dom;
8004
8005         /**
8006          * The DOM element ID
8007          * @type String
8008          */
8009         this.id = id || Roo.id(dom);
8010         
8011         return this; // assumed for cctor?
8012     };
8013
8014     var El = Roo.Element;
8015
8016     El.prototype = {
8017         /**
8018          * The element's default display mode  (defaults to "") 
8019          * @type String
8020          */
8021         originalDisplay : "",
8022
8023         
8024         // note this is overridden in BS version..
8025         visibilityMode : 1, 
8026         /**
8027          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8028          * @type String
8029          */
8030         defaultUnit : "px",
8031         
8032         /**
8033          * Sets the element's visibility mode. When setVisible() is called it
8034          * will use this to determine whether to set the visibility or the display property.
8035          * @param visMode Element.VISIBILITY or Element.DISPLAY
8036          * @return {Roo.Element} this
8037          */
8038         setVisibilityMode : function(visMode){
8039             this.visibilityMode = visMode;
8040             return this;
8041         },
8042         /**
8043          * Convenience method for setVisibilityMode(Element.DISPLAY)
8044          * @param {String} display (optional) What to set display to when visible
8045          * @return {Roo.Element} this
8046          */
8047         enableDisplayMode : function(display){
8048             this.setVisibilityMode(El.DISPLAY);
8049             if(typeof display != "undefined") { this.originalDisplay = display; }
8050             return this;
8051         },
8052
8053         /**
8054          * 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)
8055          * @param {String} selector The simple selector to test
8056          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8057                 search as a number or element (defaults to 10 || document.body)
8058          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8059          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8060          */
8061         findParent : function(simpleSelector, maxDepth, returnEl){
8062             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8063             maxDepth = maxDepth || 50;
8064             if(typeof maxDepth != "number"){
8065                 stopEl = Roo.getDom(maxDepth);
8066                 maxDepth = 10;
8067             }
8068             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8069                 if(dq.is(p, simpleSelector)){
8070                     return returnEl ? Roo.get(p) : p;
8071                 }
8072                 depth++;
8073                 p = p.parentNode;
8074             }
8075             return null;
8076         },
8077
8078
8079         /**
8080          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8081          * @param {String} selector The simple selector to test
8082          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8083                 search as a number or element (defaults to 10 || document.body)
8084          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8085          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8086          */
8087         findParentNode : function(simpleSelector, maxDepth, returnEl){
8088             var p = Roo.fly(this.dom.parentNode, '_internal');
8089             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8090         },
8091         
8092         /**
8093          * Looks at  the scrollable parent element
8094          */
8095         findScrollableParent : function()
8096         {
8097             var overflowRegex = /(auto|scroll)/;
8098             
8099             if(this.getStyle('position') === 'fixed'){
8100                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8101             }
8102             
8103             var excludeStaticParent = this.getStyle('position') === "absolute";
8104             
8105             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8106                 
8107                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8108                     continue;
8109                 }
8110                 
8111                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8112                     return parent;
8113                 }
8114                 
8115                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8116                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8117                 }
8118             }
8119             
8120             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8121         },
8122
8123         /**
8124          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8125          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8126          * @param {String} selector The simple selector to test
8127          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8128                 search as a number or element (defaults to 10 || document.body)
8129          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8130          */
8131         up : function(simpleSelector, maxDepth){
8132             return this.findParentNode(simpleSelector, maxDepth, true);
8133         },
8134
8135
8136
8137         /**
8138          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8139          * @param {String} selector The simple selector to test
8140          * @return {Boolean} True if this element matches the selector, else false
8141          */
8142         is : function(simpleSelector){
8143             return Roo.DomQuery.is(this.dom, simpleSelector);
8144         },
8145
8146         /**
8147          * Perform animation on this element.
8148          * @param {Object} args The YUI animation control args
8149          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8150          * @param {Function} onComplete (optional) Function to call when animation completes
8151          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8152          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8153          * @return {Roo.Element} this
8154          */
8155         animate : function(args, duration, onComplete, easing, animType){
8156             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8157             return this;
8158         },
8159
8160         /*
8161          * @private Internal animation call
8162          */
8163         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8164             animType = animType || 'run';
8165             opt = opt || {};
8166             var anim = Roo.lib.Anim[animType](
8167                 this.dom, args,
8168                 (opt.duration || defaultDur) || .35,
8169                 (opt.easing || defaultEase) || 'easeOut',
8170                 function(){
8171                     Roo.callback(cb, this);
8172                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8173                 },
8174                 this
8175             );
8176             opt.anim = anim;
8177             return anim;
8178         },
8179
8180         // private legacy anim prep
8181         preanim : function(a, i){
8182             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8183         },
8184
8185         /**
8186          * Removes worthless text nodes
8187          * @param {Boolean} forceReclean (optional) By default the element
8188          * keeps track if it has been cleaned already so
8189          * you can call this over and over. However, if you update the element and
8190          * need to force a reclean, you can pass true.
8191          */
8192         clean : function(forceReclean){
8193             if(this.isCleaned && forceReclean !== true){
8194                 return this;
8195             }
8196             var ns = /\S/;
8197             var d = this.dom, n = d.firstChild, ni = -1;
8198             while(n){
8199                 var nx = n.nextSibling;
8200                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8201                     d.removeChild(n);
8202                 }else{
8203                     n.nodeIndex = ++ni;
8204                 }
8205                 n = nx;
8206             }
8207             this.isCleaned = true;
8208             return this;
8209         },
8210
8211         // private
8212         calcOffsetsTo : function(el){
8213             el = Roo.get(el);
8214             var d = el.dom;
8215             var restorePos = false;
8216             if(el.getStyle('position') == 'static'){
8217                 el.position('relative');
8218                 restorePos = true;
8219             }
8220             var x = 0, y =0;
8221             var op = this.dom;
8222             while(op && op != d && op.tagName != 'HTML'){
8223                 x+= op.offsetLeft;
8224                 y+= op.offsetTop;
8225                 op = op.offsetParent;
8226             }
8227             if(restorePos){
8228                 el.position('static');
8229             }
8230             return [x, y];
8231         },
8232
8233         /**
8234          * Scrolls this element into view within the passed container.
8235          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8236          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8237          * @return {Roo.Element} this
8238          */
8239         scrollIntoView : function(container, hscroll){
8240             var c = Roo.getDom(container) || document.body;
8241             var el = this.dom;
8242
8243             var o = this.calcOffsetsTo(c),
8244                 l = o[0],
8245                 t = o[1],
8246                 b = t+el.offsetHeight,
8247                 r = l+el.offsetWidth;
8248
8249             var ch = c.clientHeight;
8250             var ct = parseInt(c.scrollTop, 10);
8251             var cl = parseInt(c.scrollLeft, 10);
8252             var cb = ct + ch;
8253             var cr = cl + c.clientWidth;
8254
8255             if(t < ct){
8256                 c.scrollTop = t;
8257             }else if(b > cb){
8258                 c.scrollTop = b-ch;
8259             }
8260
8261             if(hscroll !== false){
8262                 if(l < cl){
8263                     c.scrollLeft = l;
8264                 }else if(r > cr){
8265                     c.scrollLeft = r-c.clientWidth;
8266                 }
8267             }
8268             return this;
8269         },
8270
8271         // private
8272         scrollChildIntoView : function(child, hscroll){
8273             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8274         },
8275
8276         /**
8277          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8278          * the new height may not be available immediately.
8279          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8280          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8281          * @param {Function} onComplete (optional) Function to call when animation completes
8282          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8283          * @return {Roo.Element} this
8284          */
8285         autoHeight : function(animate, duration, onComplete, easing){
8286             var oldHeight = this.getHeight();
8287             this.clip();
8288             this.setHeight(1); // force clipping
8289             setTimeout(function(){
8290                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8291                 if(!animate){
8292                     this.setHeight(height);
8293                     this.unclip();
8294                     if(typeof onComplete == "function"){
8295                         onComplete();
8296                     }
8297                 }else{
8298                     this.setHeight(oldHeight); // restore original height
8299                     this.setHeight(height, animate, duration, function(){
8300                         this.unclip();
8301                         if(typeof onComplete == "function") { onComplete(); }
8302                     }.createDelegate(this), easing);
8303                 }
8304             }.createDelegate(this), 0);
8305             return this;
8306         },
8307
8308         /**
8309          * Returns true if this element is an ancestor of the passed element
8310          * @param {HTMLElement/String} el The element to check
8311          * @return {Boolean} True if this element is an ancestor of el, else false
8312          */
8313         contains : function(el){
8314             if(!el){return false;}
8315             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8316         },
8317
8318         /**
8319          * Checks whether the element is currently visible using both visibility and display properties.
8320          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8321          * @return {Boolean} True if the element is currently visible, else false
8322          */
8323         isVisible : function(deep) {
8324             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8325             if(deep !== true || !vis){
8326                 return vis;
8327             }
8328             var p = this.dom.parentNode;
8329             while(p && p.tagName.toLowerCase() != "body"){
8330                 if(!Roo.fly(p, '_isVisible').isVisible()){
8331                     return false;
8332                 }
8333                 p = p.parentNode;
8334             }
8335             return true;
8336         },
8337
8338         /**
8339          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8340          * @param {String} selector The CSS selector
8341          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8342          * @return {CompositeElement/CompositeElementLite} The composite element
8343          */
8344         select : function(selector, unique){
8345             return El.select(selector, unique, this.dom);
8346         },
8347
8348         /**
8349          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8350          * @param {String} selector The CSS selector
8351          * @return {Array} An array of the matched nodes
8352          */
8353         query : function(selector, unique){
8354             return Roo.DomQuery.select(selector, this.dom);
8355         },
8356
8357         /**
8358          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8359          * @param {String} selector The CSS selector
8360          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8361          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8362          */
8363         child : function(selector, returnDom){
8364             var n = Roo.DomQuery.selectNode(selector, this.dom);
8365             return returnDom ? n : Roo.get(n);
8366         },
8367
8368         /**
8369          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8370          * @param {String} selector The CSS selector
8371          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8372          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8373          */
8374         down : function(selector, returnDom){
8375             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8376             return returnDom ? n : Roo.get(n);
8377         },
8378
8379         /**
8380          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8381          * @param {String} group The group the DD object is member of
8382          * @param {Object} config The DD config object
8383          * @param {Object} overrides An object containing methods to override/implement on the DD object
8384          * @return {Roo.dd.DD} The DD object
8385          */
8386         initDD : function(group, config, overrides){
8387             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8388             return Roo.apply(dd, overrides);
8389         },
8390
8391         /**
8392          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8393          * @param {String} group The group the DDProxy object is member of
8394          * @param {Object} config The DDProxy config object
8395          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8396          * @return {Roo.dd.DDProxy} The DDProxy object
8397          */
8398         initDDProxy : function(group, config, overrides){
8399             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8400             return Roo.apply(dd, overrides);
8401         },
8402
8403         /**
8404          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8405          * @param {String} group The group the DDTarget object is member of
8406          * @param {Object} config The DDTarget config object
8407          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8408          * @return {Roo.dd.DDTarget} The DDTarget object
8409          */
8410         initDDTarget : function(group, config, overrides){
8411             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8412             return Roo.apply(dd, overrides);
8413         },
8414
8415         /**
8416          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8417          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8418          * @param {Boolean} visible Whether the element is visible
8419          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8420          * @return {Roo.Element} this
8421          */
8422          setVisible : function(visible, animate){
8423             if(!animate || !A){
8424                 if(this.visibilityMode == El.DISPLAY){
8425                     this.setDisplayed(visible);
8426                 }else{
8427                     this.fixDisplay();
8428                     this.dom.style.visibility = visible ? "visible" : "hidden";
8429                 }
8430             }else{
8431                 // closure for composites
8432                 var dom = this.dom;
8433                 var visMode = this.visibilityMode;
8434                 if(visible){
8435                     this.setOpacity(.01);
8436                     this.setVisible(true);
8437                 }
8438                 this.anim({opacity: { to: (visible?1:0) }},
8439                       this.preanim(arguments, 1),
8440                       null, .35, 'easeIn', function(){
8441                          if(!visible){
8442                              if(visMode == El.DISPLAY){
8443                                  dom.style.display = "none";
8444                              }else{
8445                                  dom.style.visibility = "hidden";
8446                              }
8447                              Roo.get(dom).setOpacity(1);
8448                          }
8449                      });
8450             }
8451             return this;
8452         },
8453
8454         /**
8455          * Returns true if display is not "none"
8456          * @return {Boolean}
8457          */
8458         isDisplayed : function() {
8459             return this.getStyle("display") != "none";
8460         },
8461
8462         /**
8463          * Toggles the element's visibility or display, depending on visibility mode.
8464          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8465          * @return {Roo.Element} this
8466          */
8467         toggle : function(animate){
8468             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8469             return this;
8470         },
8471
8472         /**
8473          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8474          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8475          * @return {Roo.Element} this
8476          */
8477         setDisplayed : function(value) {
8478             if(typeof value == "boolean"){
8479                value = value ? this.originalDisplay : "none";
8480             }
8481             this.setStyle("display", value);
8482             return this;
8483         },
8484
8485         /**
8486          * Tries to focus the element. Any exceptions are caught and ignored.
8487          * @return {Roo.Element} this
8488          */
8489         focus : function() {
8490             try{
8491                 this.dom.focus();
8492             }catch(e){}
8493             return this;
8494         },
8495
8496         /**
8497          * Tries to blur the element. Any exceptions are caught and ignored.
8498          * @return {Roo.Element} this
8499          */
8500         blur : function() {
8501             try{
8502                 this.dom.blur();
8503             }catch(e){}
8504             return this;
8505         },
8506
8507         /**
8508          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8509          * @param {String/Array} className The CSS class to add, or an array of classes
8510          * @return {Roo.Element} this
8511          */
8512         addClass : function(className){
8513             if(className instanceof Array){
8514                 for(var i = 0, len = className.length; i < len; i++) {
8515                     this.addClass(className[i]);
8516                 }
8517             }else{
8518                 if(className && !this.hasClass(className)){
8519                     if (this.dom instanceof SVGElement) {
8520                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8521                     } else {
8522                         this.dom.className = this.dom.className + " " + className;
8523                     }
8524                 }
8525             }
8526             return this;
8527         },
8528
8529         /**
8530          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8531          * @param {String/Array} className The CSS class to add, or an array of classes
8532          * @return {Roo.Element} this
8533          */
8534         radioClass : function(className){
8535             var siblings = this.dom.parentNode.childNodes;
8536             for(var i = 0; i < siblings.length; i++) {
8537                 var s = siblings[i];
8538                 if(s.nodeType == 1){
8539                     Roo.get(s).removeClass(className);
8540                 }
8541             }
8542             this.addClass(className);
8543             return this;
8544         },
8545
8546         /**
8547          * Removes one or more CSS classes from the element.
8548          * @param {String/Array} className The CSS class to remove, or an array of classes
8549          * @return {Roo.Element} this
8550          */
8551         removeClass : function(className){
8552             
8553             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8554             if(!className || !cn){
8555                 return this;
8556             }
8557             if(className instanceof Array){
8558                 for(var i = 0, len = className.length; i < len; i++) {
8559                     this.removeClass(className[i]);
8560                 }
8561             }else{
8562                 if(this.hasClass(className)){
8563                     var re = this.classReCache[className];
8564                     if (!re) {
8565                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8566                        this.classReCache[className] = re;
8567                     }
8568                     if (this.dom instanceof SVGElement) {
8569                         this.dom.className.baseVal = cn.replace(re, " ");
8570                     } else {
8571                         this.dom.className = cn.replace(re, " ");
8572                     }
8573                 }
8574             }
8575             return this;
8576         },
8577
8578         // private
8579         classReCache: {},
8580
8581         /**
8582          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8583          * @param {String} className The CSS class to toggle
8584          * @return {Roo.Element} this
8585          */
8586         toggleClass : function(className){
8587             if(this.hasClass(className)){
8588                 this.removeClass(className);
8589             }else{
8590                 this.addClass(className);
8591             }
8592             return this;
8593         },
8594
8595         /**
8596          * Checks if the specified CSS class exists on this element's DOM node.
8597          * @param {String} className The CSS class to check for
8598          * @return {Boolean} True if the class exists, else false
8599          */
8600         hasClass : function(className){
8601             if (this.dom instanceof SVGElement) {
8602                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8603             } 
8604             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8605         },
8606
8607         /**
8608          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8609          * @param {String} oldClassName The CSS class to replace
8610          * @param {String} newClassName The replacement CSS class
8611          * @return {Roo.Element} this
8612          */
8613         replaceClass : function(oldClassName, newClassName){
8614             this.removeClass(oldClassName);
8615             this.addClass(newClassName);
8616             return this;
8617         },
8618
8619         /**
8620          * Returns an object with properties matching the styles requested.
8621          * For example, el.getStyles('color', 'font-size', 'width') might return
8622          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8623          * @param {String} style1 A style name
8624          * @param {String} style2 A style name
8625          * @param {String} etc.
8626          * @return {Object} The style object
8627          */
8628         getStyles : function(){
8629             var a = arguments, len = a.length, r = {};
8630             for(var i = 0; i < len; i++){
8631                 r[a[i]] = this.getStyle(a[i]);
8632             }
8633             return r;
8634         },
8635
8636         /**
8637          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8638          * @param {String} property The style property whose value is returned.
8639          * @return {String} The current value of the style property for this element.
8640          */
8641         getStyle : function(){
8642             return view && view.getComputedStyle ?
8643                 function(prop){
8644                     var el = this.dom, v, cs, camel;
8645                     if(prop == 'float'){
8646                         prop = "cssFloat";
8647                     }
8648                     if(el.style && (v = el.style[prop])){
8649                         return v;
8650                     }
8651                     if(cs = view.getComputedStyle(el, "")){
8652                         if(!(camel = propCache[prop])){
8653                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8654                         }
8655                         return cs[camel];
8656                     }
8657                     return null;
8658                 } :
8659                 function(prop){
8660                     var el = this.dom, v, cs, camel;
8661                     if(prop == 'opacity'){
8662                         if(typeof el.style.filter == 'string'){
8663                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8664                             if(m){
8665                                 var fv = parseFloat(m[1]);
8666                                 if(!isNaN(fv)){
8667                                     return fv ? fv / 100 : 0;
8668                                 }
8669                             }
8670                         }
8671                         return 1;
8672                     }else if(prop == 'float'){
8673                         prop = "styleFloat";
8674                     }
8675                     if(!(camel = propCache[prop])){
8676                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8677                     }
8678                     if(v = el.style[camel]){
8679                         return v;
8680                     }
8681                     if(cs = el.currentStyle){
8682                         return cs[camel];
8683                     }
8684                     return null;
8685                 };
8686         }(),
8687
8688         /**
8689          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8690          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8691          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8692          * @return {Roo.Element} this
8693          */
8694         setStyle : function(prop, value){
8695             if(typeof prop == "string"){
8696                 
8697                 if (prop == 'float') {
8698                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8699                     return this;
8700                 }
8701                 
8702                 var camel;
8703                 if(!(camel = propCache[prop])){
8704                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8705                 }
8706                 
8707                 if(camel == 'opacity') {
8708                     this.setOpacity(value);
8709                 }else{
8710                     this.dom.style[camel] = value;
8711                 }
8712             }else{
8713                 for(var style in prop){
8714                     if(typeof prop[style] != "function"){
8715                        this.setStyle(style, prop[style]);
8716                     }
8717                 }
8718             }
8719             return this;
8720         },
8721
8722         /**
8723          * More flexible version of {@link #setStyle} for setting style properties.
8724          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8725          * a function which returns such a specification.
8726          * @return {Roo.Element} this
8727          */
8728         applyStyles : function(style){
8729             Roo.DomHelper.applyStyles(this.dom, style);
8730             return this;
8731         },
8732
8733         /**
8734           * 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).
8735           * @return {Number} The X position of the element
8736           */
8737         getX : function(){
8738             return D.getX(this.dom);
8739         },
8740
8741         /**
8742           * 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).
8743           * @return {Number} The Y position of the element
8744           */
8745         getY : function(){
8746             return D.getY(this.dom);
8747         },
8748
8749         /**
8750           * 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).
8751           * @return {Array} The XY position of the element
8752           */
8753         getXY : function(){
8754             return D.getXY(this.dom);
8755         },
8756
8757         /**
8758          * 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).
8759          * @param {Number} The X position of the element
8760          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8761          * @return {Roo.Element} this
8762          */
8763         setX : function(x, animate){
8764             if(!animate || !A){
8765                 D.setX(this.dom, x);
8766             }else{
8767                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8768             }
8769             return this;
8770         },
8771
8772         /**
8773          * 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).
8774          * @param {Number} The Y position of the element
8775          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8776          * @return {Roo.Element} this
8777          */
8778         setY : function(y, animate){
8779             if(!animate || !A){
8780                 D.setY(this.dom, y);
8781             }else{
8782                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8783             }
8784             return this;
8785         },
8786
8787         /**
8788          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8789          * @param {String} left The left CSS property value
8790          * @return {Roo.Element} this
8791          */
8792         setLeft : function(left){
8793             this.setStyle("left", this.addUnits(left));
8794             return this;
8795         },
8796
8797         /**
8798          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8799          * @param {String} top The top CSS property value
8800          * @return {Roo.Element} this
8801          */
8802         setTop : function(top){
8803             this.setStyle("top", this.addUnits(top));
8804             return this;
8805         },
8806
8807         /**
8808          * Sets the element's CSS right style.
8809          * @param {String} right The right CSS property value
8810          * @return {Roo.Element} this
8811          */
8812         setRight : function(right){
8813             this.setStyle("right", this.addUnits(right));
8814             return this;
8815         },
8816
8817         /**
8818          * Sets the element's CSS bottom style.
8819          * @param {String} bottom The bottom CSS property value
8820          * @return {Roo.Element} this
8821          */
8822         setBottom : function(bottom){
8823             this.setStyle("bottom", this.addUnits(bottom));
8824             return this;
8825         },
8826
8827         /**
8828          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8829          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8830          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8831          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8832          * @return {Roo.Element} this
8833          */
8834         setXY : function(pos, animate){
8835             if(!animate || !A){
8836                 D.setXY(this.dom, pos);
8837             }else{
8838                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8839             }
8840             return this;
8841         },
8842
8843         /**
8844          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8845          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8846          * @param {Number} x X value for new position (coordinates are page-based)
8847          * @param {Number} y Y value for new position (coordinates are page-based)
8848          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8849          * @return {Roo.Element} this
8850          */
8851         setLocation : function(x, y, animate){
8852             this.setXY([x, y], this.preanim(arguments, 2));
8853             return this;
8854         },
8855
8856         /**
8857          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8858          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8859          * @param {Number} x X value for new position (coordinates are page-based)
8860          * @param {Number} y Y value for new position (coordinates are page-based)
8861          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8862          * @return {Roo.Element} this
8863          */
8864         moveTo : function(x, y, animate){
8865             this.setXY([x, y], this.preanim(arguments, 2));
8866             return this;
8867         },
8868
8869         /**
8870          * Returns the region of the given element.
8871          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8872          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8873          */
8874         getRegion : function(){
8875             return D.getRegion(this.dom);
8876         },
8877
8878         /**
8879          * Returns the offset height of the element
8880          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8881          * @return {Number} The element's height
8882          */
8883         getHeight : function(contentHeight){
8884             var h = this.dom.offsetHeight || 0;
8885             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8886         },
8887
8888         /**
8889          * Returns the offset width of the element
8890          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8891          * @return {Number} The element's width
8892          */
8893         getWidth : function(contentWidth){
8894             var w = this.dom.offsetWidth || 0;
8895             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8896         },
8897
8898         /**
8899          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8900          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8901          * if a height has not been set using CSS.
8902          * @return {Number}
8903          */
8904         getComputedHeight : function(){
8905             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8906             if(!h){
8907                 h = parseInt(this.getStyle('height'), 10) || 0;
8908                 if(!this.isBorderBox()){
8909                     h += this.getFrameWidth('tb');
8910                 }
8911             }
8912             return h;
8913         },
8914
8915         /**
8916          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8917          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8918          * if a width has not been set using CSS.
8919          * @return {Number}
8920          */
8921         getComputedWidth : function(){
8922             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8923             if(!w){
8924                 w = parseInt(this.getStyle('width'), 10) || 0;
8925                 if(!this.isBorderBox()){
8926                     w += this.getFrameWidth('lr');
8927                 }
8928             }
8929             return w;
8930         },
8931
8932         /**
8933          * Returns the size of the element.
8934          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8935          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8936          */
8937         getSize : function(contentSize){
8938             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8939         },
8940
8941         /**
8942          * Returns the width and height of the viewport.
8943          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8944          */
8945         getViewSize : function(){
8946             var d = this.dom, doc = document, aw = 0, ah = 0;
8947             if(d == doc || d == doc.body){
8948                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8949             }else{
8950                 return {
8951                     width : d.clientWidth,
8952                     height: d.clientHeight
8953                 };
8954             }
8955         },
8956
8957         /**
8958          * Returns the value of the "value" attribute
8959          * @param {Boolean} asNumber true to parse the value as a number
8960          * @return {String/Number}
8961          */
8962         getValue : function(asNumber){
8963             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8964         },
8965
8966         // private
8967         adjustWidth : function(width){
8968             if(typeof width == "number"){
8969                 if(this.autoBoxAdjust && !this.isBorderBox()){
8970                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8971                 }
8972                 if(width < 0){
8973                     width = 0;
8974                 }
8975             }
8976             return width;
8977         },
8978
8979         // private
8980         adjustHeight : function(height){
8981             if(typeof height == "number"){
8982                if(this.autoBoxAdjust && !this.isBorderBox()){
8983                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8984                }
8985                if(height < 0){
8986                    height = 0;
8987                }
8988             }
8989             return height;
8990         },
8991
8992         /**
8993          * Set the width of the element
8994          * @param {Number} width The new width
8995          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8996          * @return {Roo.Element} this
8997          */
8998         setWidth : function(width, animate){
8999             width = this.adjustWidth(width);
9000             if(!animate || !A){
9001                 this.dom.style.width = this.addUnits(width);
9002             }else{
9003                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9004             }
9005             return this;
9006         },
9007
9008         /**
9009          * Set the height of the element
9010          * @param {Number} height The new height
9011          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9012          * @return {Roo.Element} this
9013          */
9014          setHeight : function(height, animate){
9015             height = this.adjustHeight(height);
9016             if(!animate || !A){
9017                 this.dom.style.height = this.addUnits(height);
9018             }else{
9019                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9020             }
9021             return this;
9022         },
9023
9024         /**
9025          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9026          * @param {Number} width The new width
9027          * @param {Number} height The new height
9028          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9029          * @return {Roo.Element} this
9030          */
9031          setSize : function(width, height, animate){
9032             if(typeof width == "object"){ // in case of object from getSize()
9033                 height = width.height; width = width.width;
9034             }
9035             width = this.adjustWidth(width); height = this.adjustHeight(height);
9036             if(!animate || !A){
9037                 this.dom.style.width = this.addUnits(width);
9038                 this.dom.style.height = this.addUnits(height);
9039             }else{
9040                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9041             }
9042             return this;
9043         },
9044
9045         /**
9046          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9047          * @param {Number} x X value for new position (coordinates are page-based)
9048          * @param {Number} y Y value for new position (coordinates are page-based)
9049          * @param {Number} width The new width
9050          * @param {Number} height The new height
9051          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9052          * @return {Roo.Element} this
9053          */
9054         setBounds : function(x, y, width, height, animate){
9055             if(!animate || !A){
9056                 this.setSize(width, height);
9057                 this.setLocation(x, y);
9058             }else{
9059                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9060                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9061                               this.preanim(arguments, 4), 'motion');
9062             }
9063             return this;
9064         },
9065
9066         /**
9067          * 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.
9068          * @param {Roo.lib.Region} region The region to fill
9069          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9070          * @return {Roo.Element} this
9071          */
9072         setRegion : function(region, animate){
9073             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9074             return this;
9075         },
9076
9077         /**
9078          * Appends an event handler
9079          *
9080          * @param {String}   eventName     The type of event to append
9081          * @param {Function} fn        The method the event invokes
9082          * @param {Object} scope       (optional) The scope (this object) of the fn
9083          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9084          */
9085         addListener : function(eventName, fn, scope, options)
9086         {
9087             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9088                 this.addListener('touchstart', this.onTapHandler, this);
9089             }
9090             
9091             // we need to handle a special case where dom element is a svg element.
9092             // in this case we do not actua
9093             if (!this.dom) {
9094                 return;
9095             }
9096             
9097             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9098                 if (typeof(this.listeners[eventName]) == 'undefined') {
9099                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9100                 }
9101                 this.listeners[eventName].addListener(fn, scope, options);
9102                 return;
9103             }
9104             
9105                 
9106             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9107             
9108             
9109         },
9110         tapedTwice : false,
9111         onTapHandler : function(event)
9112         {
9113             if(!this.tapedTwice) {
9114                 this.tapedTwice = true;
9115                 var s = this;
9116                 setTimeout( function() {
9117                     s.tapedTwice = false;
9118                 }, 300 );
9119                 return;
9120             }
9121             event.preventDefault();
9122             var revent = new MouseEvent('dblclick',  {
9123                 view: window,
9124                 bubbles: true,
9125                 cancelable: true
9126             });
9127              
9128             this.dom.dispatchEvent(revent);
9129             //action on double tap goes below
9130              
9131         }, 
9132  
9133         /**
9134          * Removes an event handler from this element
9135          * @param {String} eventName the type of event to remove
9136          * @param {Function} fn the method the event invokes
9137          * @param {Function} scope (needed for svg fake listeners)
9138          * @return {Roo.Element} this
9139          */
9140         removeListener : function(eventName, fn, scope){
9141             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9142             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9143                 return this;
9144             }
9145             this.listeners[eventName].removeListener(fn, scope);
9146             return this;
9147         },
9148
9149         /**
9150          * Removes all previous added listeners from this element
9151          * @return {Roo.Element} this
9152          */
9153         removeAllListeners : function(){
9154             E.purgeElement(this.dom);
9155             this.listeners = {};
9156             return this;
9157         },
9158
9159         relayEvent : function(eventName, observable){
9160             this.on(eventName, function(e){
9161                 observable.fireEvent(eventName, e);
9162             });
9163         },
9164
9165         
9166         /**
9167          * Set the opacity of the element
9168          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9169          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9170          * @return {Roo.Element} this
9171          */
9172          setOpacity : function(opacity, animate){
9173             if(!animate || !A){
9174                 var s = this.dom.style;
9175                 if(Roo.isIE){
9176                     s.zoom = 1;
9177                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9178                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9179                 }else{
9180                     s.opacity = opacity;
9181                 }
9182             }else{
9183                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9184             }
9185             return this;
9186         },
9187
9188         /**
9189          * Gets the left X coordinate
9190          * @param {Boolean} local True to get the local css position instead of page coordinate
9191          * @return {Number}
9192          */
9193         getLeft : function(local){
9194             if(!local){
9195                 return this.getX();
9196             }else{
9197                 return parseInt(this.getStyle("left"), 10) || 0;
9198             }
9199         },
9200
9201         /**
9202          * Gets the right X coordinate of the element (element X position + element width)
9203          * @param {Boolean} local True to get the local css position instead of page coordinate
9204          * @return {Number}
9205          */
9206         getRight : function(local){
9207             if(!local){
9208                 return this.getX() + this.getWidth();
9209             }else{
9210                 return (this.getLeft(true) + this.getWidth()) || 0;
9211             }
9212         },
9213
9214         /**
9215          * Gets the top Y coordinate
9216          * @param {Boolean} local True to get the local css position instead of page coordinate
9217          * @return {Number}
9218          */
9219         getTop : function(local) {
9220             if(!local){
9221                 return this.getY();
9222             }else{
9223                 return parseInt(this.getStyle("top"), 10) || 0;
9224             }
9225         },
9226
9227         /**
9228          * Gets the bottom Y coordinate of the element (element Y position + element height)
9229          * @param {Boolean} local True to get the local css position instead of page coordinate
9230          * @return {Number}
9231          */
9232         getBottom : function(local){
9233             if(!local){
9234                 return this.getY() + this.getHeight();
9235             }else{
9236                 return (this.getTop(true) + this.getHeight()) || 0;
9237             }
9238         },
9239
9240         /**
9241         * Initializes positioning on this element. If a desired position is not passed, it will make the
9242         * the element positioned relative IF it is not already positioned.
9243         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9244         * @param {Number} zIndex (optional) The zIndex to apply
9245         * @param {Number} x (optional) Set the page X position
9246         * @param {Number} y (optional) Set the page Y position
9247         */
9248         position : function(pos, zIndex, x, y){
9249             if(!pos){
9250                if(this.getStyle('position') == 'static'){
9251                    this.setStyle('position', 'relative');
9252                }
9253             }else{
9254                 this.setStyle("position", pos);
9255             }
9256             if(zIndex){
9257                 this.setStyle("z-index", zIndex);
9258             }
9259             if(x !== undefined && y !== undefined){
9260                 this.setXY([x, y]);
9261             }else if(x !== undefined){
9262                 this.setX(x);
9263             }else if(y !== undefined){
9264                 this.setY(y);
9265             }
9266         },
9267
9268         /**
9269         * Clear positioning back to the default when the document was loaded
9270         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9271         * @return {Roo.Element} this
9272          */
9273         clearPositioning : function(value){
9274             value = value ||'';
9275             this.setStyle({
9276                 "left": value,
9277                 "right": value,
9278                 "top": value,
9279                 "bottom": value,
9280                 "z-index": "",
9281                 "position" : "static"
9282             });
9283             return this;
9284         },
9285
9286         /**
9287         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9288         * snapshot before performing an update and then restoring the element.
9289         * @return {Object}
9290         */
9291         getPositioning : function(){
9292             var l = this.getStyle("left");
9293             var t = this.getStyle("top");
9294             return {
9295                 "position" : this.getStyle("position"),
9296                 "left" : l,
9297                 "right" : l ? "" : this.getStyle("right"),
9298                 "top" : t,
9299                 "bottom" : t ? "" : this.getStyle("bottom"),
9300                 "z-index" : this.getStyle("z-index")
9301             };
9302         },
9303
9304         /**
9305          * Gets the width of the border(s) for the specified side(s)
9306          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9307          * passing lr would get the border (l)eft width + the border (r)ight width.
9308          * @return {Number} The width of the sides passed added together
9309          */
9310         getBorderWidth : function(side){
9311             return this.addStyles(side, El.borders);
9312         },
9313
9314         /**
9315          * Gets the width of the padding(s) for the specified side(s)
9316          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9317          * passing lr would get the padding (l)eft + the padding (r)ight.
9318          * @return {Number} The padding of the sides passed added together
9319          */
9320         getPadding : function(side){
9321             return this.addStyles(side, El.paddings);
9322         },
9323
9324         /**
9325         * Set positioning with an object returned by getPositioning().
9326         * @param {Object} posCfg
9327         * @return {Roo.Element} this
9328          */
9329         setPositioning : function(pc){
9330             this.applyStyles(pc);
9331             if(pc.right == "auto"){
9332                 this.dom.style.right = "";
9333             }
9334             if(pc.bottom == "auto"){
9335                 this.dom.style.bottom = "";
9336             }
9337             return this;
9338         },
9339
9340         // private
9341         fixDisplay : function(){
9342             if(this.getStyle("display") == "none"){
9343                 this.setStyle("visibility", "hidden");
9344                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9345                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9346                     this.setStyle("display", "block");
9347                 }
9348             }
9349         },
9350
9351         /**
9352          * Quick set left and top adding default units
9353          * @param {String} left The left CSS property value
9354          * @param {String} top The top CSS property value
9355          * @return {Roo.Element} this
9356          */
9357          setLeftTop : function(left, top){
9358             this.dom.style.left = this.addUnits(left);
9359             this.dom.style.top = this.addUnits(top);
9360             return this;
9361         },
9362
9363         /**
9364          * Move this element relative to its current position.
9365          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9366          * @param {Number} distance How far to move the element in pixels
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          move : function(direction, distance, animate){
9371             var xy = this.getXY();
9372             direction = direction.toLowerCase();
9373             switch(direction){
9374                 case "l":
9375                 case "left":
9376                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9377                     break;
9378                case "r":
9379                case "right":
9380                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9381                     break;
9382                case "t":
9383                case "top":
9384                case "up":
9385                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9386                     break;
9387                case "b":
9388                case "bottom":
9389                case "down":
9390                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9391                     break;
9392             }
9393             return this;
9394         },
9395
9396         /**
9397          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9398          * @return {Roo.Element} this
9399          */
9400         clip : function(){
9401             if(!this.isClipped){
9402                this.isClipped = true;
9403                this.originalClip = {
9404                    "o": this.getStyle("overflow"),
9405                    "x": this.getStyle("overflow-x"),
9406                    "y": this.getStyle("overflow-y")
9407                };
9408                this.setStyle("overflow", "hidden");
9409                this.setStyle("overflow-x", "hidden");
9410                this.setStyle("overflow-y", "hidden");
9411             }
9412             return this;
9413         },
9414
9415         /**
9416          *  Return clipping (overflow) to original clipping before clip() was called
9417          * @return {Roo.Element} this
9418          */
9419         unclip : function(){
9420             if(this.isClipped){
9421                 this.isClipped = false;
9422                 var o = this.originalClip;
9423                 if(o.o){this.setStyle("overflow", o.o);}
9424                 if(o.x){this.setStyle("overflow-x", o.x);}
9425                 if(o.y){this.setStyle("overflow-y", o.y);}
9426             }
9427             return this;
9428         },
9429
9430
9431         /**
9432          * Gets the x,y coordinates specified by the anchor position on the element.
9433          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9434          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9435          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9436          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9437          * @return {Array} [x, y] An array containing the element's x and y coordinates
9438          */
9439         getAnchorXY : function(anchor, local, s){
9440             //Passing a different size is useful for pre-calculating anchors,
9441             //especially for anchored animations that change the el size.
9442
9443             var w, h, vp = false;
9444             if(!s){
9445                 var d = this.dom;
9446                 if(d == document.body || d == document){
9447                     vp = true;
9448                     w = D.getViewWidth(); h = D.getViewHeight();
9449                 }else{
9450                     w = this.getWidth(); h = this.getHeight();
9451                 }
9452             }else{
9453                 w = s.width;  h = s.height;
9454             }
9455             var x = 0, y = 0, r = Math.round;
9456             switch((anchor || "tl").toLowerCase()){
9457                 case "c":
9458                     x = r(w*.5);
9459                     y = r(h*.5);
9460                 break;
9461                 case "t":
9462                     x = r(w*.5);
9463                     y = 0;
9464                 break;
9465                 case "l":
9466                     x = 0;
9467                     y = r(h*.5);
9468                 break;
9469                 case "r":
9470                     x = w;
9471                     y = r(h*.5);
9472                 break;
9473                 case "b":
9474                     x = r(w*.5);
9475                     y = h;
9476                 break;
9477                 case "tl":
9478                     x = 0;
9479                     y = 0;
9480                 break;
9481                 case "bl":
9482                     x = 0;
9483                     y = h;
9484                 break;
9485                 case "br":
9486                     x = w;
9487                     y = h;
9488                 break;
9489                 case "tr":
9490                     x = w;
9491                     y = 0;
9492                 break;
9493             }
9494             if(local === true){
9495                 return [x, y];
9496             }
9497             if(vp){
9498                 var sc = this.getScroll();
9499                 return [x + sc.left, y + sc.top];
9500             }
9501             //Add the element's offset xy
9502             var o = this.getXY();
9503             return [x+o[0], y+o[1]];
9504         },
9505
9506         /**
9507          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9508          * supported position values.
9509          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9510          * @param {String} position The position to align to.
9511          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9512          * @return {Array} [x, y]
9513          */
9514         getAlignToXY : function(el, p, o)
9515         {
9516             el = Roo.get(el);
9517             var d = this.dom;
9518             if(!el.dom){
9519                 throw "Element.alignTo with an element that doesn't exist";
9520             }
9521             var c = false; //constrain to viewport
9522             var p1 = "", p2 = "";
9523             o = o || [0,0];
9524
9525             if(!p){
9526                 p = "tl-bl";
9527             }else if(p == "?"){
9528                 p = "tl-bl?";
9529             }else if(p.indexOf("-") == -1){
9530                 p = "tl-" + p;
9531             }
9532             p = p.toLowerCase();
9533             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9534             if(!m){
9535                throw "Element.alignTo with an invalid alignment " + p;
9536             }
9537             p1 = m[1]; p2 = m[2]; c = !!m[3];
9538
9539             //Subtract the aligned el's internal xy from the target's offset xy
9540             //plus custom offset to get the aligned el's new offset xy
9541             var a1 = this.getAnchorXY(p1, true);
9542             var a2 = el.getAnchorXY(p2, false);
9543             var x = a2[0] - a1[0] + o[0];
9544             var y = a2[1] - a1[1] + o[1];
9545             if(c){
9546                 //constrain the aligned el to viewport if necessary
9547                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9548                 // 5px of margin for ie
9549                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9550
9551                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9552                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9553                 //otherwise swap the aligned el to the opposite border of the target.
9554                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9555                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9556                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9557                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9558
9559                var doc = document;
9560                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9561                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9562
9563                if((x+w) > dw + scrollX){
9564                     x = swapX ? r.left-w : dw+scrollX-w;
9565                 }
9566                if(x < scrollX){
9567                    x = swapX ? r.right : scrollX;
9568                }
9569                if((y+h) > dh + scrollY){
9570                     y = swapY ? r.top-h : dh+scrollY-h;
9571                 }
9572                if (y < scrollY){
9573                    y = swapY ? r.bottom : scrollY;
9574                }
9575             }
9576             return [x,y];
9577         },
9578
9579         // private
9580         getConstrainToXY : function(){
9581             var os = {top:0, left:0, bottom:0, right: 0};
9582
9583             return function(el, local, offsets, proposedXY){
9584                 el = Roo.get(el);
9585                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9586
9587                 var vw, vh, vx = 0, vy = 0;
9588                 if(el.dom == document.body || el.dom == document){
9589                     vw = Roo.lib.Dom.getViewWidth();
9590                     vh = Roo.lib.Dom.getViewHeight();
9591                 }else{
9592                     vw = el.dom.clientWidth;
9593                     vh = el.dom.clientHeight;
9594                     if(!local){
9595                         var vxy = el.getXY();
9596                         vx = vxy[0];
9597                         vy = vxy[1];
9598                     }
9599                 }
9600
9601                 var s = el.getScroll();
9602
9603                 vx += offsets.left + s.left;
9604                 vy += offsets.top + s.top;
9605
9606                 vw -= offsets.right;
9607                 vh -= offsets.bottom;
9608
9609                 var vr = vx+vw;
9610                 var vb = vy+vh;
9611
9612                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9613                 var x = xy[0], y = xy[1];
9614                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9615
9616                 // only move it if it needs it
9617                 var moved = false;
9618
9619                 // first validate right/bottom
9620                 if((x + w) > vr){
9621                     x = vr - w;
9622                     moved = true;
9623                 }
9624                 if((y + h) > vb){
9625                     y = vb - h;
9626                     moved = true;
9627                 }
9628                 // then make sure top/left isn't negative
9629                 if(x < vx){
9630                     x = vx;
9631                     moved = true;
9632                 }
9633                 if(y < vy){
9634                     y = vy;
9635                     moved = true;
9636                 }
9637                 return moved ? [x, y] : false;
9638             };
9639         }(),
9640
9641         // private
9642         adjustForConstraints : function(xy, parent, offsets){
9643             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9644         },
9645
9646         /**
9647          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9648          * document it aligns it to the viewport.
9649          * The position parameter is optional, and can be specified in any one of the following formats:
9650          * <ul>
9651          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9652          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9653          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9654          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9655          *   <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
9656          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9657          * </ul>
9658          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9659          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9660          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9661          * that specified in order to enforce the viewport constraints.
9662          * Following are all of the supported anchor positions:
9663     <pre>
9664     Value  Description
9665     -----  -----------------------------
9666     tl     The top left corner (default)
9667     t      The center of the top edge
9668     tr     The top right corner
9669     l      The center of the left edge
9670     c      In the center of the element
9671     r      The center of the right edge
9672     bl     The bottom left corner
9673     b      The center of the bottom edge
9674     br     The bottom right corner
9675     </pre>
9676     Example Usage:
9677     <pre><code>
9678     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9679     el.alignTo("other-el");
9680
9681     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9682     el.alignTo("other-el", "tr?");
9683
9684     // align the bottom right corner of el with the center left edge of other-el
9685     el.alignTo("other-el", "br-l?");
9686
9687     // align the center of el with the bottom left corner of other-el and
9688     // adjust the x position by -6 pixels (and the y position by 0)
9689     el.alignTo("other-el", "c-bl", [-6, 0]);
9690     </code></pre>
9691          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9692          * @param {String} position The position to align to.
9693          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9694          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9695          * @return {Roo.Element} this
9696          */
9697         alignTo : function(element, position, offsets, animate){
9698             var xy = this.getAlignToXY(element, position, offsets);
9699             this.setXY(xy, this.preanim(arguments, 3));
9700             return this;
9701         },
9702
9703         /**
9704          * Anchors an element to another element and realigns it when the window is resized.
9705          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9706          * @param {String} position The position to align to.
9707          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9708          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9709          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9710          * is a number, it is used as the buffer delay (defaults to 50ms).
9711          * @param {Function} callback The function to call after the animation finishes
9712          * @return {Roo.Element} this
9713          */
9714         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9715             var action = function(){
9716                 this.alignTo(el, alignment, offsets, animate);
9717                 Roo.callback(callback, this);
9718             };
9719             Roo.EventManager.onWindowResize(action, this);
9720             var tm = typeof monitorScroll;
9721             if(tm != 'undefined'){
9722                 Roo.EventManager.on(window, 'scroll', action, this,
9723                     {buffer: tm == 'number' ? monitorScroll : 50});
9724             }
9725             action.call(this); // align immediately
9726             return this;
9727         },
9728         /**
9729          * Clears any opacity settings from this element. Required in some cases for IE.
9730          * @return {Roo.Element} this
9731          */
9732         clearOpacity : function(){
9733             if (window.ActiveXObject) {
9734                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9735                     this.dom.style.filter = "";
9736                 }
9737             } else {
9738                 this.dom.style.opacity = "";
9739                 this.dom.style["-moz-opacity"] = "";
9740                 this.dom.style["-khtml-opacity"] = "";
9741             }
9742             return this;
9743         },
9744
9745         /**
9746          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9747          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9748          * @return {Roo.Element} this
9749          */
9750         hide : function(animate){
9751             this.setVisible(false, this.preanim(arguments, 0));
9752             return this;
9753         },
9754
9755         /**
9756         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9757         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9758          * @return {Roo.Element} this
9759          */
9760         show : function(animate){
9761             this.setVisible(true, this.preanim(arguments, 0));
9762             return this;
9763         },
9764
9765         /**
9766          * @private Test if size has a unit, otherwise appends the default
9767          */
9768         addUnits : function(size){
9769             return Roo.Element.addUnits(size, this.defaultUnit);
9770         },
9771
9772         /**
9773          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9774          * @return {Roo.Element} this
9775          */
9776         beginMeasure : function(){
9777             var el = this.dom;
9778             if(el.offsetWidth || el.offsetHeight){
9779                 return this; // offsets work already
9780             }
9781             var changed = [];
9782             var p = this.dom, b = document.body; // start with this element
9783             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9784                 var pe = Roo.get(p);
9785                 if(pe.getStyle('display') == 'none'){
9786                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9787                     p.style.visibility = "hidden";
9788                     p.style.display = "block";
9789                 }
9790                 p = p.parentNode;
9791             }
9792             this._measureChanged = changed;
9793             return this;
9794
9795         },
9796
9797         /**
9798          * Restores displays to before beginMeasure was called
9799          * @return {Roo.Element} this
9800          */
9801         endMeasure : function(){
9802             var changed = this._measureChanged;
9803             if(changed){
9804                 for(var i = 0, len = changed.length; i < len; i++) {
9805                     var r = changed[i];
9806                     r.el.style.visibility = r.visibility;
9807                     r.el.style.display = "none";
9808                 }
9809                 this._measureChanged = null;
9810             }
9811             return this;
9812         },
9813
9814         /**
9815         * Update the innerHTML of this element, optionally searching for and processing scripts
9816         * @param {String} html The new HTML
9817         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9818         * @param {Function} callback For async script loading you can be noticed when the update completes
9819         * @return {Roo.Element} this
9820          */
9821         update : function(html, loadScripts, callback){
9822             if(typeof html == "undefined"){
9823                 html = "";
9824             }
9825             if(loadScripts !== true){
9826                 this.dom.innerHTML = html;
9827                 if(typeof callback == "function"){
9828                     callback();
9829                 }
9830                 return this;
9831             }
9832             var id = Roo.id();
9833             var dom = this.dom;
9834
9835             html += '<span id="' + id + '"></span>';
9836
9837             E.onAvailable(id, function(){
9838                 var hd = document.getElementsByTagName("head")[0];
9839                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9840                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9841                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9842
9843                 var match;
9844                 while(match = re.exec(html)){
9845                     var attrs = match[1];
9846                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9847                     if(srcMatch && srcMatch[2]){
9848                        var s = document.createElement("script");
9849                        s.src = srcMatch[2];
9850                        var typeMatch = attrs.match(typeRe);
9851                        if(typeMatch && typeMatch[2]){
9852                            s.type = typeMatch[2];
9853                        }
9854                        hd.appendChild(s);
9855                     }else if(match[2] && match[2].length > 0){
9856                         if(window.execScript) {
9857                            window.execScript(match[2]);
9858                         } else {
9859                             /**
9860                              * eval:var:id
9861                              * eval:var:dom
9862                              * eval:var:html
9863                              * 
9864                              */
9865                            window.eval(match[2]);
9866                         }
9867                     }
9868                 }
9869                 var el = document.getElementById(id);
9870                 if(el){el.parentNode.removeChild(el);}
9871                 if(typeof callback == "function"){
9872                     callback();
9873                 }
9874             });
9875             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9876             return this;
9877         },
9878
9879         /**
9880          * Direct access to the UpdateManager update() method (takes the same parameters).
9881          * @param {String/Function} url The url for this request or a function to call to get the url
9882          * @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}
9883          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9884          * @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.
9885          * @return {Roo.Element} this
9886          */
9887         load : function(){
9888             var um = this.getUpdateManager();
9889             um.update.apply(um, arguments);
9890             return this;
9891         },
9892
9893         /**
9894         * Gets this element's UpdateManager
9895         * @return {Roo.UpdateManager} The UpdateManager
9896         */
9897         getUpdateManager : function(){
9898             if(!this.updateManager){
9899                 this.updateManager = new Roo.UpdateManager(this);
9900             }
9901             return this.updateManager;
9902         },
9903
9904         /**
9905          * Disables text selection for this element (normalized across browsers)
9906          * @return {Roo.Element} this
9907          */
9908         unselectable : function(){
9909             this.dom.unselectable = "on";
9910             this.swallowEvent("selectstart", true);
9911             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9912             this.addClass("x-unselectable");
9913             return this;
9914         },
9915
9916         /**
9917         * Calculates the x, y to center this element on the screen
9918         * @return {Array} The x, y values [x, y]
9919         */
9920         getCenterXY : function(){
9921             return this.getAlignToXY(document, 'c-c');
9922         },
9923
9924         /**
9925         * Centers the Element in either the viewport, or another Element.
9926         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9927         */
9928         center : function(centerIn){
9929             this.alignTo(centerIn || document, 'c-c');
9930             return this;
9931         },
9932
9933         /**
9934          * Tests various css rules/browsers to determine if this element uses a border box
9935          * @return {Boolean}
9936          */
9937         isBorderBox : function(){
9938             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9939         },
9940
9941         /**
9942          * Return a box {x, y, width, height} that can be used to set another elements
9943          * size/location to match this element.
9944          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9945          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9946          * @return {Object} box An object in the format {x, y, width, height}
9947          */
9948         getBox : function(contentBox, local){
9949             var xy;
9950             if(!local){
9951                 xy = this.getXY();
9952             }else{
9953                 var left = parseInt(this.getStyle("left"), 10) || 0;
9954                 var top = parseInt(this.getStyle("top"), 10) || 0;
9955                 xy = [left, top];
9956             }
9957             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9958             if(!contentBox){
9959                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9960             }else{
9961                 var l = this.getBorderWidth("l")+this.getPadding("l");
9962                 var r = this.getBorderWidth("r")+this.getPadding("r");
9963                 var t = this.getBorderWidth("t")+this.getPadding("t");
9964                 var b = this.getBorderWidth("b")+this.getPadding("b");
9965                 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)};
9966             }
9967             bx.right = bx.x + bx.width;
9968             bx.bottom = bx.y + bx.height;
9969             return bx;
9970         },
9971
9972         /**
9973          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9974          for more information about the sides.
9975          * @param {String} sides
9976          * @return {Number}
9977          */
9978         getFrameWidth : function(sides, onlyContentBox){
9979             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9980         },
9981
9982         /**
9983          * 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.
9984          * @param {Object} box The box to fill {x, y, width, height}
9985          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9986          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9987          * @return {Roo.Element} this
9988          */
9989         setBox : function(box, adjust, animate){
9990             var w = box.width, h = box.height;
9991             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9992                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9993                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9994             }
9995             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9996             return this;
9997         },
9998
9999         /**
10000          * Forces the browser to repaint this element
10001          * @return {Roo.Element} this
10002          */
10003          repaint : function(){
10004             var dom = this.dom;
10005             this.addClass("x-repaint");
10006             setTimeout(function(){
10007                 Roo.get(dom).removeClass("x-repaint");
10008             }, 1);
10009             return this;
10010         },
10011
10012         /**
10013          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10014          * then it returns the calculated width of the sides (see getPadding)
10015          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10016          * @return {Object/Number}
10017          */
10018         getMargins : function(side){
10019             if(!side){
10020                 return {
10021                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10022                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10023                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10024                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10025                 };
10026             }else{
10027                 return this.addStyles(side, El.margins);
10028              }
10029         },
10030
10031         // private
10032         addStyles : function(sides, styles){
10033             var val = 0, v, w;
10034             for(var i = 0, len = sides.length; i < len; i++){
10035                 v = this.getStyle(styles[sides.charAt(i)]);
10036                 if(v){
10037                      w = parseInt(v, 10);
10038                      if(w){ val += w; }
10039                 }
10040             }
10041             return val;
10042         },
10043
10044         /**
10045          * Creates a proxy element of this element
10046          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10047          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10048          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10049          * @return {Roo.Element} The new proxy element
10050          */
10051         createProxy : function(config, renderTo, matchBox){
10052             if(renderTo){
10053                 renderTo = Roo.getDom(renderTo);
10054             }else{
10055                 renderTo = document.body;
10056             }
10057             config = typeof config == "object" ?
10058                 config : {tag : "div", cls: config};
10059             var proxy = Roo.DomHelper.append(renderTo, config, true);
10060             if(matchBox){
10061                proxy.setBox(this.getBox());
10062             }
10063             return proxy;
10064         },
10065
10066         /**
10067          * Puts a mask over this element to disable user interaction. Requires core.css.
10068          * This method can only be applied to elements which accept child nodes.
10069          * @param {String} msg (optional) A message to display in the mask
10070          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10071          * @return {Element} The mask  element
10072          */
10073         mask : function(msg, msgCls)
10074         {
10075             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10076                 this.setStyle("position", "relative");
10077             }
10078             if(!this._mask){
10079                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10080             }
10081             
10082             this.addClass("x-masked");
10083             this._mask.setDisplayed(true);
10084             
10085             // we wander
10086             var z = 0;
10087             var dom = this.dom;
10088             while (dom && dom.style) {
10089                 if (!isNaN(parseInt(dom.style.zIndex))) {
10090                     z = Math.max(z, parseInt(dom.style.zIndex));
10091                 }
10092                 dom = dom.parentNode;
10093             }
10094             // if we are masking the body - then it hides everything..
10095             if (this.dom == document.body) {
10096                 z = 1000000;
10097                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10098                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10099             }
10100            
10101             if(typeof msg == 'string'){
10102                 if(!this._maskMsg){
10103                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10104                         cls: "roo-el-mask-msg", 
10105                         cn: [
10106                             {
10107                                 tag: 'i',
10108                                 cls: 'fa fa-spinner fa-spin'
10109                             },
10110                             {
10111                                 tag: 'div'
10112                             }   
10113                         ]
10114                     }, true);
10115                 }
10116                 var mm = this._maskMsg;
10117                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10118                 if (mm.dom.lastChild) { // weird IE issue?
10119                     mm.dom.lastChild.innerHTML = msg;
10120                 }
10121                 mm.setDisplayed(true);
10122                 mm.center(this);
10123                 mm.setStyle('z-index', z + 102);
10124             }
10125             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10126                 this._mask.setHeight(this.getHeight());
10127             }
10128             this._mask.setStyle('z-index', z + 100);
10129             
10130             return this._mask;
10131         },
10132
10133         /**
10134          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10135          * it is cached for reuse.
10136          */
10137         unmask : function(removeEl){
10138             if(this._mask){
10139                 if(removeEl === true){
10140                     this._mask.remove();
10141                     delete this._mask;
10142                     if(this._maskMsg){
10143                         this._maskMsg.remove();
10144                         delete this._maskMsg;
10145                     }
10146                 }else{
10147                     this._mask.setDisplayed(false);
10148                     if(this._maskMsg){
10149                         this._maskMsg.setDisplayed(false);
10150                     }
10151                 }
10152             }
10153             this.removeClass("x-masked");
10154         },
10155
10156         /**
10157          * Returns true if this element is masked
10158          * @return {Boolean}
10159          */
10160         isMasked : function(){
10161             return this._mask && this._mask.isVisible();
10162         },
10163
10164         /**
10165          * Creates an iframe shim for this element to keep selects and other windowed objects from
10166          * showing through.
10167          * @return {Roo.Element} The new shim element
10168          */
10169         createShim : function(){
10170             var el = document.createElement('iframe');
10171             el.frameBorder = 'no';
10172             el.className = 'roo-shim';
10173             if(Roo.isIE && Roo.isSecure){
10174                 el.src = Roo.SSL_SECURE_URL;
10175             }
10176             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10177             shim.autoBoxAdjust = false;
10178             return shim;
10179         },
10180
10181         /**
10182          * Removes this element from the DOM and deletes it from the cache
10183          */
10184         remove : function(){
10185             if(this.dom.parentNode){
10186                 this.dom.parentNode.removeChild(this.dom);
10187             }
10188             delete El.cache[this.dom.id];
10189         },
10190
10191         /**
10192          * Sets up event handlers to add and remove a css class when the mouse is over this element
10193          * @param {String} className
10194          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10195          * mouseout events for children elements
10196          * @return {Roo.Element} this
10197          */
10198         addClassOnOver : function(className, preventFlicker){
10199             this.on("mouseover", function(){
10200                 Roo.fly(this, '_internal').addClass(className);
10201             }, this.dom);
10202             var removeFn = function(e){
10203                 if(preventFlicker !== true || !e.within(this, true)){
10204                     Roo.fly(this, '_internal').removeClass(className);
10205                 }
10206             };
10207             this.on("mouseout", removeFn, this.dom);
10208             return this;
10209         },
10210
10211         /**
10212          * Sets up event handlers to add and remove a css class when this element has the focus
10213          * @param {String} className
10214          * @return {Roo.Element} this
10215          */
10216         addClassOnFocus : function(className){
10217             this.on("focus", function(){
10218                 Roo.fly(this, '_internal').addClass(className);
10219             }, this.dom);
10220             this.on("blur", function(){
10221                 Roo.fly(this, '_internal').removeClass(className);
10222             }, this.dom);
10223             return this;
10224         },
10225         /**
10226          * 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)
10227          * @param {String} className
10228          * @return {Roo.Element} this
10229          */
10230         addClassOnClick : function(className){
10231             var dom = this.dom;
10232             this.on("mousedown", function(){
10233                 Roo.fly(dom, '_internal').addClass(className);
10234                 var d = Roo.get(document);
10235                 var fn = function(){
10236                     Roo.fly(dom, '_internal').removeClass(className);
10237                     d.removeListener("mouseup", fn);
10238                 };
10239                 d.on("mouseup", fn);
10240             });
10241             return this;
10242         },
10243
10244         /**
10245          * Stops the specified event from bubbling and optionally prevents the default action
10246          * @param {String} eventName
10247          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10248          * @return {Roo.Element} this
10249          */
10250         swallowEvent : function(eventName, preventDefault){
10251             var fn = function(e){
10252                 e.stopPropagation();
10253                 if(preventDefault){
10254                     e.preventDefault();
10255                 }
10256             };
10257             if(eventName instanceof Array){
10258                 for(var i = 0, len = eventName.length; i < len; i++){
10259                      this.on(eventName[i], fn);
10260                 }
10261                 return this;
10262             }
10263             this.on(eventName, fn);
10264             return this;
10265         },
10266
10267         /**
10268          * @private
10269          */
10270         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10271
10272         /**
10273          * Sizes this element to its parent element's dimensions performing
10274          * neccessary box adjustments.
10275          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10276          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10277          * @return {Roo.Element} this
10278          */
10279         fitToParent : function(monitorResize, targetParent) {
10280           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10281           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10282           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10283             return this;
10284           }
10285           var p = Roo.get(targetParent || this.dom.parentNode);
10286           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10287           if (monitorResize === true) {
10288             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10289             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10290           }
10291           return this;
10292         },
10293
10294         /**
10295          * Gets the next sibling, skipping text nodes
10296          * @return {HTMLElement} The next sibling or null
10297          */
10298         getNextSibling : function(){
10299             var n = this.dom.nextSibling;
10300             while(n && n.nodeType != 1){
10301                 n = n.nextSibling;
10302             }
10303             return n;
10304         },
10305
10306         /**
10307          * Gets the previous sibling, skipping text nodes
10308          * @return {HTMLElement} The previous sibling or null
10309          */
10310         getPrevSibling : function(){
10311             var n = this.dom.previousSibling;
10312             while(n && n.nodeType != 1){
10313                 n = n.previousSibling;
10314             }
10315             return n;
10316         },
10317
10318
10319         /**
10320          * Appends the passed element(s) to this element
10321          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10322          * @return {Roo.Element} this
10323          */
10324         appendChild: function(el){
10325             el = Roo.get(el);
10326             el.appendTo(this);
10327             return this;
10328         },
10329
10330         /**
10331          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10332          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10333          * automatically generated with the specified attributes.
10334          * @param {HTMLElement} insertBefore (optional) a child element of this element
10335          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10336          * @return {Roo.Element} The new child element
10337          */
10338         createChild: function(config, insertBefore, returnDom){
10339             config = config || {tag:'div'};
10340             if(insertBefore){
10341                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10342             }
10343             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10344         },
10345
10346         /**
10347          * Appends this element to the passed element
10348          * @param {String/HTMLElement/Element} el The new parent element
10349          * @return {Roo.Element} this
10350          */
10351         appendTo: function(el){
10352             el = Roo.getDom(el);
10353             el.appendChild(this.dom);
10354             return this;
10355         },
10356
10357         /**
10358          * Inserts this element before the passed element in the DOM
10359          * @param {String/HTMLElement/Element} el The element to insert before
10360          * @return {Roo.Element} this
10361          */
10362         insertBefore: function(el){
10363             el = Roo.getDom(el);
10364             el.parentNode.insertBefore(this.dom, el);
10365             return this;
10366         },
10367
10368         /**
10369          * Inserts this element after the passed element in the DOM
10370          * @param {String/HTMLElement/Element} el The element to insert after
10371          * @return {Roo.Element} this
10372          */
10373         insertAfter: function(el){
10374             el = Roo.getDom(el);
10375             el.parentNode.insertBefore(this.dom, el.nextSibling);
10376             return this;
10377         },
10378
10379         /**
10380          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10381          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10382          * @return {Roo.Element} The new child
10383          */
10384         insertFirst: function(el, returnDom){
10385             el = el || {};
10386             if(typeof el == 'object' && !el.nodeType){ // dh config
10387                 return this.createChild(el, this.dom.firstChild, returnDom);
10388             }else{
10389                 el = Roo.getDom(el);
10390                 this.dom.insertBefore(el, this.dom.firstChild);
10391                 return !returnDom ? Roo.get(el) : el;
10392             }
10393         },
10394
10395         /**
10396          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10397          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10398          * @param {String} where (optional) 'before' or 'after' defaults to before
10399          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10400          * @return {Roo.Element} the inserted Element
10401          */
10402         insertSibling: function(el, where, returnDom){
10403             where = where ? where.toLowerCase() : 'before';
10404             el = el || {};
10405             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10406
10407             if(typeof el == 'object' && !el.nodeType){ // dh config
10408                 if(where == 'after' && !this.dom.nextSibling){
10409                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10410                 }else{
10411                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10412                 }
10413
10414             }else{
10415                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10416                             where == 'before' ? this.dom : this.dom.nextSibling);
10417                 if(!returnDom){
10418                     rt = Roo.get(rt);
10419                 }
10420             }
10421             return rt;
10422         },
10423
10424         /**
10425          * Creates and wraps this element with another element
10426          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10427          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10428          * @return {HTMLElement/Element} The newly created wrapper element
10429          */
10430         wrap: function(config, returnDom){
10431             if(!config){
10432                 config = {tag: "div"};
10433             }
10434             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10435             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10436             return newEl;
10437         },
10438
10439         /**
10440          * Replaces the passed element with this element
10441          * @param {String/HTMLElement/Element} el The element to replace
10442          * @return {Roo.Element} this
10443          */
10444         replace: function(el){
10445             el = Roo.get(el);
10446             this.insertBefore(el);
10447             el.remove();
10448             return this;
10449         },
10450
10451         /**
10452          * Inserts an html fragment into this element
10453          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10454          * @param {String} html The HTML fragment
10455          * @param {Boolean} returnEl True to return an Roo.Element
10456          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10457          */
10458         insertHtml : function(where, html, returnEl){
10459             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10460             return returnEl ? Roo.get(el) : el;
10461         },
10462
10463         /**
10464          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10465          * @param {Object} o The object with the attributes
10466          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10467          * @return {Roo.Element} this
10468          */
10469         set : function(o, useSet){
10470             var el = this.dom;
10471             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10472             for(var attr in o){
10473                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10474                 if(attr=="cls"){
10475                     el.className = o["cls"];
10476                 }else{
10477                     if(useSet) {
10478                         el.setAttribute(attr, o[attr]);
10479                     } else {
10480                         el[attr] = o[attr];
10481                     }
10482                 }
10483             }
10484             if(o.style){
10485                 Roo.DomHelper.applyStyles(el, o.style);
10486             }
10487             return this;
10488         },
10489
10490         /**
10491          * Convenience method for constructing a KeyMap
10492          * @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:
10493          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10494          * @param {Function} fn The function to call
10495          * @param {Object} scope (optional) The scope of the function
10496          * @return {Roo.KeyMap} The KeyMap created
10497          */
10498         addKeyListener : function(key, fn, scope){
10499             var config;
10500             if(typeof key != "object" || key instanceof Array){
10501                 config = {
10502                     key: key,
10503                     fn: fn,
10504                     scope: scope
10505                 };
10506             }else{
10507                 config = {
10508                     key : key.key,
10509                     shift : key.shift,
10510                     ctrl : key.ctrl,
10511                     alt : key.alt,
10512                     fn: fn,
10513                     scope: scope
10514                 };
10515             }
10516             return new Roo.KeyMap(this, config);
10517         },
10518
10519         /**
10520          * Creates a KeyMap for this element
10521          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10522          * @return {Roo.KeyMap} The KeyMap created
10523          */
10524         addKeyMap : function(config){
10525             return new Roo.KeyMap(this, config);
10526         },
10527
10528         /**
10529          * Returns true if this element is scrollable.
10530          * @return {Boolean}
10531          */
10532          isScrollable : function(){
10533             var dom = this.dom;
10534             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10535         },
10536
10537         /**
10538          * 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().
10539          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10540          * @param {Number} value The new scroll value
10541          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10542          * @return {Element} this
10543          */
10544
10545         scrollTo : function(side, value, animate){
10546             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10547             if(!animate || !A){
10548                 this.dom[prop] = value;
10549             }else{
10550                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10551                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10552             }
10553             return this;
10554         },
10555
10556         /**
10557          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10558          * within this element's scrollable range.
10559          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10560          * @param {Number} distance How far to scroll the element in pixels
10561          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10562          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10563          * was scrolled as far as it could go.
10564          */
10565          scroll : function(direction, distance, animate){
10566              if(!this.isScrollable()){
10567                  return;
10568              }
10569              var el = this.dom;
10570              var l = el.scrollLeft, t = el.scrollTop;
10571              var w = el.scrollWidth, h = el.scrollHeight;
10572              var cw = el.clientWidth, ch = el.clientHeight;
10573              direction = direction.toLowerCase();
10574              var scrolled = false;
10575              var a = this.preanim(arguments, 2);
10576              switch(direction){
10577                  case "l":
10578                  case "left":
10579                      if(w - l > cw){
10580                          var v = Math.min(l + distance, w-cw);
10581                          this.scrollTo("left", v, a);
10582                          scrolled = true;
10583                      }
10584                      break;
10585                 case "r":
10586                 case "right":
10587                      if(l > 0){
10588                          var v = Math.max(l - distance, 0);
10589                          this.scrollTo("left", v, a);
10590                          scrolled = true;
10591                      }
10592                      break;
10593                 case "t":
10594                 case "top":
10595                 case "up":
10596                      if(t > 0){
10597                          var v = Math.max(t - distance, 0);
10598                          this.scrollTo("top", v, a);
10599                          scrolled = true;
10600                      }
10601                      break;
10602                 case "b":
10603                 case "bottom":
10604                 case "down":
10605                      if(h - t > ch){
10606                          var v = Math.min(t + distance, h-ch);
10607                          this.scrollTo("top", v, a);
10608                          scrolled = true;
10609                      }
10610                      break;
10611              }
10612              return scrolled;
10613         },
10614
10615         /**
10616          * Translates the passed page coordinates into left/top css values for this element
10617          * @param {Number/Array} x The page x or an array containing [x, y]
10618          * @param {Number} y The page y
10619          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10620          */
10621         translatePoints : function(x, y){
10622             if(typeof x == 'object' || x instanceof Array){
10623                 y = x[1]; x = x[0];
10624             }
10625             var p = this.getStyle('position');
10626             var o = this.getXY();
10627
10628             var l = parseInt(this.getStyle('left'), 10);
10629             var t = parseInt(this.getStyle('top'), 10);
10630
10631             if(isNaN(l)){
10632                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10633             }
10634             if(isNaN(t)){
10635                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10636             }
10637
10638             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10639         },
10640
10641         /**
10642          * Returns the current scroll position of the element.
10643          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10644          */
10645         getScroll : function(){
10646             var d = this.dom, doc = document;
10647             if(d == doc || d == doc.body){
10648                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10649                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10650                 return {left: l, top: t};
10651             }else{
10652                 return {left: d.scrollLeft, top: d.scrollTop};
10653             }
10654         },
10655
10656         /**
10657          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10658          * are convert to standard 6 digit hex color.
10659          * @param {String} attr The css attribute
10660          * @param {String} defaultValue The default value to use when a valid color isn't found
10661          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10662          * YUI color anims.
10663          */
10664         getColor : function(attr, defaultValue, prefix){
10665             var v = this.getStyle(attr);
10666             if(!v || v == "transparent" || v == "inherit") {
10667                 return defaultValue;
10668             }
10669             var color = typeof prefix == "undefined" ? "#" : prefix;
10670             if(v.substr(0, 4) == "rgb("){
10671                 var rvs = v.slice(4, v.length -1).split(",");
10672                 for(var i = 0; i < 3; i++){
10673                     var h = parseInt(rvs[i]).toString(16);
10674                     if(h < 16){
10675                         h = "0" + h;
10676                     }
10677                     color += h;
10678                 }
10679             } else {
10680                 if(v.substr(0, 1) == "#"){
10681                     if(v.length == 4) {
10682                         for(var i = 1; i < 4; i++){
10683                             var c = v.charAt(i);
10684                             color +=  c + c;
10685                         }
10686                     }else if(v.length == 7){
10687                         color += v.substr(1);
10688                     }
10689                 }
10690             }
10691             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10692         },
10693
10694         /**
10695          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10696          * gradient background, rounded corners and a 4-way shadow.
10697          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10698          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10699          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10700          * @return {Roo.Element} this
10701          */
10702         boxWrap : function(cls){
10703             cls = cls || 'x-box';
10704             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10705             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10706             return el;
10707         },
10708
10709         /**
10710          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10711          * @param {String} namespace The namespace in which to look for the attribute
10712          * @param {String} name The attribute name
10713          * @return {String} The attribute value
10714          */
10715         getAttributeNS : Roo.isIE ? function(ns, name){
10716             var d = this.dom;
10717             var type = typeof d[ns+":"+name];
10718             if(type != 'undefined' && type != 'unknown'){
10719                 return d[ns+":"+name];
10720             }
10721             return d[name];
10722         } : function(ns, name){
10723             var d = this.dom;
10724             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10725         },
10726         
10727         
10728         /**
10729          * Sets or Returns the value the dom attribute value
10730          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10731          * @param {String} value (optional) The value to set the attribute to
10732          * @return {String} The attribute value
10733          */
10734         attr : function(name){
10735             if (arguments.length > 1) {
10736                 this.dom.setAttribute(name, arguments[1]);
10737                 return arguments[1];
10738             }
10739             if (typeof(name) == 'object') {
10740                 for(var i in name) {
10741                     this.attr(i, name[i]);
10742                 }
10743                 return name;
10744             }
10745             
10746             
10747             if (!this.dom.hasAttribute(name)) {
10748                 return undefined;
10749             }
10750             return this.dom.getAttribute(name);
10751         }
10752         
10753         
10754         
10755     };
10756
10757     var ep = El.prototype;
10758
10759     /**
10760      * Appends an event handler (Shorthand for addListener)
10761      * @param {String}   eventName     The type of event to append
10762      * @param {Function} fn        The method the event invokes
10763      * @param {Object} scope       (optional) The scope (this object) of the fn
10764      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10765      * @method
10766      */
10767     ep.on = ep.addListener;
10768         // backwards compat
10769     ep.mon = ep.addListener;
10770
10771     /**
10772      * Removes an event handler from this element (shorthand for removeListener)
10773      * @param {String} eventName the type of event to remove
10774      * @param {Function} fn the method the event invokes
10775      * @return {Roo.Element} this
10776      * @method
10777      */
10778     ep.un = ep.removeListener;
10779
10780     /**
10781      * true to automatically adjust width and height settings for box-model issues (default to true)
10782      */
10783     ep.autoBoxAdjust = true;
10784
10785     // private
10786     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10787
10788     // private
10789     El.addUnits = function(v, defaultUnit){
10790         if(v === "" || v == "auto"){
10791             return v;
10792         }
10793         if(v === undefined){
10794             return '';
10795         }
10796         if(typeof v == "number" || !El.unitPattern.test(v)){
10797             return v + (defaultUnit || 'px');
10798         }
10799         return v;
10800     };
10801
10802     // special markup used throughout Roo when box wrapping elements
10803     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>';
10804     /**
10805      * Visibility mode constant - Use visibility to hide element
10806      * @static
10807      * @type Number
10808      */
10809     El.VISIBILITY = 1;
10810     /**
10811      * Visibility mode constant - Use display to hide element
10812      * @static
10813      * @type Number
10814      */
10815     El.DISPLAY = 2;
10816
10817     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10818     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10819     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10820
10821
10822
10823     /**
10824      * @private
10825      */
10826     El.cache = {};
10827
10828     var docEl;
10829
10830     /**
10831      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10832      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10833      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10834      * @return {Element} The Element object
10835      * @static
10836      */
10837     El.get = function(el){
10838         var ex, elm, id;
10839         if(!el){ return null; }
10840         if(typeof el == "string"){ // element id
10841             if(!(elm = document.getElementById(el))){
10842                 return null;
10843             }
10844             if(ex = El.cache[el]){
10845                 ex.dom = elm;
10846             }else{
10847                 ex = El.cache[el] = new El(elm);
10848             }
10849             return ex;
10850         }else if(el.tagName){ // dom element
10851             if(!(id = el.id)){
10852                 id = Roo.id(el);
10853             }
10854             if(ex = El.cache[id]){
10855                 ex.dom = el;
10856             }else{
10857                 ex = El.cache[id] = new El(el);
10858             }
10859             return ex;
10860         }else if(el instanceof El){
10861             if(el != docEl){
10862                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10863                                                               // catch case where it hasn't been appended
10864                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10865             }
10866             return el;
10867         }else if(el.isComposite){
10868             return el;
10869         }else if(el instanceof Array){
10870             return El.select(el);
10871         }else if(el == document){
10872             // create a bogus element object representing the document object
10873             if(!docEl){
10874                 var f = function(){};
10875                 f.prototype = El.prototype;
10876                 docEl = new f();
10877                 docEl.dom = document;
10878             }
10879             return docEl;
10880         }
10881         return null;
10882     };
10883
10884     // private
10885     El.uncache = function(el){
10886         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10887             if(a[i]){
10888                 delete El.cache[a[i].id || a[i]];
10889             }
10890         }
10891     };
10892
10893     // private
10894     // Garbage collection - uncache elements/purge listeners on orphaned elements
10895     // so we don't hold a reference and cause the browser to retain them
10896     El.garbageCollect = function(){
10897         if(!Roo.enableGarbageCollector){
10898             clearInterval(El.collectorThread);
10899             return;
10900         }
10901         for(var eid in El.cache){
10902             var el = El.cache[eid], d = el.dom;
10903             // -------------------------------------------------------
10904             // Determining what is garbage:
10905             // -------------------------------------------------------
10906             // !d
10907             // dom node is null, definitely garbage
10908             // -------------------------------------------------------
10909             // !d.parentNode
10910             // no parentNode == direct orphan, definitely garbage
10911             // -------------------------------------------------------
10912             // !d.offsetParent && !document.getElementById(eid)
10913             // display none elements have no offsetParent so we will
10914             // also try to look it up by it's id. However, check
10915             // offsetParent first so we don't do unneeded lookups.
10916             // This enables collection of elements that are not orphans
10917             // directly, but somewhere up the line they have an orphan
10918             // parent.
10919             // -------------------------------------------------------
10920             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10921                 delete El.cache[eid];
10922                 if(d && Roo.enableListenerCollection){
10923                     E.purgeElement(d);
10924                 }
10925             }
10926         }
10927     }
10928     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10929
10930
10931     // dom is optional
10932     El.Flyweight = function(dom){
10933         this.dom = dom;
10934     };
10935     El.Flyweight.prototype = El.prototype;
10936
10937     El._flyweights = {};
10938     /**
10939      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10940      * the dom node can be overwritten by other code.
10941      * @param {String/HTMLElement} el The dom node or id
10942      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10943      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10944      * @static
10945      * @return {Element} The shared Element object
10946      */
10947     El.fly = function(el, named){
10948         named = named || '_global';
10949         el = Roo.getDom(el);
10950         if(!el){
10951             return null;
10952         }
10953         if(!El._flyweights[named]){
10954             El._flyweights[named] = new El.Flyweight();
10955         }
10956         El._flyweights[named].dom = el;
10957         return El._flyweights[named];
10958     };
10959
10960     /**
10961      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10962      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10963      * Shorthand of {@link Roo.Element#get}
10964      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10965      * @return {Element} The Element object
10966      * @member Roo
10967      * @method get
10968      */
10969     Roo.get = El.get;
10970     /**
10971      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10972      * the dom node can be overwritten by other code.
10973      * Shorthand of {@link Roo.Element#fly}
10974      * @param {String/HTMLElement} el The dom node or id
10975      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10976      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10977      * @static
10978      * @return {Element} The shared Element object
10979      * @member Roo
10980      * @method fly
10981      */
10982     Roo.fly = El.fly;
10983
10984     // speedy lookup for elements never to box adjust
10985     var noBoxAdjust = Roo.isStrict ? {
10986         select:1
10987     } : {
10988         input:1, select:1, textarea:1
10989     };
10990     if(Roo.isIE || Roo.isGecko){
10991         noBoxAdjust['button'] = 1;
10992     }
10993
10994
10995     Roo.EventManager.on(window, 'unload', function(){
10996         delete El.cache;
10997         delete El._flyweights;
10998     });
10999 })();
11000
11001
11002
11003
11004 if(Roo.DomQuery){
11005     Roo.Element.selectorFunction = Roo.DomQuery.select;
11006 }
11007
11008 Roo.Element.select = function(selector, unique, root){
11009     var els;
11010     if(typeof selector == "string"){
11011         els = Roo.Element.selectorFunction(selector, root);
11012     }else if(selector.length !== undefined){
11013         els = selector;
11014     }else{
11015         throw "Invalid selector";
11016     }
11017     if(unique === true){
11018         return new Roo.CompositeElement(els);
11019     }else{
11020         return new Roo.CompositeElementLite(els);
11021     }
11022 };
11023 /**
11024  * Selects elements based on the passed CSS selector to enable working on them as 1.
11025  * @param {String/Array} selector The CSS selector or an array of elements
11026  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11027  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11028  * @return {CompositeElementLite/CompositeElement}
11029  * @member Roo
11030  * @method select
11031  */
11032 Roo.select = Roo.Element.select;
11033
11034
11035
11036
11037
11038
11039
11040
11041
11042
11043
11044
11045
11046
11047 /*
11048  * Based on:
11049  * Ext JS Library 1.1.1
11050  * Copyright(c) 2006-2007, Ext JS, LLC.
11051  *
11052  * Originally Released Under LGPL - original licence link has changed is not relivant.
11053  *
11054  * Fork - LGPL
11055  * <script type="text/javascript">
11056  */
11057
11058
11059
11060 //Notifies Element that fx methods are available
11061 Roo.enableFx = true;
11062
11063 /**
11064  * @class Roo.Fx
11065  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11066  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11067  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11068  * Element effects to work.</p><br/>
11069  *
11070  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11071  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11072  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11073  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11074  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11075  * expected results and should be done with care.</p><br/>
11076  *
11077  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11078  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11079 <pre>
11080 Value  Description
11081 -----  -----------------------------
11082 tl     The top left corner
11083 t      The center of the top edge
11084 tr     The top right corner
11085 l      The center of the left edge
11086 r      The center of the right edge
11087 bl     The bottom left corner
11088 b      The center of the bottom edge
11089 br     The bottom right corner
11090 </pre>
11091  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11092  * below are common options that can be passed to any Fx method.</b>
11093  * @cfg {Function} callback A function called when the effect is finished
11094  * @cfg {Object} scope The scope of the effect function
11095  * @cfg {String} easing A valid Easing value for the effect
11096  * @cfg {String} afterCls A css class to apply after the effect
11097  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11098  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11099  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11100  * effects that end with the element being visually hidden, ignored otherwise)
11101  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11102  * a function which returns such a specification that will be applied to the Element after the effect finishes
11103  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11104  * @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
11105  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11106  */
11107 Roo.Fx = {
11108         /**
11109          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11110          * origin for the slide effect.  This function automatically handles wrapping the element with
11111          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11112          * Usage:
11113          *<pre><code>
11114 // default: slide the element in from the top
11115 el.slideIn();
11116
11117 // custom: slide the element in from the right with a 2-second duration
11118 el.slideIn('r', { duration: 2 });
11119
11120 // common config options shown with default values
11121 el.slideIn('t', {
11122     easing: 'easeOut',
11123     duration: .5
11124 });
11125 </code></pre>
11126          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11127          * @param {Object} options (optional) Object literal with any of the Fx config options
11128          * @return {Roo.Element} The Element
11129          */
11130     slideIn : function(anchor, o){
11131         var el = this.getFxEl();
11132         o = o || {};
11133
11134         el.queueFx(o, function(){
11135
11136             anchor = anchor || "t";
11137
11138             // fix display to visibility
11139             this.fixDisplay();
11140
11141             // restore values after effect
11142             var r = this.getFxRestore();
11143             var b = this.getBox();
11144             // fixed size for slide
11145             this.setSize(b);
11146
11147             // wrap if needed
11148             var wrap = this.fxWrap(r.pos, o, "hidden");
11149
11150             var st = this.dom.style;
11151             st.visibility = "visible";
11152             st.position = "absolute";
11153
11154             // clear out temp styles after slide and unwrap
11155             var after = function(){
11156                 el.fxUnwrap(wrap, r.pos, o);
11157                 st.width = r.width;
11158                 st.height = r.height;
11159                 el.afterFx(o);
11160             };
11161             // time to calc the positions
11162             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11163
11164             switch(anchor.toLowerCase()){
11165                 case "t":
11166                     wrap.setSize(b.width, 0);
11167                     st.left = st.bottom = "0";
11168                     a = {height: bh};
11169                 break;
11170                 case "l":
11171                     wrap.setSize(0, b.height);
11172                     st.right = st.top = "0";
11173                     a = {width: bw};
11174                 break;
11175                 case "r":
11176                     wrap.setSize(0, b.height);
11177                     wrap.setX(b.right);
11178                     st.left = st.top = "0";
11179                     a = {width: bw, points: pt};
11180                 break;
11181                 case "b":
11182                     wrap.setSize(b.width, 0);
11183                     wrap.setY(b.bottom);
11184                     st.left = st.top = "0";
11185                     a = {height: bh, points: pt};
11186                 break;
11187                 case "tl":
11188                     wrap.setSize(0, 0);
11189                     st.right = st.bottom = "0";
11190                     a = {width: bw, height: bh};
11191                 break;
11192                 case "bl":
11193                     wrap.setSize(0, 0);
11194                     wrap.setY(b.y+b.height);
11195                     st.right = st.top = "0";
11196                     a = {width: bw, height: bh, points: pt};
11197                 break;
11198                 case "br":
11199                     wrap.setSize(0, 0);
11200                     wrap.setXY([b.right, b.bottom]);
11201                     st.left = st.top = "0";
11202                     a = {width: bw, height: bh, points: pt};
11203                 break;
11204                 case "tr":
11205                     wrap.setSize(0, 0);
11206                     wrap.setX(b.x+b.width);
11207                     st.left = st.bottom = "0";
11208                     a = {width: bw, height: bh, points: pt};
11209                 break;
11210             }
11211             this.dom.style.visibility = "visible";
11212             wrap.show();
11213
11214             arguments.callee.anim = wrap.fxanim(a,
11215                 o,
11216                 'motion',
11217                 .5,
11218                 'easeOut', after);
11219         });
11220         return this;
11221     },
11222     
11223         /**
11224          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11225          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11226          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11227          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11228          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11229          * Usage:
11230          *<pre><code>
11231 // default: slide the element out to the top
11232 el.slideOut();
11233
11234 // custom: slide the element out to the right with a 2-second duration
11235 el.slideOut('r', { duration: 2 });
11236
11237 // common config options shown with default values
11238 el.slideOut('t', {
11239     easing: 'easeOut',
11240     duration: .5,
11241     remove: false,
11242     useDisplay: false
11243 });
11244 </code></pre>
11245          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11246          * @param {Object} options (optional) Object literal with any of the Fx config options
11247          * @return {Roo.Element} The Element
11248          */
11249     slideOut : function(anchor, o){
11250         var el = this.getFxEl();
11251         o = o || {};
11252
11253         el.queueFx(o, function(){
11254
11255             anchor = anchor || "t";
11256
11257             // restore values after effect
11258             var r = this.getFxRestore();
11259             
11260             var b = this.getBox();
11261             // fixed size for slide
11262             this.setSize(b);
11263
11264             // wrap if needed
11265             var wrap = this.fxWrap(r.pos, o, "visible");
11266
11267             var st = this.dom.style;
11268             st.visibility = "visible";
11269             st.position = "absolute";
11270
11271             wrap.setSize(b);
11272
11273             var after = function(){
11274                 if(o.useDisplay){
11275                     el.setDisplayed(false);
11276                 }else{
11277                     el.hide();
11278                 }
11279
11280                 el.fxUnwrap(wrap, r.pos, o);
11281
11282                 st.width = r.width;
11283                 st.height = r.height;
11284
11285                 el.afterFx(o);
11286             };
11287
11288             var a, zero = {to: 0};
11289             switch(anchor.toLowerCase()){
11290                 case "t":
11291                     st.left = st.bottom = "0";
11292                     a = {height: zero};
11293                 break;
11294                 case "l":
11295                     st.right = st.top = "0";
11296                     a = {width: zero};
11297                 break;
11298                 case "r":
11299                     st.left = st.top = "0";
11300                     a = {width: zero, points: {to:[b.right, b.y]}};
11301                 break;
11302                 case "b":
11303                     st.left = st.top = "0";
11304                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11305                 break;
11306                 case "tl":
11307                     st.right = st.bottom = "0";
11308                     a = {width: zero, height: zero};
11309                 break;
11310                 case "bl":
11311                     st.right = st.top = "0";
11312                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11313                 break;
11314                 case "br":
11315                     st.left = st.top = "0";
11316                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11317                 break;
11318                 case "tr":
11319                     st.left = st.bottom = "0";
11320                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11321                 break;
11322             }
11323
11324             arguments.callee.anim = wrap.fxanim(a,
11325                 o,
11326                 'motion',
11327                 .5,
11328                 "easeOut", after);
11329         });
11330         return this;
11331     },
11332
11333         /**
11334          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11335          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11336          * The element must be removed from the DOM using the 'remove' config option if desired.
11337          * Usage:
11338          *<pre><code>
11339 // default
11340 el.puff();
11341
11342 // common config options shown with default values
11343 el.puff({
11344     easing: 'easeOut',
11345     duration: .5,
11346     remove: false,
11347     useDisplay: false
11348 });
11349 </code></pre>
11350          * @param {Object} options (optional) Object literal with any of the Fx config options
11351          * @return {Roo.Element} The Element
11352          */
11353     puff : function(o){
11354         var el = this.getFxEl();
11355         o = o || {};
11356
11357         el.queueFx(o, function(){
11358             this.clearOpacity();
11359             this.show();
11360
11361             // restore values after effect
11362             var r = this.getFxRestore();
11363             var st = this.dom.style;
11364
11365             var after = function(){
11366                 if(o.useDisplay){
11367                     el.setDisplayed(false);
11368                 }else{
11369                     el.hide();
11370                 }
11371
11372                 el.clearOpacity();
11373
11374                 el.setPositioning(r.pos);
11375                 st.width = r.width;
11376                 st.height = r.height;
11377                 st.fontSize = '';
11378                 el.afterFx(o);
11379             };
11380
11381             var width = this.getWidth();
11382             var height = this.getHeight();
11383
11384             arguments.callee.anim = this.fxanim({
11385                     width : {to: this.adjustWidth(width * 2)},
11386                     height : {to: this.adjustHeight(height * 2)},
11387                     points : {by: [-(width * .5), -(height * .5)]},
11388                     opacity : {to: 0},
11389                     fontSize: {to:200, unit: "%"}
11390                 },
11391                 o,
11392                 'motion',
11393                 .5,
11394                 "easeOut", after);
11395         });
11396         return this;
11397     },
11398
11399         /**
11400          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11401          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11402          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11403          * Usage:
11404          *<pre><code>
11405 // default
11406 el.switchOff();
11407
11408 // all config options shown with default values
11409 el.switchOff({
11410     easing: 'easeIn',
11411     duration: .3,
11412     remove: false,
11413     useDisplay: false
11414 });
11415 </code></pre>
11416          * @param {Object} options (optional) Object literal with any of the Fx config options
11417          * @return {Roo.Element} The Element
11418          */
11419     switchOff : function(o){
11420         var el = this.getFxEl();
11421         o = o || {};
11422
11423         el.queueFx(o, function(){
11424             this.clearOpacity();
11425             this.clip();
11426
11427             // restore values after effect
11428             var r = this.getFxRestore();
11429             var st = this.dom.style;
11430
11431             var after = function(){
11432                 if(o.useDisplay){
11433                     el.setDisplayed(false);
11434                 }else{
11435                     el.hide();
11436                 }
11437
11438                 el.clearOpacity();
11439                 el.setPositioning(r.pos);
11440                 st.width = r.width;
11441                 st.height = r.height;
11442
11443                 el.afterFx(o);
11444             };
11445
11446             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11447                 this.clearOpacity();
11448                 (function(){
11449                     this.fxanim({
11450                         height:{to:1},
11451                         points:{by:[0, this.getHeight() * .5]}
11452                     }, o, 'motion', 0.3, 'easeIn', after);
11453                 }).defer(100, this);
11454             });
11455         });
11456         return this;
11457     },
11458
11459     /**
11460      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11461      * changed using the "attr" config option) and then fading back to the original color. If no original
11462      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11463      * Usage:
11464 <pre><code>
11465 // default: highlight background to yellow
11466 el.highlight();
11467
11468 // custom: highlight foreground text to blue for 2 seconds
11469 el.highlight("0000ff", { attr: 'color', duration: 2 });
11470
11471 // common config options shown with default values
11472 el.highlight("ffff9c", {
11473     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11474     endColor: (current color) or "ffffff",
11475     easing: 'easeIn',
11476     duration: 1
11477 });
11478 </code></pre>
11479      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11480      * @param {Object} options (optional) Object literal with any of the Fx config options
11481      * @return {Roo.Element} The Element
11482      */ 
11483     highlight : function(color, o){
11484         var el = this.getFxEl();
11485         o = o || {};
11486
11487         el.queueFx(o, function(){
11488             color = color || "ffff9c";
11489             attr = o.attr || "backgroundColor";
11490
11491             this.clearOpacity();
11492             this.show();
11493
11494             var origColor = this.getColor(attr);
11495             var restoreColor = this.dom.style[attr];
11496             endColor = (o.endColor || origColor) || "ffffff";
11497
11498             var after = function(){
11499                 el.dom.style[attr] = restoreColor;
11500                 el.afterFx(o);
11501             };
11502
11503             var a = {};
11504             a[attr] = {from: color, to: endColor};
11505             arguments.callee.anim = this.fxanim(a,
11506                 o,
11507                 'color',
11508                 1,
11509                 'easeIn', after);
11510         });
11511         return this;
11512     },
11513
11514    /**
11515     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11516     * Usage:
11517 <pre><code>
11518 // default: a single light blue ripple
11519 el.frame();
11520
11521 // custom: 3 red ripples lasting 3 seconds total
11522 el.frame("ff0000", 3, { duration: 3 });
11523
11524 // common config options shown with default values
11525 el.frame("C3DAF9", 1, {
11526     duration: 1 //duration of entire animation (not each individual ripple)
11527     // Note: Easing is not configurable and will be ignored if included
11528 });
11529 </code></pre>
11530     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11531     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11532     * @param {Object} options (optional) Object literal with any of the Fx config options
11533     * @return {Roo.Element} The Element
11534     */
11535     frame : function(color, count, o){
11536         var el = this.getFxEl();
11537         o = o || {};
11538
11539         el.queueFx(o, function(){
11540             color = color || "#C3DAF9";
11541             if(color.length == 6){
11542                 color = "#" + color;
11543             }
11544             count = count || 1;
11545             duration = o.duration || 1;
11546             this.show();
11547
11548             var b = this.getBox();
11549             var animFn = function(){
11550                 var proxy = this.createProxy({
11551
11552                      style:{
11553                         visbility:"hidden",
11554                         position:"absolute",
11555                         "z-index":"35000", // yee haw
11556                         border:"0px solid " + color
11557                      }
11558                   });
11559                 var scale = Roo.isBorderBox ? 2 : 1;
11560                 proxy.animate({
11561                     top:{from:b.y, to:b.y - 20},
11562                     left:{from:b.x, to:b.x - 20},
11563                     borderWidth:{from:0, to:10},
11564                     opacity:{from:1, to:0},
11565                     height:{from:b.height, to:(b.height + (20*scale))},
11566                     width:{from:b.width, to:(b.width + (20*scale))}
11567                 }, duration, function(){
11568                     proxy.remove();
11569                 });
11570                 if(--count > 0){
11571                      animFn.defer((duration/2)*1000, this);
11572                 }else{
11573                     el.afterFx(o);
11574                 }
11575             };
11576             animFn.call(this);
11577         });
11578         return this;
11579     },
11580
11581    /**
11582     * Creates a pause before any subsequent queued effects begin.  If there are
11583     * no effects queued after the pause it will have no effect.
11584     * Usage:
11585 <pre><code>
11586 el.pause(1);
11587 </code></pre>
11588     * @param {Number} seconds The length of time to pause (in seconds)
11589     * @return {Roo.Element} The Element
11590     */
11591     pause : function(seconds){
11592         var el = this.getFxEl();
11593         var o = {};
11594
11595         el.queueFx(o, function(){
11596             setTimeout(function(){
11597                 el.afterFx(o);
11598             }, seconds * 1000);
11599         });
11600         return this;
11601     },
11602
11603    /**
11604     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11605     * using the "endOpacity" config option.
11606     * Usage:
11607 <pre><code>
11608 // default: fade in from opacity 0 to 100%
11609 el.fadeIn();
11610
11611 // custom: fade in from opacity 0 to 75% over 2 seconds
11612 el.fadeIn({ endOpacity: .75, duration: 2});
11613
11614 // common config options shown with default values
11615 el.fadeIn({
11616     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11617     easing: 'easeOut',
11618     duration: .5
11619 });
11620 </code></pre>
11621     * @param {Object} options (optional) Object literal with any of the Fx config options
11622     * @return {Roo.Element} The Element
11623     */
11624     fadeIn : function(o){
11625         var el = this.getFxEl();
11626         o = o || {};
11627         el.queueFx(o, function(){
11628             this.setOpacity(0);
11629             this.fixDisplay();
11630             this.dom.style.visibility = 'visible';
11631             var to = o.endOpacity || 1;
11632             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11633                 o, null, .5, "easeOut", function(){
11634                 if(to == 1){
11635                     this.clearOpacity();
11636                 }
11637                 el.afterFx(o);
11638             });
11639         });
11640         return this;
11641     },
11642
11643    /**
11644     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11645     * using the "endOpacity" config option.
11646     * Usage:
11647 <pre><code>
11648 // default: fade out from the element's current opacity to 0
11649 el.fadeOut();
11650
11651 // custom: fade out from the element's current opacity to 25% over 2 seconds
11652 el.fadeOut({ endOpacity: .25, duration: 2});
11653
11654 // common config options shown with default values
11655 el.fadeOut({
11656     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11657     easing: 'easeOut',
11658     duration: .5
11659     remove: false,
11660     useDisplay: false
11661 });
11662 </code></pre>
11663     * @param {Object} options (optional) Object literal with any of the Fx config options
11664     * @return {Roo.Element} The Element
11665     */
11666     fadeOut : function(o){
11667         var el = this.getFxEl();
11668         o = o || {};
11669         el.queueFx(o, function(){
11670             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11671                 o, null, .5, "easeOut", function(){
11672                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11673                      this.dom.style.display = "none";
11674                 }else{
11675                      this.dom.style.visibility = "hidden";
11676                 }
11677                 this.clearOpacity();
11678                 el.afterFx(o);
11679             });
11680         });
11681         return this;
11682     },
11683
11684    /**
11685     * Animates the transition of an element's dimensions from a starting height/width
11686     * to an ending height/width.
11687     * Usage:
11688 <pre><code>
11689 // change height and width to 100x100 pixels
11690 el.scale(100, 100);
11691
11692 // common config options shown with default values.  The height and width will default to
11693 // the element's existing values if passed as null.
11694 el.scale(
11695     [element's width],
11696     [element's height], {
11697     easing: 'easeOut',
11698     duration: .35
11699 });
11700 </code></pre>
11701     * @param {Number} width  The new width (pass undefined to keep the original width)
11702     * @param {Number} height  The new height (pass undefined to keep the original height)
11703     * @param {Object} options (optional) Object literal with any of the Fx config options
11704     * @return {Roo.Element} The Element
11705     */
11706     scale : function(w, h, o){
11707         this.shift(Roo.apply({}, o, {
11708             width: w,
11709             height: h
11710         }));
11711         return this;
11712     },
11713
11714    /**
11715     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11716     * Any of these properties not specified in the config object will not be changed.  This effect 
11717     * requires that at least one new dimension, position or opacity setting must be passed in on
11718     * the config object in order for the function to have any effect.
11719     * Usage:
11720 <pre><code>
11721 // slide the element horizontally to x position 200 while changing the height and opacity
11722 el.shift({ x: 200, height: 50, opacity: .8 });
11723
11724 // common config options shown with default values.
11725 el.shift({
11726     width: [element's width],
11727     height: [element's height],
11728     x: [element's x position],
11729     y: [element's y position],
11730     opacity: [element's opacity],
11731     easing: 'easeOut',
11732     duration: .35
11733 });
11734 </code></pre>
11735     * @param {Object} options  Object literal with any of the Fx config options
11736     * @return {Roo.Element} The Element
11737     */
11738     shift : function(o){
11739         var el = this.getFxEl();
11740         o = o || {};
11741         el.queueFx(o, function(){
11742             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11743             if(w !== undefined){
11744                 a.width = {to: this.adjustWidth(w)};
11745             }
11746             if(h !== undefined){
11747                 a.height = {to: this.adjustHeight(h)};
11748             }
11749             if(x !== undefined || y !== undefined){
11750                 a.points = {to: [
11751                     x !== undefined ? x : this.getX(),
11752                     y !== undefined ? y : this.getY()
11753                 ]};
11754             }
11755             if(op !== undefined){
11756                 a.opacity = {to: op};
11757             }
11758             if(o.xy !== undefined){
11759                 a.points = {to: o.xy};
11760             }
11761             arguments.callee.anim = this.fxanim(a,
11762                 o, 'motion', .35, "easeOut", function(){
11763                 el.afterFx(o);
11764             });
11765         });
11766         return this;
11767     },
11768
11769         /**
11770          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11771          * ending point of the effect.
11772          * Usage:
11773          *<pre><code>
11774 // default: slide the element downward while fading out
11775 el.ghost();
11776
11777 // custom: slide the element out to the right with a 2-second duration
11778 el.ghost('r', { duration: 2 });
11779
11780 // common config options shown with default values
11781 el.ghost('b', {
11782     easing: 'easeOut',
11783     duration: .5
11784     remove: false,
11785     useDisplay: false
11786 });
11787 </code></pre>
11788          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11789          * @param {Object} options (optional) Object literal with any of the Fx config options
11790          * @return {Roo.Element} The Element
11791          */
11792     ghost : function(anchor, o){
11793         var el = this.getFxEl();
11794         o = o || {};
11795
11796         el.queueFx(o, function(){
11797             anchor = anchor || "b";
11798
11799             // restore values after effect
11800             var r = this.getFxRestore();
11801             var w = this.getWidth(),
11802                 h = this.getHeight();
11803
11804             var st = this.dom.style;
11805
11806             var after = function(){
11807                 if(o.useDisplay){
11808                     el.setDisplayed(false);
11809                 }else{
11810                     el.hide();
11811                 }
11812
11813                 el.clearOpacity();
11814                 el.setPositioning(r.pos);
11815                 st.width = r.width;
11816                 st.height = r.height;
11817
11818                 el.afterFx(o);
11819             };
11820
11821             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11822             switch(anchor.toLowerCase()){
11823                 case "t":
11824                     pt.by = [0, -h];
11825                 break;
11826                 case "l":
11827                     pt.by = [-w, 0];
11828                 break;
11829                 case "r":
11830                     pt.by = [w, 0];
11831                 break;
11832                 case "b":
11833                     pt.by = [0, h];
11834                 break;
11835                 case "tl":
11836                     pt.by = [-w, -h];
11837                 break;
11838                 case "bl":
11839                     pt.by = [-w, h];
11840                 break;
11841                 case "br":
11842                     pt.by = [w, h];
11843                 break;
11844                 case "tr":
11845                     pt.by = [w, -h];
11846                 break;
11847             }
11848
11849             arguments.callee.anim = this.fxanim(a,
11850                 o,
11851                 'motion',
11852                 .5,
11853                 "easeOut", after);
11854         });
11855         return this;
11856     },
11857
11858         /**
11859          * Ensures that all effects queued after syncFx is called on the element are
11860          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11861          * @return {Roo.Element} The Element
11862          */
11863     syncFx : function(){
11864         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11865             block : false,
11866             concurrent : true,
11867             stopFx : false
11868         });
11869         return this;
11870     },
11871
11872         /**
11873          * Ensures that all effects queued after sequenceFx is called on the element are
11874          * run in sequence.  This is the opposite of {@link #syncFx}.
11875          * @return {Roo.Element} The Element
11876          */
11877     sequenceFx : function(){
11878         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11879             block : false,
11880             concurrent : false,
11881             stopFx : false
11882         });
11883         return this;
11884     },
11885
11886         /* @private */
11887     nextFx : function(){
11888         var ef = this.fxQueue[0];
11889         if(ef){
11890             ef.call(this);
11891         }
11892     },
11893
11894         /**
11895          * Returns true if the element has any effects actively running or queued, else returns false.
11896          * @return {Boolean} True if element has active effects, else false
11897          */
11898     hasActiveFx : function(){
11899         return this.fxQueue && this.fxQueue[0];
11900     },
11901
11902         /**
11903          * Stops any running effects and clears the element's internal effects queue if it contains
11904          * any additional effects that haven't started yet.
11905          * @return {Roo.Element} The Element
11906          */
11907     stopFx : function(){
11908         if(this.hasActiveFx()){
11909             var cur = this.fxQueue[0];
11910             if(cur && cur.anim && cur.anim.isAnimated()){
11911                 this.fxQueue = [cur]; // clear out others
11912                 cur.anim.stop(true);
11913             }
11914         }
11915         return this;
11916     },
11917
11918         /* @private */
11919     beforeFx : function(o){
11920         if(this.hasActiveFx() && !o.concurrent){
11921            if(o.stopFx){
11922                this.stopFx();
11923                return true;
11924            }
11925            return false;
11926         }
11927         return true;
11928     },
11929
11930         /**
11931          * Returns true if the element is currently blocking so that no other effect can be queued
11932          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11933          * used to ensure that an effect initiated by a user action runs to completion prior to the
11934          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11935          * @return {Boolean} True if blocking, else false
11936          */
11937     hasFxBlock : function(){
11938         var q = this.fxQueue;
11939         return q && q[0] && q[0].block;
11940     },
11941
11942         /* @private */
11943     queueFx : function(o, fn){
11944         if(!this.fxQueue){
11945             this.fxQueue = [];
11946         }
11947         if(!this.hasFxBlock()){
11948             Roo.applyIf(o, this.fxDefaults);
11949             if(!o.concurrent){
11950                 var run = this.beforeFx(o);
11951                 fn.block = o.block;
11952                 this.fxQueue.push(fn);
11953                 if(run){
11954                     this.nextFx();
11955                 }
11956             }else{
11957                 fn.call(this);
11958             }
11959         }
11960         return this;
11961     },
11962
11963         /* @private */
11964     fxWrap : function(pos, o, vis){
11965         var wrap;
11966         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11967             var wrapXY;
11968             if(o.fixPosition){
11969                 wrapXY = this.getXY();
11970             }
11971             var div = document.createElement("div");
11972             div.style.visibility = vis;
11973             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11974             wrap.setPositioning(pos);
11975             if(wrap.getStyle("position") == "static"){
11976                 wrap.position("relative");
11977             }
11978             this.clearPositioning('auto');
11979             wrap.clip();
11980             wrap.dom.appendChild(this.dom);
11981             if(wrapXY){
11982                 wrap.setXY(wrapXY);
11983             }
11984         }
11985         return wrap;
11986     },
11987
11988         /* @private */
11989     fxUnwrap : function(wrap, pos, o){
11990         this.clearPositioning();
11991         this.setPositioning(pos);
11992         if(!o.wrap){
11993             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11994             wrap.remove();
11995         }
11996     },
11997
11998         /* @private */
11999     getFxRestore : function(){
12000         var st = this.dom.style;
12001         return {pos: this.getPositioning(), width: st.width, height : st.height};
12002     },
12003
12004         /* @private */
12005     afterFx : function(o){
12006         if(o.afterStyle){
12007             this.applyStyles(o.afterStyle);
12008         }
12009         if(o.afterCls){
12010             this.addClass(o.afterCls);
12011         }
12012         if(o.remove === true){
12013             this.remove();
12014         }
12015         Roo.callback(o.callback, o.scope, [this]);
12016         if(!o.concurrent){
12017             this.fxQueue.shift();
12018             this.nextFx();
12019         }
12020     },
12021
12022         /* @private */
12023     getFxEl : function(){ // support for composite element fx
12024         return Roo.get(this.dom);
12025     },
12026
12027         /* @private */
12028     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12029         animType = animType || 'run';
12030         opt = opt || {};
12031         var anim = Roo.lib.Anim[animType](
12032             this.dom, args,
12033             (opt.duration || defaultDur) || .35,
12034             (opt.easing || defaultEase) || 'easeOut',
12035             function(){
12036                 Roo.callback(cb, this);
12037             },
12038             this
12039         );
12040         opt.anim = anim;
12041         return anim;
12042     }
12043 };
12044
12045 // backwords compat
12046 Roo.Fx.resize = Roo.Fx.scale;
12047
12048 //When included, Roo.Fx is automatically applied to Element so that all basic
12049 //effects are available directly via the Element API
12050 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12051  * Based on:
12052  * Ext JS Library 1.1.1
12053  * Copyright(c) 2006-2007, Ext JS, LLC.
12054  *
12055  * Originally Released Under LGPL - original licence link has changed is not relivant.
12056  *
12057  * Fork - LGPL
12058  * <script type="text/javascript">
12059  */
12060
12061
12062 /**
12063  * @class Roo.CompositeElement
12064  * Standard composite class. Creates a Roo.Element for every element in the collection.
12065  * <br><br>
12066  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12067  * actions will be performed on all the elements in this collection.</b>
12068  * <br><br>
12069  * All methods return <i>this</i> and can be chained.
12070  <pre><code>
12071  var els = Roo.select("#some-el div.some-class", true);
12072  // or select directly from an existing element
12073  var el = Roo.get('some-el');
12074  el.select('div.some-class', true);
12075
12076  els.setWidth(100); // all elements become 100 width
12077  els.hide(true); // all elements fade out and hide
12078  // or
12079  els.setWidth(100).hide(true);
12080  </code></pre>
12081  */
12082 Roo.CompositeElement = function(els){
12083     this.elements = [];
12084     this.addElements(els);
12085 };
12086 Roo.CompositeElement.prototype = {
12087     isComposite: true,
12088     addElements : function(els){
12089         if(!els) {
12090             return this;
12091         }
12092         if(typeof els == "string"){
12093             els = Roo.Element.selectorFunction(els);
12094         }
12095         var yels = this.elements;
12096         var index = yels.length-1;
12097         for(var i = 0, len = els.length; i < len; i++) {
12098                 yels[++index] = Roo.get(els[i]);
12099         }
12100         return this;
12101     },
12102
12103     /**
12104     * Clears this composite and adds the elements returned by the passed selector.
12105     * @param {String/Array} els A string CSS selector, an array of elements or an element
12106     * @return {CompositeElement} this
12107     */
12108     fill : function(els){
12109         this.elements = [];
12110         this.add(els);
12111         return this;
12112     },
12113
12114     /**
12115     * Filters this composite to only elements that match the passed selector.
12116     * @param {String} selector A string CSS selector
12117     * @param {Boolean} inverse return inverse filter (not matches)
12118     * @return {CompositeElement} this
12119     */
12120     filter : function(selector, inverse){
12121         var els = [];
12122         inverse = inverse || false;
12123         this.each(function(el){
12124             var match = inverse ? !el.is(selector) : el.is(selector);
12125             if(match){
12126                 els[els.length] = el.dom;
12127             }
12128         });
12129         this.fill(els);
12130         return this;
12131     },
12132
12133     invoke : function(fn, args){
12134         var els = this.elements;
12135         for(var i = 0, len = els.length; i < len; i++) {
12136                 Roo.Element.prototype[fn].apply(els[i], args);
12137         }
12138         return this;
12139     },
12140     /**
12141     * Adds elements to this composite.
12142     * @param {String/Array} els A string CSS selector, an array of elements or an element
12143     * @return {CompositeElement} this
12144     */
12145     add : function(els){
12146         if(typeof els == "string"){
12147             this.addElements(Roo.Element.selectorFunction(els));
12148         }else if(els.length !== undefined){
12149             this.addElements(els);
12150         }else{
12151             this.addElements([els]);
12152         }
12153         return this;
12154     },
12155     /**
12156     * Calls the passed function passing (el, this, index) for each element in this composite.
12157     * @param {Function} fn The function to call
12158     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12159     * @return {CompositeElement} this
12160     */
12161     each : function(fn, scope){
12162         var els = this.elements;
12163         for(var i = 0, len = els.length; i < len; i++){
12164             if(fn.call(scope || els[i], els[i], this, i) === false) {
12165                 break;
12166             }
12167         }
12168         return this;
12169     },
12170
12171     /**
12172      * Returns the Element object at the specified index
12173      * @param {Number} index
12174      * @return {Roo.Element}
12175      */
12176     item : function(index){
12177         return this.elements[index] || null;
12178     },
12179
12180     /**
12181      * Returns the first Element
12182      * @return {Roo.Element}
12183      */
12184     first : function(){
12185         return this.item(0);
12186     },
12187
12188     /**
12189      * Returns the last Element
12190      * @return {Roo.Element}
12191      */
12192     last : function(){
12193         return this.item(this.elements.length-1);
12194     },
12195
12196     /**
12197      * Returns the number of elements in this composite
12198      * @return Number
12199      */
12200     getCount : function(){
12201         return this.elements.length;
12202     },
12203
12204     /**
12205      * Returns true if this composite contains the passed element
12206      * @return Boolean
12207      */
12208     contains : function(el){
12209         return this.indexOf(el) !== -1;
12210     },
12211
12212     /**
12213      * Returns true if this composite contains the passed element
12214      * @return Boolean
12215      */
12216     indexOf : function(el){
12217         return this.elements.indexOf(Roo.get(el));
12218     },
12219
12220
12221     /**
12222     * Removes the specified element(s).
12223     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12224     * or an array of any of those.
12225     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12226     * @return {CompositeElement} this
12227     */
12228     removeElement : function(el, removeDom){
12229         if(el instanceof Array){
12230             for(var i = 0, len = el.length; i < len; i++){
12231                 this.removeElement(el[i]);
12232             }
12233             return this;
12234         }
12235         var index = typeof el == 'number' ? el : this.indexOf(el);
12236         if(index !== -1){
12237             if(removeDom){
12238                 var d = this.elements[index];
12239                 if(d.dom){
12240                     d.remove();
12241                 }else{
12242                     d.parentNode.removeChild(d);
12243                 }
12244             }
12245             this.elements.splice(index, 1);
12246         }
12247         return this;
12248     },
12249
12250     /**
12251     * Replaces the specified element with the passed element.
12252     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12253     * to replace.
12254     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12255     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12256     * @return {CompositeElement} this
12257     */
12258     replaceElement : function(el, replacement, domReplace){
12259         var index = typeof el == 'number' ? el : this.indexOf(el);
12260         if(index !== -1){
12261             if(domReplace){
12262                 this.elements[index].replaceWith(replacement);
12263             }else{
12264                 this.elements.splice(index, 1, Roo.get(replacement))
12265             }
12266         }
12267         return this;
12268     },
12269
12270     /**
12271      * Removes all elements.
12272      */
12273     clear : function(){
12274         this.elements = [];
12275     }
12276 };
12277 (function(){
12278     Roo.CompositeElement.createCall = function(proto, fnName){
12279         if(!proto[fnName]){
12280             proto[fnName] = function(){
12281                 return this.invoke(fnName, arguments);
12282             };
12283         }
12284     };
12285     for(var fnName in Roo.Element.prototype){
12286         if(typeof Roo.Element.prototype[fnName] == "function"){
12287             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12288         }
12289     };
12290 })();
12291 /*
12292  * Based on:
12293  * Ext JS Library 1.1.1
12294  * Copyright(c) 2006-2007, Ext JS, LLC.
12295  *
12296  * Originally Released Under LGPL - original licence link has changed is not relivant.
12297  *
12298  * Fork - LGPL
12299  * <script type="text/javascript">
12300  */
12301
12302 /**
12303  * @class Roo.CompositeElementLite
12304  * @extends Roo.CompositeElement
12305  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12306  <pre><code>
12307  var els = Roo.select("#some-el div.some-class");
12308  // or select directly from an existing element
12309  var el = Roo.get('some-el');
12310  el.select('div.some-class');
12311
12312  els.setWidth(100); // all elements become 100 width
12313  els.hide(true); // all elements fade out and hide
12314  // or
12315  els.setWidth(100).hide(true);
12316  </code></pre><br><br>
12317  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12318  * actions will be performed on all the elements in this collection.</b>
12319  */
12320 Roo.CompositeElementLite = function(els){
12321     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12322     this.el = new Roo.Element.Flyweight();
12323 };
12324 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12325     addElements : function(els){
12326         if(els){
12327             if(els instanceof Array){
12328                 this.elements = this.elements.concat(els);
12329             }else{
12330                 var yels = this.elements;
12331                 var index = yels.length-1;
12332                 for(var i = 0, len = els.length; i < len; i++) {
12333                     yels[++index] = els[i];
12334                 }
12335             }
12336         }
12337         return this;
12338     },
12339     invoke : function(fn, args){
12340         var els = this.elements;
12341         var el = this.el;
12342         for(var i = 0, len = els.length; i < len; i++) {
12343             el.dom = els[i];
12344                 Roo.Element.prototype[fn].apply(el, args);
12345         }
12346         return this;
12347     },
12348     /**
12349      * Returns a flyweight Element of the dom element object at the specified index
12350      * @param {Number} index
12351      * @return {Roo.Element}
12352      */
12353     item : function(index){
12354         if(!this.elements[index]){
12355             return null;
12356         }
12357         this.el.dom = this.elements[index];
12358         return this.el;
12359     },
12360
12361     // fixes scope with flyweight
12362     addListener : function(eventName, handler, scope, opt){
12363         var els = this.elements;
12364         for(var i = 0, len = els.length; i < len; i++) {
12365             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12366         }
12367         return this;
12368     },
12369
12370     /**
12371     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12372     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12373     * a reference to the dom node, use el.dom.</b>
12374     * @param {Function} fn The function to call
12375     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12376     * @return {CompositeElement} this
12377     */
12378     each : function(fn, scope){
12379         var els = this.elements;
12380         var el = this.el;
12381         for(var i = 0, len = els.length; i < len; i++){
12382             el.dom = els[i];
12383                 if(fn.call(scope || el, el, this, i) === false){
12384                 break;
12385             }
12386         }
12387         return this;
12388     },
12389
12390     indexOf : function(el){
12391         return this.elements.indexOf(Roo.getDom(el));
12392     },
12393
12394     replaceElement : function(el, replacement, domReplace){
12395         var index = typeof el == 'number' ? el : this.indexOf(el);
12396         if(index !== -1){
12397             replacement = Roo.getDom(replacement);
12398             if(domReplace){
12399                 var d = this.elements[index];
12400                 d.parentNode.insertBefore(replacement, d);
12401                 d.parentNode.removeChild(d);
12402             }
12403             this.elements.splice(index, 1, replacement);
12404         }
12405         return this;
12406     }
12407 });
12408 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12409
12410 /*
12411  * Based on:
12412  * Ext JS Library 1.1.1
12413  * Copyright(c) 2006-2007, Ext JS, LLC.
12414  *
12415  * Originally Released Under LGPL - original licence link has changed is not relivant.
12416  *
12417  * Fork - LGPL
12418  * <script type="text/javascript">
12419  */
12420
12421  
12422
12423 /**
12424  * @class Roo.data.Connection
12425  * @extends Roo.util.Observable
12426  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12427  * either to a configured URL, or to a URL specified at request time. 
12428  * 
12429  * Requests made by this class are asynchronous, and will return immediately. No data from
12430  * the server will be available to the statement immediately following the {@link #request} call.
12431  * To process returned data, use a callback in the request options object, or an event listener.
12432  * 
12433  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12434  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12435  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12436  * property and, if present, the IFRAME's XML document as the responseXML property.
12437  * 
12438  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12439  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12440  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12441  * standard DOM methods.
12442  * @constructor
12443  * @param {Object} config a configuration object.
12444  */
12445 Roo.data.Connection = function(config){
12446     Roo.apply(this, config);
12447     this.addEvents({
12448         /**
12449          * @event beforerequest
12450          * Fires before a network request is made to retrieve a data object.
12451          * @param {Connection} conn This Connection object.
12452          * @param {Object} options The options config object passed to the {@link #request} method.
12453          */
12454         "beforerequest" : true,
12455         /**
12456          * @event requestcomplete
12457          * Fires if the request was successfully completed.
12458          * @param {Connection} conn This Connection object.
12459          * @param {Object} response The XHR object containing the response data.
12460          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12461          * @param {Object} options The options config object passed to the {@link #request} method.
12462          */
12463         "requestcomplete" : true,
12464         /**
12465          * @event requestexception
12466          * Fires if an error HTTP status was returned from the server.
12467          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12468          * @param {Connection} conn This Connection object.
12469          * @param {Object} response The XHR object containing the response data.
12470          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12471          * @param {Object} options The options config object passed to the {@link #request} method.
12472          */
12473         "requestexception" : true
12474     });
12475     Roo.data.Connection.superclass.constructor.call(this);
12476 };
12477
12478 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12479     /**
12480      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12481      */
12482     /**
12483      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12484      * extra parameters to each request made by this object. (defaults to undefined)
12485      */
12486     /**
12487      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12488      *  to each request made by this object. (defaults to undefined)
12489      */
12490     /**
12491      * @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)
12492      */
12493     /**
12494      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12495      */
12496     timeout : 30000,
12497     /**
12498      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12499      * @type Boolean
12500      */
12501     autoAbort:false,
12502
12503     /**
12504      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12505      * @type Boolean
12506      */
12507     disableCaching: true,
12508
12509     /**
12510      * Sends an HTTP request to a remote server.
12511      * @param {Object} options An object which may contain the following properties:<ul>
12512      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12513      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12514      * request, a url encoded string or a function to call to get either.</li>
12515      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12516      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12517      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12518      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12519      * <li>options {Object} The parameter to the request call.</li>
12520      * <li>success {Boolean} True if the request succeeded.</li>
12521      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12522      * </ul></li>
12523      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12524      * The callback is passed the following parameters:<ul>
12525      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12526      * <li>options {Object} The parameter to the request call.</li>
12527      * </ul></li>
12528      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12529      * The callback is passed the following parameters:<ul>
12530      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12531      * <li>options {Object} The parameter to the request call.</li>
12532      * </ul></li>
12533      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12534      * for the callback function. Defaults to the browser window.</li>
12535      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12536      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12537      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12538      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12539      * params for the post data. Any params will be appended to the URL.</li>
12540      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12541      * </ul>
12542      * @return {Number} transactionId
12543      */
12544     request : function(o){
12545         if(this.fireEvent("beforerequest", this, o) !== false){
12546             var p = o.params;
12547
12548             if(typeof p == "function"){
12549                 p = p.call(o.scope||window, o);
12550             }
12551             if(typeof p == "object"){
12552                 p = Roo.urlEncode(o.params);
12553             }
12554             if(this.extraParams){
12555                 var extras = Roo.urlEncode(this.extraParams);
12556                 p = p ? (p + '&' + extras) : extras;
12557             }
12558
12559             var url = o.url || this.url;
12560             if(typeof url == 'function'){
12561                 url = url.call(o.scope||window, o);
12562             }
12563
12564             if(o.form){
12565                 var form = Roo.getDom(o.form);
12566                 url = url || form.action;
12567
12568                 var enctype = form.getAttribute("enctype");
12569                 
12570                 if (o.formData) {
12571                     return this.doFormDataUpload(o, url);
12572                 }
12573                 
12574                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12575                     return this.doFormUpload(o, p, url);
12576                 }
12577                 var f = Roo.lib.Ajax.serializeForm(form);
12578                 p = p ? (p + '&' + f) : f;
12579             }
12580             
12581             if (!o.form && o.formData) {
12582                 o.formData = o.formData === true ? new FormData() : o.formData;
12583                 for (var k in o.params) {
12584                     o.formData.append(k,o.params[k]);
12585                 }
12586                     
12587                 return this.doFormDataUpload(o, url);
12588             }
12589             
12590
12591             var hs = o.headers;
12592             if(this.defaultHeaders){
12593                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12594                 if(!o.headers){
12595                     o.headers = hs;
12596                 }
12597             }
12598
12599             var cb = {
12600                 success: this.handleResponse,
12601                 failure: this.handleFailure,
12602                 scope: this,
12603                 argument: {options: o},
12604                 timeout : o.timeout || this.timeout
12605             };
12606
12607             var method = o.method||this.method||(p ? "POST" : "GET");
12608
12609             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12610                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12611             }
12612
12613             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12614                 if(o.autoAbort){
12615                     this.abort();
12616                 }
12617             }else if(this.autoAbort !== false){
12618                 this.abort();
12619             }
12620
12621             if((method == 'GET' && p) || o.xmlData){
12622                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12623                 p = '';
12624             }
12625             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12626             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12627             Roo.lib.Ajax.useDefaultHeader == true;
12628             return this.transId;
12629         }else{
12630             Roo.callback(o.callback, o.scope, [o, null, null]);
12631             return null;
12632         }
12633     },
12634
12635     /**
12636      * Determine whether this object has a request outstanding.
12637      * @param {Number} transactionId (Optional) defaults to the last transaction
12638      * @return {Boolean} True if there is an outstanding request.
12639      */
12640     isLoading : function(transId){
12641         if(transId){
12642             return Roo.lib.Ajax.isCallInProgress(transId);
12643         }else{
12644             return this.transId ? true : false;
12645         }
12646     },
12647
12648     /**
12649      * Aborts any outstanding request.
12650      * @param {Number} transactionId (Optional) defaults to the last transaction
12651      */
12652     abort : function(transId){
12653         if(transId || this.isLoading()){
12654             Roo.lib.Ajax.abort(transId || this.transId);
12655         }
12656     },
12657
12658     // private
12659     handleResponse : function(response){
12660         this.transId = false;
12661         var options = response.argument.options;
12662         response.argument = options ? options.argument : null;
12663         this.fireEvent("requestcomplete", this, response, options);
12664         Roo.callback(options.success, options.scope, [response, options]);
12665         Roo.callback(options.callback, options.scope, [options, true, response]);
12666     },
12667
12668     // private
12669     handleFailure : function(response, e){
12670         this.transId = false;
12671         var options = response.argument.options;
12672         response.argument = options ? options.argument : null;
12673         this.fireEvent("requestexception", this, response, options, e);
12674         Roo.callback(options.failure, options.scope, [response, options]);
12675         Roo.callback(options.callback, options.scope, [options, false, response]);
12676     },
12677
12678     // private
12679     doFormUpload : function(o, ps, url){
12680         var id = Roo.id();
12681         var frame = document.createElement('iframe');
12682         frame.id = id;
12683         frame.name = id;
12684         frame.className = 'x-hidden';
12685         if(Roo.isIE){
12686             frame.src = Roo.SSL_SECURE_URL;
12687         }
12688         document.body.appendChild(frame);
12689
12690         if(Roo.isIE){
12691            document.frames[id].name = id;
12692         }
12693
12694         var form = Roo.getDom(o.form);
12695         form.target = id;
12696         form.method = 'POST';
12697         form.enctype = form.encoding = 'multipart/form-data';
12698         if(url){
12699             form.action = url;
12700         }
12701
12702         var hiddens, hd;
12703         if(ps){ // add dynamic params
12704             hiddens = [];
12705             ps = Roo.urlDecode(ps, false);
12706             for(var k in ps){
12707                 if(ps.hasOwnProperty(k)){
12708                     hd = document.createElement('input');
12709                     hd.type = 'hidden';
12710                     hd.name = k;
12711                     hd.value = ps[k];
12712                     form.appendChild(hd);
12713                     hiddens.push(hd);
12714                 }
12715             }
12716         }
12717
12718         function cb(){
12719             var r = {  // bogus response object
12720                 responseText : '',
12721                 responseXML : null
12722             };
12723
12724             r.argument = o ? o.argument : null;
12725
12726             try { //
12727                 var doc;
12728                 if(Roo.isIE){
12729                     doc = frame.contentWindow.document;
12730                 }else {
12731                     doc = (frame.contentDocument || window.frames[id].document);
12732                 }
12733                 if(doc && doc.body){
12734                     r.responseText = doc.body.innerHTML;
12735                 }
12736                 if(doc && doc.XMLDocument){
12737                     r.responseXML = doc.XMLDocument;
12738                 }else {
12739                     r.responseXML = doc;
12740                 }
12741             }
12742             catch(e) {
12743                 // ignore
12744             }
12745
12746             Roo.EventManager.removeListener(frame, 'load', cb, this);
12747
12748             this.fireEvent("requestcomplete", this, r, o);
12749             Roo.callback(o.success, o.scope, [r, o]);
12750             Roo.callback(o.callback, o.scope, [o, true, r]);
12751
12752             setTimeout(function(){document.body.removeChild(frame);}, 100);
12753         }
12754
12755         Roo.EventManager.on(frame, 'load', cb, this);
12756         form.submit();
12757
12758         if(hiddens){ // remove dynamic params
12759             for(var i = 0, len = hiddens.length; i < len; i++){
12760                 form.removeChild(hiddens[i]);
12761             }
12762         }
12763     },
12764     // this is a 'formdata version???'
12765     
12766     
12767     doFormDataUpload : function(o,  url)
12768     {
12769         var formData;
12770         if (o.form) {
12771             var form =  Roo.getDom(o.form);
12772             form.enctype = form.encoding = 'multipart/form-data';
12773             formData = o.formData === true ? new FormData(form) : o.formData;
12774         } else {
12775             formData = o.formData === true ? new FormData() : o.formData;
12776         }
12777         
12778       
12779         var cb = {
12780             success: this.handleResponse,
12781             failure: this.handleFailure,
12782             scope: this,
12783             argument: {options: o},
12784             timeout : o.timeout || this.timeout
12785         };
12786  
12787         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12788             if(o.autoAbort){
12789                 this.abort();
12790             }
12791         }else if(this.autoAbort !== false){
12792             this.abort();
12793         }
12794
12795         //Roo.lib.Ajax.defaultPostHeader = null;
12796         Roo.lib.Ajax.useDefaultHeader = false;
12797         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12798         Roo.lib.Ajax.useDefaultHeader = true;
12799  
12800          
12801     }
12802     
12803 });
12804 /*
12805  * Based on:
12806  * Ext JS Library 1.1.1
12807  * Copyright(c) 2006-2007, Ext JS, LLC.
12808  *
12809  * Originally Released Under LGPL - original licence link has changed is not relivant.
12810  *
12811  * Fork - LGPL
12812  * <script type="text/javascript">
12813  */
12814  
12815 /**
12816  * Global Ajax request class.
12817  * 
12818  * @class Roo.Ajax
12819  * @extends Roo.data.Connection
12820  * @static
12821  * 
12822  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12823  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12824  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12825  * @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)
12826  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12827  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12828  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12829  */
12830 Roo.Ajax = new Roo.data.Connection({
12831     // fix up the docs
12832     /**
12833      * @scope Roo.Ajax
12834      * @type {Boolear} 
12835      */
12836     autoAbort : false,
12837
12838     /**
12839      * Serialize the passed form into a url encoded string
12840      * @scope Roo.Ajax
12841      * @param {String/HTMLElement} form
12842      * @return {String}
12843      */
12844     serializeForm : function(form){
12845         return Roo.lib.Ajax.serializeForm(form);
12846     }
12847 });/*
12848  * Based on:
12849  * Ext JS Library 1.1.1
12850  * Copyright(c) 2006-2007, Ext JS, LLC.
12851  *
12852  * Originally Released Under LGPL - original licence link has changed is not relivant.
12853  *
12854  * Fork - LGPL
12855  * <script type="text/javascript">
12856  */
12857
12858  
12859 /**
12860  * @class Roo.UpdateManager
12861  * @extends Roo.util.Observable
12862  * Provides AJAX-style update for Element object.<br><br>
12863  * Usage:<br>
12864  * <pre><code>
12865  * // Get it from a Roo.Element object
12866  * var el = Roo.get("foo");
12867  * var mgr = el.getUpdateManager();
12868  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12869  * ...
12870  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12871  * <br>
12872  * // or directly (returns the same UpdateManager instance)
12873  * var mgr = new Roo.UpdateManager("myElementId");
12874  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12875  * mgr.on("update", myFcnNeedsToKnow);
12876  * <br>
12877    // short handed call directly from the element object
12878    Roo.get("foo").load({
12879         url: "bar.php",
12880         scripts:true,
12881         params: "for=bar",
12882         text: "Loading Foo..."
12883    });
12884  * </code></pre>
12885  * @constructor
12886  * Create new UpdateManager directly.
12887  * @param {String/HTMLElement/Roo.Element} el The element to update
12888  * @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).
12889  */
12890 Roo.UpdateManager = function(el, forceNew){
12891     el = Roo.get(el);
12892     if(!forceNew && el.updateManager){
12893         return el.updateManager;
12894     }
12895     /**
12896      * The Element object
12897      * @type Roo.Element
12898      */
12899     this.el = el;
12900     /**
12901      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12902      * @type String
12903      */
12904     this.defaultUrl = null;
12905
12906     this.addEvents({
12907         /**
12908          * @event beforeupdate
12909          * Fired before an update is made, return false from your handler and the update is cancelled.
12910          * @param {Roo.Element} el
12911          * @param {String/Object/Function} url
12912          * @param {String/Object} params
12913          */
12914         "beforeupdate": true,
12915         /**
12916          * @event update
12917          * Fired after successful update is made.
12918          * @param {Roo.Element} el
12919          * @param {Object} oResponseObject The response Object
12920          */
12921         "update": true,
12922         /**
12923          * @event failure
12924          * Fired on update failure.
12925          * @param {Roo.Element} el
12926          * @param {Object} oResponseObject The response Object
12927          */
12928         "failure": true
12929     });
12930     var d = Roo.UpdateManager.defaults;
12931     /**
12932      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12933      * @type String
12934      */
12935     this.sslBlankUrl = d.sslBlankUrl;
12936     /**
12937      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12938      * @type Boolean
12939      */
12940     this.disableCaching = d.disableCaching;
12941     /**
12942      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12943      * @type String
12944      */
12945     this.indicatorText = d.indicatorText;
12946     /**
12947      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12948      * @type String
12949      */
12950     this.showLoadIndicator = d.showLoadIndicator;
12951     /**
12952      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12953      * @type Number
12954      */
12955     this.timeout = d.timeout;
12956
12957     /**
12958      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12959      * @type Boolean
12960      */
12961     this.loadScripts = d.loadScripts;
12962
12963     /**
12964      * Transaction object of current executing transaction
12965      */
12966     this.transaction = null;
12967
12968     /**
12969      * @private
12970      */
12971     this.autoRefreshProcId = null;
12972     /**
12973      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12974      * @type Function
12975      */
12976     this.refreshDelegate = this.refresh.createDelegate(this);
12977     /**
12978      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12979      * @type Function
12980      */
12981     this.updateDelegate = this.update.createDelegate(this);
12982     /**
12983      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12984      * @type Function
12985      */
12986     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12987     /**
12988      * @private
12989      */
12990     this.successDelegate = this.processSuccess.createDelegate(this);
12991     /**
12992      * @private
12993      */
12994     this.failureDelegate = this.processFailure.createDelegate(this);
12995
12996     if(!this.renderer){
12997      /**
12998       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12999       */
13000     this.renderer = new Roo.UpdateManager.BasicRenderer();
13001     }
13002     
13003     Roo.UpdateManager.superclass.constructor.call(this);
13004 };
13005
13006 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13007     /**
13008      * Get the Element this UpdateManager is bound to
13009      * @return {Roo.Element} The element
13010      */
13011     getEl : function(){
13012         return this.el;
13013     },
13014     /**
13015      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13016      * @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:
13017 <pre><code>
13018 um.update({<br/>
13019     url: "your-url.php",<br/>
13020     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13021     callback: yourFunction,<br/>
13022     scope: yourObject, //(optional scope)  <br/>
13023     discardUrl: false, <br/>
13024     nocache: false,<br/>
13025     text: "Loading...",<br/>
13026     timeout: 30,<br/>
13027     scripts: false<br/>
13028 });
13029 </code></pre>
13030      * The only required property is url. The optional properties nocache, text and scripts
13031      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13032      * @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}
13033      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13034      * @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.
13035      */
13036     update : function(url, params, callback, discardUrl){
13037         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13038             var method = this.method,
13039                 cfg;
13040             if(typeof url == "object"){ // must be config object
13041                 cfg = url;
13042                 url = cfg.url;
13043                 params = params || cfg.params;
13044                 callback = callback || cfg.callback;
13045                 discardUrl = discardUrl || cfg.discardUrl;
13046                 if(callback && cfg.scope){
13047                     callback = callback.createDelegate(cfg.scope);
13048                 }
13049                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13050                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13051                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13052                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13053                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13054             }
13055             this.showLoading();
13056             if(!discardUrl){
13057                 this.defaultUrl = url;
13058             }
13059             if(typeof url == "function"){
13060                 url = url.call(this);
13061             }
13062
13063             method = method || (params ? "POST" : "GET");
13064             if(method == "GET"){
13065                 url = this.prepareUrl(url);
13066             }
13067
13068             var o = Roo.apply(cfg ||{}, {
13069                 url : url,
13070                 params: params,
13071                 success: this.successDelegate,
13072                 failure: this.failureDelegate,
13073                 callback: undefined,
13074                 timeout: (this.timeout*1000),
13075                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13076             });
13077             Roo.log("updated manager called with timeout of " + o.timeout);
13078             this.transaction = Roo.Ajax.request(o);
13079         }
13080     },
13081
13082     /**
13083      * 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.
13084      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13085      * @param {String/HTMLElement} form The form Id or form element
13086      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13087      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13088      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13089      */
13090     formUpdate : function(form, url, reset, callback){
13091         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13092             if(typeof url == "function"){
13093                 url = url.call(this);
13094             }
13095             form = Roo.getDom(form);
13096             this.transaction = Roo.Ajax.request({
13097                 form: form,
13098                 url:url,
13099                 success: this.successDelegate,
13100                 failure: this.failureDelegate,
13101                 timeout: (this.timeout*1000),
13102                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13103             });
13104             this.showLoading.defer(1, this);
13105         }
13106     },
13107
13108     /**
13109      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13110      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13111      */
13112     refresh : function(callback){
13113         if(this.defaultUrl == null){
13114             return;
13115         }
13116         this.update(this.defaultUrl, null, callback, true);
13117     },
13118
13119     /**
13120      * Set this element to auto refresh.
13121      * @param {Number} interval How often to update (in seconds).
13122      * @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)
13123      * @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}
13124      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13125      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13126      */
13127     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13128         if(refreshNow){
13129             this.update(url || this.defaultUrl, params, callback, true);
13130         }
13131         if(this.autoRefreshProcId){
13132             clearInterval(this.autoRefreshProcId);
13133         }
13134         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13135     },
13136
13137     /**
13138      * Stop auto refresh on this element.
13139      */
13140      stopAutoRefresh : function(){
13141         if(this.autoRefreshProcId){
13142             clearInterval(this.autoRefreshProcId);
13143             delete this.autoRefreshProcId;
13144         }
13145     },
13146
13147     isAutoRefreshing : function(){
13148        return this.autoRefreshProcId ? true : false;
13149     },
13150     /**
13151      * Called to update the element to "Loading" state. Override to perform custom action.
13152      */
13153     showLoading : function(){
13154         if(this.showLoadIndicator){
13155             this.el.update(this.indicatorText);
13156         }
13157     },
13158
13159     /**
13160      * Adds unique parameter to query string if disableCaching = true
13161      * @private
13162      */
13163     prepareUrl : function(url){
13164         if(this.disableCaching){
13165             var append = "_dc=" + (new Date().getTime());
13166             if(url.indexOf("?") !== -1){
13167                 url += "&" + append;
13168             }else{
13169                 url += "?" + append;
13170             }
13171         }
13172         return url;
13173     },
13174
13175     /**
13176      * @private
13177      */
13178     processSuccess : function(response){
13179         this.transaction = null;
13180         if(response.argument.form && response.argument.reset){
13181             try{ // put in try/catch since some older FF releases had problems with this
13182                 response.argument.form.reset();
13183             }catch(e){}
13184         }
13185         if(this.loadScripts){
13186             this.renderer.render(this.el, response, this,
13187                 this.updateComplete.createDelegate(this, [response]));
13188         }else{
13189             this.renderer.render(this.el, response, this);
13190             this.updateComplete(response);
13191         }
13192     },
13193
13194     updateComplete : function(response){
13195         this.fireEvent("update", this.el, response);
13196         if(typeof response.argument.callback == "function"){
13197             response.argument.callback(this.el, true, response);
13198         }
13199     },
13200
13201     /**
13202      * @private
13203      */
13204     processFailure : function(response){
13205         this.transaction = null;
13206         this.fireEvent("failure", this.el, response);
13207         if(typeof response.argument.callback == "function"){
13208             response.argument.callback(this.el, false, response);
13209         }
13210     },
13211
13212     /**
13213      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13214      * @param {Object} renderer The object implementing the render() method
13215      */
13216     setRenderer : function(renderer){
13217         this.renderer = renderer;
13218     },
13219
13220     getRenderer : function(){
13221        return this.renderer;
13222     },
13223
13224     /**
13225      * Set the defaultUrl used for updates
13226      * @param {String/Function} defaultUrl The url or a function to call to get the url
13227      */
13228     setDefaultUrl : function(defaultUrl){
13229         this.defaultUrl = defaultUrl;
13230     },
13231
13232     /**
13233      * Aborts the executing transaction
13234      */
13235     abort : function(){
13236         if(this.transaction){
13237             Roo.Ajax.abort(this.transaction);
13238         }
13239     },
13240
13241     /**
13242      * Returns true if an update is in progress
13243      * @return {Boolean}
13244      */
13245     isUpdating : function(){
13246         if(this.transaction){
13247             return Roo.Ajax.isLoading(this.transaction);
13248         }
13249         return false;
13250     }
13251 });
13252
13253 /**
13254  * @class Roo.UpdateManager.defaults
13255  * @static (not really - but it helps the doc tool)
13256  * The defaults collection enables customizing the default properties of UpdateManager
13257  */
13258    Roo.UpdateManager.defaults = {
13259        /**
13260          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13261          * @type Number
13262          */
13263          timeout : 30,
13264
13265          /**
13266          * True to process scripts by default (Defaults to false).
13267          * @type Boolean
13268          */
13269         loadScripts : false,
13270
13271         /**
13272         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13273         * @type String
13274         */
13275         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13276         /**
13277          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13278          * @type Boolean
13279          */
13280         disableCaching : false,
13281         /**
13282          * Whether to show indicatorText when loading (Defaults to true).
13283          * @type Boolean
13284          */
13285         showLoadIndicator : true,
13286         /**
13287          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13288          * @type String
13289          */
13290         indicatorText : '<div class="loading-indicator">Loading...</div>'
13291    };
13292
13293 /**
13294  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13295  *Usage:
13296  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13297  * @param {String/HTMLElement/Roo.Element} el The element to update
13298  * @param {String} url The url
13299  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13300  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13301  * @static
13302  * @deprecated
13303  * @member Roo.UpdateManager
13304  */
13305 Roo.UpdateManager.updateElement = function(el, url, params, options){
13306     var um = Roo.get(el, true).getUpdateManager();
13307     Roo.apply(um, options);
13308     um.update(url, params, options ? options.callback : null);
13309 };
13310 // alias for backwards compat
13311 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13312 /**
13313  * @class Roo.UpdateManager.BasicRenderer
13314  * Default Content renderer. Updates the elements innerHTML with the responseText.
13315  */
13316 Roo.UpdateManager.BasicRenderer = function(){};
13317
13318 Roo.UpdateManager.BasicRenderer.prototype = {
13319     /**
13320      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13321      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13322      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13323      * @param {Roo.Element} el The element being rendered
13324      * @param {Object} response The YUI Connect response object
13325      * @param {UpdateManager} updateManager The calling update manager
13326      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13327      */
13328      render : function(el, response, updateManager, callback){
13329         el.update(response.responseText, updateManager.loadScripts, callback);
13330     }
13331 };
13332 /*
13333  * Based on:
13334  * Roo JS
13335  * (c)) Alan Knowles
13336  * Licence : LGPL
13337  */
13338
13339
13340 /**
13341  * @class Roo.DomTemplate
13342  * @extends Roo.Template
13343  * An effort at a dom based template engine..
13344  *
13345  * Similar to XTemplate, except it uses dom parsing to create the template..
13346  *
13347  * Supported features:
13348  *
13349  *  Tags:
13350
13351 <pre><code>
13352       {a_variable} - output encoded.
13353       {a_variable.format:("Y-m-d")} - call a method on the variable
13354       {a_variable:raw} - unencoded output
13355       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13356       {a_variable:this.method_on_template(...)} - call a method on the template object.
13357  
13358 </code></pre>
13359  *  The tpl tag:
13360 <pre><code>
13361         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13362         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13363         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13364         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13365   
13366 </code></pre>
13367  *      
13368  */
13369 Roo.DomTemplate = function()
13370 {
13371      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13372      if (this.html) {
13373         this.compile();
13374      }
13375 };
13376
13377
13378 Roo.extend(Roo.DomTemplate, Roo.Template, {
13379     /**
13380      * id counter for sub templates.
13381      */
13382     id : 0,
13383     /**
13384      * flag to indicate if dom parser is inside a pre,
13385      * it will strip whitespace if not.
13386      */
13387     inPre : false,
13388     
13389     /**
13390      * The various sub templates
13391      */
13392     tpls : false,
13393     
13394     
13395     
13396     /**
13397      *
13398      * basic tag replacing syntax
13399      * WORD:WORD()
13400      *
13401      * // you can fake an object call by doing this
13402      *  x.t:(test,tesT) 
13403      * 
13404      */
13405     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13406     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13407     
13408     iterChild : function (node, method) {
13409         
13410         var oldPre = this.inPre;
13411         if (node.tagName == 'PRE') {
13412             this.inPre = true;
13413         }
13414         for( var i = 0; i < node.childNodes.length; i++) {
13415             method.call(this, node.childNodes[i]);
13416         }
13417         this.inPre = oldPre;
13418     },
13419     
13420     
13421     
13422     /**
13423      * compile the template
13424      *
13425      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13426      *
13427      */
13428     compile: function()
13429     {
13430         var s = this.html;
13431         
13432         // covert the html into DOM...
13433         var doc = false;
13434         var div =false;
13435         try {
13436             doc = document.implementation.createHTMLDocument("");
13437             doc.documentElement.innerHTML =   this.html  ;
13438             div = doc.documentElement;
13439         } catch (e) {
13440             // old IE... - nasty -- it causes all sorts of issues.. with
13441             // images getting pulled from server..
13442             div = document.createElement('div');
13443             div.innerHTML = this.html;
13444         }
13445         //doc.documentElement.innerHTML = htmlBody
13446          
13447         
13448         
13449         this.tpls = [];
13450         var _t = this;
13451         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13452         
13453         var tpls = this.tpls;
13454         
13455         // create a top level template from the snippet..
13456         
13457         //Roo.log(div.innerHTML);
13458         
13459         var tpl = {
13460             uid : 'master',
13461             id : this.id++,
13462             attr : false,
13463             value : false,
13464             body : div.innerHTML,
13465             
13466             forCall : false,
13467             execCall : false,
13468             dom : div,
13469             isTop : true
13470             
13471         };
13472         tpls.unshift(tpl);
13473         
13474         
13475         // compile them...
13476         this.tpls = [];
13477         Roo.each(tpls, function(tp){
13478             this.compileTpl(tp);
13479             this.tpls[tp.id] = tp;
13480         }, this);
13481         
13482         this.master = tpls[0];
13483         return this;
13484         
13485         
13486     },
13487     
13488     compileNode : function(node, istop) {
13489         // test for
13490         //Roo.log(node);
13491         
13492         
13493         // skip anything not a tag..
13494         if (node.nodeType != 1) {
13495             if (node.nodeType == 3 && !this.inPre) {
13496                 // reduce white space..
13497                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13498                 
13499             }
13500             return;
13501         }
13502         
13503         var tpl = {
13504             uid : false,
13505             id : false,
13506             attr : false,
13507             value : false,
13508             body : '',
13509             
13510             forCall : false,
13511             execCall : false,
13512             dom : false,
13513             isTop : istop
13514             
13515             
13516         };
13517         
13518         
13519         switch(true) {
13520             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13521             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13522             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13523             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13524             // no default..
13525         }
13526         
13527         
13528         if (!tpl.attr) {
13529             // just itterate children..
13530             this.iterChild(node,this.compileNode);
13531             return;
13532         }
13533         tpl.uid = this.id++;
13534         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13535         node.removeAttribute('roo-'+ tpl.attr);
13536         if (tpl.attr != 'name') {
13537             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13538             node.parentNode.replaceChild(placeholder,  node);
13539         } else {
13540             
13541             var placeholder =  document.createElement('span');
13542             placeholder.className = 'roo-tpl-' + tpl.value;
13543             node.parentNode.replaceChild(placeholder,  node);
13544         }
13545         
13546         // parent now sees '{domtplXXXX}
13547         this.iterChild(node,this.compileNode);
13548         
13549         // we should now have node body...
13550         var div = document.createElement('div');
13551         div.appendChild(node);
13552         tpl.dom = node;
13553         // this has the unfortunate side effect of converting tagged attributes
13554         // eg. href="{...}" into %7C...%7D
13555         // this has been fixed by searching for those combo's although it's a bit hacky..
13556         
13557         
13558         tpl.body = div.innerHTML;
13559         
13560         
13561          
13562         tpl.id = tpl.uid;
13563         switch(tpl.attr) {
13564             case 'for' :
13565                 switch (tpl.value) {
13566                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13567                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13568                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13569                 }
13570                 break;
13571             
13572             case 'exec':
13573                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13574                 break;
13575             
13576             case 'if':     
13577                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13578                 break;
13579             
13580             case 'name':
13581                 tpl.id  = tpl.value; // replace non characters???
13582                 break;
13583             
13584         }
13585         
13586         
13587         this.tpls.push(tpl);
13588         
13589         
13590         
13591     },
13592     
13593     
13594     
13595     
13596     /**
13597      * Compile a segment of the template into a 'sub-template'
13598      *
13599      * 
13600      * 
13601      *
13602      */
13603     compileTpl : function(tpl)
13604     {
13605         var fm = Roo.util.Format;
13606         var useF = this.disableFormats !== true;
13607         
13608         var sep = Roo.isGecko ? "+\n" : ",\n";
13609         
13610         var undef = function(str) {
13611             Roo.debug && Roo.log("Property not found :"  + str);
13612             return '';
13613         };
13614           
13615         //Roo.log(tpl.body);
13616         
13617         
13618         
13619         var fn = function(m, lbrace, name, format, args)
13620         {
13621             //Roo.log("ARGS");
13622             //Roo.log(arguments);
13623             args = args ? args.replace(/\\'/g,"'") : args;
13624             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13625             if (typeof(format) == 'undefined') {
13626                 format =  'htmlEncode'; 
13627             }
13628             if (format == 'raw' ) {
13629                 format = false;
13630             }
13631             
13632             if(name.substr(0, 6) == 'domtpl'){
13633                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13634             }
13635             
13636             // build an array of options to determine if value is undefined..
13637             
13638             // basically get 'xxxx.yyyy' then do
13639             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13640             //    (function () { Roo.log("Property not found"); return ''; })() :
13641             //    ......
13642             
13643             var udef_ar = [];
13644             var lookfor = '';
13645             Roo.each(name.split('.'), function(st) {
13646                 lookfor += (lookfor.length ? '.': '') + st;
13647                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13648             });
13649             
13650             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13651             
13652             
13653             if(format && useF){
13654                 
13655                 args = args ? ',' + args : "";
13656                  
13657                 if(format.substr(0, 5) != "this."){
13658                     format = "fm." + format + '(';
13659                 }else{
13660                     format = 'this.call("'+ format.substr(5) + '", ';
13661                     args = ", values";
13662                 }
13663                 
13664                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13665             }
13666              
13667             if (args && args.length) {
13668                 // called with xxyx.yuu:(test,test)
13669                 // change to ()
13670                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13671             }
13672             // raw.. - :raw modifier..
13673             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13674             
13675         };
13676         var body;
13677         // branched to use + in gecko and [].join() in others
13678         if(Roo.isGecko){
13679             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13680                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13681                     "';};};";
13682         }else{
13683             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13684             body.push(tpl.body.replace(/(\r\n|\n)/g,
13685                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13686             body.push("'].join('');};};");
13687             body = body.join('');
13688         }
13689         
13690         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13691        
13692         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13693         eval(body);
13694         
13695         return this;
13696     },
13697      
13698     /**
13699      * same as applyTemplate, except it's done to one of the subTemplates
13700      * when using named templates, you can do:
13701      *
13702      * var str = pl.applySubTemplate('your-name', values);
13703      *
13704      * 
13705      * @param {Number} id of the template
13706      * @param {Object} values to apply to template
13707      * @param {Object} parent (normaly the instance of this object)
13708      */
13709     applySubTemplate : function(id, values, parent)
13710     {
13711         
13712         
13713         var t = this.tpls[id];
13714         
13715         
13716         try { 
13717             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13718                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13719                 return '';
13720             }
13721         } catch(e) {
13722             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13723             Roo.log(values);
13724           
13725             return '';
13726         }
13727         try { 
13728             
13729             if(t.execCall && t.execCall.call(this, values, parent)){
13730                 return '';
13731             }
13732         } catch(e) {
13733             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13734             Roo.log(values);
13735             return '';
13736         }
13737         
13738         try {
13739             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13740             parent = t.target ? values : parent;
13741             if(t.forCall && vs instanceof Array){
13742                 var buf = [];
13743                 for(var i = 0, len = vs.length; i < len; i++){
13744                     try {
13745                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13746                     } catch (e) {
13747                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13748                         Roo.log(e.body);
13749                         //Roo.log(t.compiled);
13750                         Roo.log(vs[i]);
13751                     }   
13752                 }
13753                 return buf.join('');
13754             }
13755         } catch (e) {
13756             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13757             Roo.log(values);
13758             return '';
13759         }
13760         try {
13761             return t.compiled.call(this, vs, parent);
13762         } catch (e) {
13763             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13764             Roo.log(e.body);
13765             //Roo.log(t.compiled);
13766             Roo.log(values);
13767             return '';
13768         }
13769     },
13770
13771    
13772
13773     applyTemplate : function(values){
13774         return this.master.compiled.call(this, values, {});
13775         //var s = this.subs;
13776     },
13777
13778     apply : function(){
13779         return this.applyTemplate.apply(this, arguments);
13780     }
13781
13782  });
13783
13784 Roo.DomTemplate.from = function(el){
13785     el = Roo.getDom(el);
13786     return new Roo.Domtemplate(el.value || el.innerHTML);
13787 };/*
13788  * Based on:
13789  * Ext JS Library 1.1.1
13790  * Copyright(c) 2006-2007, Ext JS, LLC.
13791  *
13792  * Originally Released Under LGPL - original licence link has changed is not relivant.
13793  *
13794  * Fork - LGPL
13795  * <script type="text/javascript">
13796  */
13797
13798 /**
13799  * @class Roo.util.DelayedTask
13800  * Provides a convenient method of performing setTimeout where a new
13801  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13802  * You can use this class to buffer
13803  * the keypress events for a certain number of milliseconds, and perform only if they stop
13804  * for that amount of time.
13805  * @constructor The parameters to this constructor serve as defaults and are not required.
13806  * @param {Function} fn (optional) The default function to timeout
13807  * @param {Object} scope (optional) The default scope of that timeout
13808  * @param {Array} args (optional) The default Array of arguments
13809  */
13810 Roo.util.DelayedTask = function(fn, scope, args){
13811     var id = null, d, t;
13812
13813     var call = function(){
13814         var now = new Date().getTime();
13815         if(now - t >= d){
13816             clearInterval(id);
13817             id = null;
13818             fn.apply(scope, args || []);
13819         }
13820     };
13821     /**
13822      * Cancels any pending timeout and queues a new one
13823      * @param {Number} delay The milliseconds to delay
13824      * @param {Function} newFn (optional) Overrides function passed to constructor
13825      * @param {Object} newScope (optional) Overrides scope passed to constructor
13826      * @param {Array} newArgs (optional) Overrides args passed to constructor
13827      */
13828     this.delay = function(delay, newFn, newScope, newArgs){
13829         if(id && delay != d){
13830             this.cancel();
13831         }
13832         d = delay;
13833         t = new Date().getTime();
13834         fn = newFn || fn;
13835         scope = newScope || scope;
13836         args = newArgs || args;
13837         if(!id){
13838             id = setInterval(call, d);
13839         }
13840     };
13841
13842     /**
13843      * Cancel the last queued timeout
13844      */
13845     this.cancel = function(){
13846         if(id){
13847             clearInterval(id);
13848             id = null;
13849         }
13850     };
13851 };/*
13852  * Based on:
13853  * Ext JS Library 1.1.1
13854  * Copyright(c) 2006-2007, Ext JS, LLC.
13855  *
13856  * Originally Released Under LGPL - original licence link has changed is not relivant.
13857  *
13858  * Fork - LGPL
13859  * <script type="text/javascript">
13860  */
13861 /**
13862  * @class Roo.util.TaskRunner
13863  * Manage background tasks - not sure why this is better that setInterval?
13864  * @static
13865  *
13866  */
13867  
13868 Roo.util.TaskRunner = function(interval){
13869     interval = interval || 10;
13870     var tasks = [], removeQueue = [];
13871     var id = 0;
13872     var running = false;
13873
13874     var stopThread = function(){
13875         running = false;
13876         clearInterval(id);
13877         id = 0;
13878     };
13879
13880     var startThread = function(){
13881         if(!running){
13882             running = true;
13883             id = setInterval(runTasks, interval);
13884         }
13885     };
13886
13887     var removeTask = function(task){
13888         removeQueue.push(task);
13889         if(task.onStop){
13890             task.onStop();
13891         }
13892     };
13893
13894     var runTasks = function(){
13895         if(removeQueue.length > 0){
13896             for(var i = 0, len = removeQueue.length; i < len; i++){
13897                 tasks.remove(removeQueue[i]);
13898             }
13899             removeQueue = [];
13900             if(tasks.length < 1){
13901                 stopThread();
13902                 return;
13903             }
13904         }
13905         var now = new Date().getTime();
13906         for(var i = 0, len = tasks.length; i < len; ++i){
13907             var t = tasks[i];
13908             var itime = now - t.taskRunTime;
13909             if(t.interval <= itime){
13910                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13911                 t.taskRunTime = now;
13912                 if(rt === false || t.taskRunCount === t.repeat){
13913                     removeTask(t);
13914                     return;
13915                 }
13916             }
13917             if(t.duration && t.duration <= (now - t.taskStartTime)){
13918                 removeTask(t);
13919             }
13920         }
13921     };
13922
13923     /**
13924      * Queues a new task.
13925      * @param {Object} task
13926      *
13927      * Task property : interval = how frequent to run.
13928      * Task object should implement
13929      * function run()
13930      * Task object may implement
13931      * function onStop()
13932      */
13933     this.start = function(task){
13934         tasks.push(task);
13935         task.taskStartTime = new Date().getTime();
13936         task.taskRunTime = 0;
13937         task.taskRunCount = 0;
13938         startThread();
13939         return task;
13940     };
13941     /**
13942      * Stop  new task.
13943      * @param {Object} task
13944      */
13945     this.stop = function(task){
13946         removeTask(task);
13947         return task;
13948     };
13949     /**
13950      * Stop all Tasks
13951      */
13952     this.stopAll = function(){
13953         stopThread();
13954         for(var i = 0, len = tasks.length; i < len; i++){
13955             if(tasks[i].onStop){
13956                 tasks[i].onStop();
13957             }
13958         }
13959         tasks = [];
13960         removeQueue = [];
13961     };
13962 };
13963
13964 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13965  * Based on:
13966  * Ext JS Library 1.1.1
13967  * Copyright(c) 2006-2007, Ext JS, LLC.
13968  *
13969  * Originally Released Under LGPL - original licence link has changed is not relivant.
13970  *
13971  * Fork - LGPL
13972  * <script type="text/javascript">
13973  */
13974
13975  
13976 /**
13977  * @class Roo.util.MixedCollection
13978  * @extends Roo.util.Observable
13979  * A Collection class that maintains both numeric indexes and keys and exposes events.
13980  * @constructor
13981  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13982  * collection (defaults to false)
13983  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13984  * and return the key value for that item.  This is used when available to look up the key on items that
13985  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13986  * equivalent to providing an implementation for the {@link #getKey} method.
13987  */
13988 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13989     this.items = [];
13990     this.map = {};
13991     this.keys = [];
13992     this.length = 0;
13993     this.addEvents({
13994         /**
13995          * @event clear
13996          * Fires when the collection is cleared.
13997          */
13998         "clear" : true,
13999         /**
14000          * @event add
14001          * Fires when an item is added to the collection.
14002          * @param {Number} index The index at which the item was added.
14003          * @param {Object} o The item added.
14004          * @param {String} key The key associated with the added item.
14005          */
14006         "add" : true,
14007         /**
14008          * @event replace
14009          * Fires when an item is replaced in the collection.
14010          * @param {String} key he key associated with the new added.
14011          * @param {Object} old The item being replaced.
14012          * @param {Object} new The new item.
14013          */
14014         "replace" : true,
14015         /**
14016          * @event remove
14017          * Fires when an item is removed from the collection.
14018          * @param {Object} o The item being removed.
14019          * @param {String} key (optional) The key associated with the removed item.
14020          */
14021         "remove" : true,
14022         "sort" : true
14023     });
14024     this.allowFunctions = allowFunctions === true;
14025     if(keyFn){
14026         this.getKey = keyFn;
14027     }
14028     Roo.util.MixedCollection.superclass.constructor.call(this);
14029 };
14030
14031 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14032     allowFunctions : false,
14033     
14034 /**
14035  * Adds an item to the collection.
14036  * @param {String} key The key to associate with the item
14037  * @param {Object} o The item to add.
14038  * @return {Object} The item added.
14039  */
14040     add : function(key, o){
14041         if(arguments.length == 1){
14042             o = arguments[0];
14043             key = this.getKey(o);
14044         }
14045         if(typeof key == "undefined" || key === null){
14046             this.length++;
14047             this.items.push(o);
14048             this.keys.push(null);
14049         }else{
14050             var old = this.map[key];
14051             if(old){
14052                 return this.replace(key, o);
14053             }
14054             this.length++;
14055             this.items.push(o);
14056             this.map[key] = o;
14057             this.keys.push(key);
14058         }
14059         this.fireEvent("add", this.length-1, o, key);
14060         return o;
14061     },
14062        
14063 /**
14064   * MixedCollection has a generic way to fetch keys if you implement getKey.
14065 <pre><code>
14066 // normal way
14067 var mc = new Roo.util.MixedCollection();
14068 mc.add(someEl.dom.id, someEl);
14069 mc.add(otherEl.dom.id, otherEl);
14070 //and so on
14071
14072 // using getKey
14073 var mc = new Roo.util.MixedCollection();
14074 mc.getKey = function(el){
14075    return el.dom.id;
14076 };
14077 mc.add(someEl);
14078 mc.add(otherEl);
14079
14080 // or via the constructor
14081 var mc = new Roo.util.MixedCollection(false, function(el){
14082    return el.dom.id;
14083 });
14084 mc.add(someEl);
14085 mc.add(otherEl);
14086 </code></pre>
14087  * @param o {Object} The item for which to find the key.
14088  * @return {Object} The key for the passed item.
14089  */
14090     getKey : function(o){
14091          return o.id; 
14092     },
14093    
14094 /**
14095  * Replaces an item in the collection.
14096  * @param {String} key The key associated with the item to replace, or the item to replace.
14097  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14098  * @return {Object}  The new item.
14099  */
14100     replace : function(key, o){
14101         if(arguments.length == 1){
14102             o = arguments[0];
14103             key = this.getKey(o);
14104         }
14105         var old = this.item(key);
14106         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14107              return this.add(key, o);
14108         }
14109         var index = this.indexOfKey(key);
14110         this.items[index] = o;
14111         this.map[key] = o;
14112         this.fireEvent("replace", key, old, o);
14113         return o;
14114     },
14115    
14116 /**
14117  * Adds all elements of an Array or an Object to the collection.
14118  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14119  * an Array of values, each of which are added to the collection.
14120  */
14121     addAll : function(objs){
14122         if(arguments.length > 1 || objs instanceof Array){
14123             var args = arguments.length > 1 ? arguments : objs;
14124             for(var i = 0, len = args.length; i < len; i++){
14125                 this.add(args[i]);
14126             }
14127         }else{
14128             for(var key in objs){
14129                 if(this.allowFunctions || typeof objs[key] != "function"){
14130                     this.add(key, objs[key]);
14131                 }
14132             }
14133         }
14134     },
14135    
14136 /**
14137  * Executes the specified function once for every item in the collection, passing each
14138  * item as the first and only parameter. returning false from the function will stop the iteration.
14139  * @param {Function} fn The function to execute for each item.
14140  * @param {Object} scope (optional) The scope in which to execute the function.
14141  */
14142     each : function(fn, scope){
14143         var items = [].concat(this.items); // each safe for removal
14144         for(var i = 0, len = items.length; i < len; i++){
14145             if(fn.call(scope || items[i], items[i], i, len) === false){
14146                 break;
14147             }
14148         }
14149     },
14150    
14151 /**
14152  * Executes the specified function once for every key in the collection, passing each
14153  * key, and its associated item as the first two parameters.
14154  * @param {Function} fn The function to execute for each item.
14155  * @param {Object} scope (optional) The scope in which to execute the function.
14156  */
14157     eachKey : function(fn, scope){
14158         for(var i = 0, len = this.keys.length; i < len; i++){
14159             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14160         }
14161     },
14162    
14163 /**
14164  * Returns the first item in the collection which elicits a true return value from the
14165  * passed selection function.
14166  * @param {Function} fn The selection function to execute for each item.
14167  * @param {Object} scope (optional) The scope in which to execute the function.
14168  * @return {Object} The first item in the collection which returned true from the selection function.
14169  */
14170     find : function(fn, scope){
14171         for(var i = 0, len = this.items.length; i < len; i++){
14172             if(fn.call(scope || window, this.items[i], this.keys[i])){
14173                 return this.items[i];
14174             }
14175         }
14176         return null;
14177     },
14178    
14179 /**
14180  * Inserts an item at the specified index in the collection.
14181  * @param {Number} index The index to insert the item at.
14182  * @param {String} key The key to associate with the new item, or the item itself.
14183  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14184  * @return {Object} The item inserted.
14185  */
14186     insert : function(index, key, o){
14187         if(arguments.length == 2){
14188             o = arguments[1];
14189             key = this.getKey(o);
14190         }
14191         if(index >= this.length){
14192             return this.add(key, o);
14193         }
14194         this.length++;
14195         this.items.splice(index, 0, o);
14196         if(typeof key != "undefined" && key != null){
14197             this.map[key] = o;
14198         }
14199         this.keys.splice(index, 0, key);
14200         this.fireEvent("add", index, o, key);
14201         return o;
14202     },
14203    
14204 /**
14205  * Removed an item from the collection.
14206  * @param {Object} o The item to remove.
14207  * @return {Object} The item removed.
14208  */
14209     remove : function(o){
14210         return this.removeAt(this.indexOf(o));
14211     },
14212    
14213 /**
14214  * Remove an item from a specified index in the collection.
14215  * @param {Number} index The index within the collection of the item to remove.
14216  */
14217     removeAt : function(index){
14218         if(index < this.length && index >= 0){
14219             this.length--;
14220             var o = this.items[index];
14221             this.items.splice(index, 1);
14222             var key = this.keys[index];
14223             if(typeof key != "undefined"){
14224                 delete this.map[key];
14225             }
14226             this.keys.splice(index, 1);
14227             this.fireEvent("remove", o, key);
14228         }
14229     },
14230    
14231 /**
14232  * Removed an item associated with the passed key fom the collection.
14233  * @param {String} key The key of the item to remove.
14234  */
14235     removeKey : function(key){
14236         return this.removeAt(this.indexOfKey(key));
14237     },
14238    
14239 /**
14240  * Returns the number of items in the collection.
14241  * @return {Number} the number of items in the collection.
14242  */
14243     getCount : function(){
14244         return this.length; 
14245     },
14246    
14247 /**
14248  * Returns index within the collection of the passed Object.
14249  * @param {Object} o The item to find the index of.
14250  * @return {Number} index of the item.
14251  */
14252     indexOf : function(o){
14253         if(!this.items.indexOf){
14254             for(var i = 0, len = this.items.length; i < len; i++){
14255                 if(this.items[i] == o) {
14256                     return i;
14257                 }
14258             }
14259             return -1;
14260         }else{
14261             return this.items.indexOf(o);
14262         }
14263     },
14264    
14265 /**
14266  * Returns index within the collection of the passed key.
14267  * @param {String} key The key to find the index of.
14268  * @return {Number} index of the key.
14269  */
14270     indexOfKey : function(key){
14271         if(!this.keys.indexOf){
14272             for(var i = 0, len = this.keys.length; i < len; i++){
14273                 if(this.keys[i] == key) {
14274                     return i;
14275                 }
14276             }
14277             return -1;
14278         }else{
14279             return this.keys.indexOf(key);
14280         }
14281     },
14282    
14283 /**
14284  * Returns the item associated with the passed key OR index. Key has priority over index.
14285  * @param {String/Number} key The key or index of the item.
14286  * @return {Object} The item associated with the passed key.
14287  */
14288     item : function(key){
14289         if (key === 'length') {
14290             return null;
14291         }
14292         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14293         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14294     },
14295     
14296 /**
14297  * Returns the item at the specified index.
14298  * @param {Number} index The index of the item.
14299  * @return {Object}
14300  */
14301     itemAt : function(index){
14302         return this.items[index];
14303     },
14304     
14305 /**
14306  * Returns the item associated with the passed key.
14307  * @param {String/Number} key The key of the item.
14308  * @return {Object} The item associated with the passed key.
14309  */
14310     key : function(key){
14311         return this.map[key];
14312     },
14313    
14314 /**
14315  * Returns true if the collection contains the passed Object as an item.
14316  * @param {Object} o  The Object to look for in the collection.
14317  * @return {Boolean} True if the collection contains the Object as an item.
14318  */
14319     contains : function(o){
14320         return this.indexOf(o) != -1;
14321     },
14322    
14323 /**
14324  * Returns true if the collection contains the passed Object as a key.
14325  * @param {String} key The key to look for in the collection.
14326  * @return {Boolean} True if the collection contains the Object as a key.
14327  */
14328     containsKey : function(key){
14329         return typeof this.map[key] != "undefined";
14330     },
14331    
14332 /**
14333  * Removes all items from the collection.
14334  */
14335     clear : function(){
14336         this.length = 0;
14337         this.items = [];
14338         this.keys = [];
14339         this.map = {};
14340         this.fireEvent("clear");
14341     },
14342    
14343 /**
14344  * Returns the first item in the collection.
14345  * @return {Object} the first item in the collection..
14346  */
14347     first : function(){
14348         return this.items[0]; 
14349     },
14350    
14351 /**
14352  * Returns the last item in the collection.
14353  * @return {Object} the last item in the collection..
14354  */
14355     last : function(){
14356         return this.items[this.length-1];   
14357     },
14358     
14359     _sort : function(property, dir, fn){
14360         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14361         fn = fn || function(a, b){
14362             return a-b;
14363         };
14364         var c = [], k = this.keys, items = this.items;
14365         for(var i = 0, len = items.length; i < len; i++){
14366             c[c.length] = {key: k[i], value: items[i], index: i};
14367         }
14368         c.sort(function(a, b){
14369             var v = fn(a[property], b[property]) * dsc;
14370             if(v == 0){
14371                 v = (a.index < b.index ? -1 : 1);
14372             }
14373             return v;
14374         });
14375         for(var i = 0, len = c.length; i < len; i++){
14376             items[i] = c[i].value;
14377             k[i] = c[i].key;
14378         }
14379         this.fireEvent("sort", this);
14380     },
14381     
14382     /**
14383      * Sorts this collection with the passed comparison function
14384      * @param {String} direction (optional) "ASC" or "DESC"
14385      * @param {Function} fn (optional) comparison function
14386      */
14387     sort : function(dir, fn){
14388         this._sort("value", dir, fn);
14389     },
14390     
14391     /**
14392      * Sorts this collection by keys
14393      * @param {String} direction (optional) "ASC" or "DESC"
14394      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14395      */
14396     keySort : function(dir, fn){
14397         this._sort("key", dir, fn || function(a, b){
14398             return String(a).toUpperCase()-String(b).toUpperCase();
14399         });
14400     },
14401     
14402     /**
14403      * Returns a range of items in this collection
14404      * @param {Number} startIndex (optional) defaults to 0
14405      * @param {Number} endIndex (optional) default to the last item
14406      * @return {Array} An array of items
14407      */
14408     getRange : function(start, end){
14409         var items = this.items;
14410         if(items.length < 1){
14411             return [];
14412         }
14413         start = start || 0;
14414         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14415         var r = [];
14416         if(start <= end){
14417             for(var i = start; i <= end; i++) {
14418                     r[r.length] = items[i];
14419             }
14420         }else{
14421             for(var i = start; i >= end; i--) {
14422                     r[r.length] = items[i];
14423             }
14424         }
14425         return r;
14426     },
14427         
14428     /**
14429      * Filter the <i>objects</i> in this collection by a specific property. 
14430      * Returns a new collection that has been filtered.
14431      * @param {String} property A property on your objects
14432      * @param {String/RegExp} value Either string that the property values 
14433      * should start with or a RegExp to test against the property
14434      * @return {MixedCollection} The new filtered collection
14435      */
14436     filter : function(property, value){
14437         if(!value.exec){ // not a regex
14438             value = String(value);
14439             if(value.length == 0){
14440                 return this.clone();
14441             }
14442             value = new RegExp("^" + Roo.escapeRe(value), "i");
14443         }
14444         return this.filterBy(function(o){
14445             return o && value.test(o[property]);
14446         });
14447         },
14448     
14449     /**
14450      * Filter by a function. * Returns a new collection that has been filtered.
14451      * The passed function will be called with each 
14452      * object in the collection. If the function returns true, the value is included 
14453      * otherwise it is filtered.
14454      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14455      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14456      * @return {MixedCollection} The new filtered collection
14457      */
14458     filterBy : function(fn, scope){
14459         var r = new Roo.util.MixedCollection();
14460         r.getKey = this.getKey;
14461         var k = this.keys, it = this.items;
14462         for(var i = 0, len = it.length; i < len; i++){
14463             if(fn.call(scope||this, it[i], k[i])){
14464                                 r.add(k[i], it[i]);
14465                         }
14466         }
14467         return r;
14468     },
14469     
14470     /**
14471      * Creates a duplicate of this collection
14472      * @return {MixedCollection}
14473      */
14474     clone : function(){
14475         var r = new Roo.util.MixedCollection();
14476         var k = this.keys, it = this.items;
14477         for(var i = 0, len = it.length; i < len; i++){
14478             r.add(k[i], it[i]);
14479         }
14480         r.getKey = this.getKey;
14481         return r;
14482     }
14483 });
14484 /**
14485  * Returns the item associated with the passed key or index.
14486  * @method
14487  * @param {String/Number} key The key or index of the item.
14488  * @return {Object} The item associated with the passed key.
14489  */
14490 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14491  * Based on:
14492  * Ext JS Library 1.1.1
14493  * Copyright(c) 2006-2007, Ext JS, LLC.
14494  *
14495  * Originally Released Under LGPL - original licence link has changed is not relivant.
14496  *
14497  * Fork - LGPL
14498  * <script type="text/javascript">
14499  */
14500 /**
14501  * @class Roo.util.JSON
14502  * Modified version of Douglas Crockford"s json.js that doesn"t
14503  * mess with the Object prototype 
14504  * http://www.json.org/js.html
14505  * @static
14506  */
14507 Roo.util.JSON = new (function(){
14508     var useHasOwn = {}.hasOwnProperty ? true : false;
14509     
14510     // crashes Safari in some instances
14511     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14512     
14513     var pad = function(n) {
14514         return n < 10 ? "0" + n : n;
14515     };
14516     
14517     var m = {
14518         "\b": '\\b',
14519         "\t": '\\t',
14520         "\n": '\\n',
14521         "\f": '\\f',
14522         "\r": '\\r',
14523         '"' : '\\"',
14524         "\\": '\\\\'
14525     };
14526
14527     var encodeString = function(s){
14528         if (/["\\\x00-\x1f]/.test(s)) {
14529             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14530                 var c = m[b];
14531                 if(c){
14532                     return c;
14533                 }
14534                 c = b.charCodeAt();
14535                 return "\\u00" +
14536                     Math.floor(c / 16).toString(16) +
14537                     (c % 16).toString(16);
14538             }) + '"';
14539         }
14540         return '"' + s + '"';
14541     };
14542     
14543     var encodeArray = function(o){
14544         var a = ["["], b, i, l = o.length, v;
14545             for (i = 0; i < l; i += 1) {
14546                 v = o[i];
14547                 switch (typeof v) {
14548                     case "undefined":
14549                     case "function":
14550                     case "unknown":
14551                         break;
14552                     default:
14553                         if (b) {
14554                             a.push(',');
14555                         }
14556                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14557                         b = true;
14558                 }
14559             }
14560             a.push("]");
14561             return a.join("");
14562     };
14563     
14564     var encodeDate = function(o){
14565         return '"' + o.getFullYear() + "-" +
14566                 pad(o.getMonth() + 1) + "-" +
14567                 pad(o.getDate()) + "T" +
14568                 pad(o.getHours()) + ":" +
14569                 pad(o.getMinutes()) + ":" +
14570                 pad(o.getSeconds()) + '"';
14571     };
14572     
14573     /**
14574      * Encodes an Object, Array or other value
14575      * @param {Mixed} o The variable to encode
14576      * @return {String} The JSON string
14577      */
14578     this.encode = function(o)
14579     {
14580         // should this be extended to fully wrap stringify..
14581         
14582         if(typeof o == "undefined" || o === null){
14583             return "null";
14584         }else if(o instanceof Array){
14585             return encodeArray(o);
14586         }else if(o instanceof Date){
14587             return encodeDate(o);
14588         }else if(typeof o == "string"){
14589             return encodeString(o);
14590         }else if(typeof o == "number"){
14591             return isFinite(o) ? String(o) : "null";
14592         }else if(typeof o == "boolean"){
14593             return String(o);
14594         }else {
14595             var a = ["{"], b, i, v;
14596             for (i in o) {
14597                 if(!useHasOwn || o.hasOwnProperty(i)) {
14598                     v = o[i];
14599                     switch (typeof v) {
14600                     case "undefined":
14601                     case "function":
14602                     case "unknown":
14603                         break;
14604                     default:
14605                         if(b){
14606                             a.push(',');
14607                         }
14608                         a.push(this.encode(i), ":",
14609                                 v === null ? "null" : this.encode(v));
14610                         b = true;
14611                     }
14612                 }
14613             }
14614             a.push("}");
14615             return a.join("");
14616         }
14617     };
14618     
14619     /**
14620      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14621      * @param {String} json The JSON string
14622      * @return {Object} The resulting object
14623      */
14624     this.decode = function(json){
14625         
14626         return  /** eval:var:json */ eval("(" + json + ')');
14627     };
14628 })();
14629 /** 
14630  * Shorthand for {@link Roo.util.JSON#encode}
14631  * @member Roo encode 
14632  * @method */
14633 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14634 /** 
14635  * Shorthand for {@link Roo.util.JSON#decode}
14636  * @member Roo decode 
14637  * @method */
14638 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14639 /*
14640  * Based on:
14641  * Ext JS Library 1.1.1
14642  * Copyright(c) 2006-2007, Ext JS, LLC.
14643  *
14644  * Originally Released Under LGPL - original licence link has changed is not relivant.
14645  *
14646  * Fork - LGPL
14647  * <script type="text/javascript">
14648  */
14649  
14650 /**
14651  * @class Roo.util.Format
14652  * Reusable data formatting functions
14653  * @static
14654  */
14655 Roo.util.Format = function(){
14656     var trimRe = /^\s+|\s+$/g;
14657     return {
14658         /**
14659          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14660          * @param {String} value The string to truncate
14661          * @param {Number} length The maximum length to allow before truncating
14662          * @return {String} The converted text
14663          */
14664         ellipsis : function(value, len){
14665             if(value && value.length > len){
14666                 return value.substr(0, len-3)+"...";
14667             }
14668             return value;
14669         },
14670
14671         /**
14672          * Checks a reference and converts it to empty string if it is undefined
14673          * @param {Mixed} value Reference to check
14674          * @return {Mixed} Empty string if converted, otherwise the original value
14675          */
14676         undef : function(value){
14677             return typeof value != "undefined" ? value : "";
14678         },
14679
14680         /**
14681          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14682          * @param {String} value The string to encode
14683          * @return {String} The encoded text
14684          */
14685         htmlEncode : function(value){
14686             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14687         },
14688
14689         /**
14690          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14691          * @param {String} value The string to decode
14692          * @return {String} The decoded text
14693          */
14694         htmlDecode : function(value){
14695             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14696         },
14697
14698         /**
14699          * Trims any whitespace from either side of a string
14700          * @param {String} value The text to trim
14701          * @return {String} The trimmed text
14702          */
14703         trim : function(value){
14704             return String(value).replace(trimRe, "");
14705         },
14706
14707         /**
14708          * Returns a substring from within an original string
14709          * @param {String} value The original text
14710          * @param {Number} start The start index of the substring
14711          * @param {Number} length The length of the substring
14712          * @return {String} The substring
14713          */
14714         substr : function(value, start, length){
14715             return String(value).substr(start, length);
14716         },
14717
14718         /**
14719          * Converts a string to all lower case letters
14720          * @param {String} value The text to convert
14721          * @return {String} The converted text
14722          */
14723         lowercase : function(value){
14724             return String(value).toLowerCase();
14725         },
14726
14727         /**
14728          * Converts a string to all upper case letters
14729          * @param {String} value The text to convert
14730          * @return {String} The converted text
14731          */
14732         uppercase : function(value){
14733             return String(value).toUpperCase();
14734         },
14735
14736         /**
14737          * Converts the first character only of a string to upper case
14738          * @param {String} value The text to convert
14739          * @return {String} The converted text
14740          */
14741         capitalize : function(value){
14742             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14743         },
14744
14745         // private
14746         call : function(value, fn){
14747             if(arguments.length > 2){
14748                 var args = Array.prototype.slice.call(arguments, 2);
14749                 args.unshift(value);
14750                  
14751                 return /** eval:var:value */  eval(fn).apply(window, args);
14752             }else{
14753                 /** eval:var:value */
14754                 return /** eval:var:value */ eval(fn).call(window, value);
14755             }
14756         },
14757
14758        
14759         /**
14760          * safer version of Math.toFixed..??/
14761          * @param {Number/String} value The numeric value to format
14762          * @param {Number/String} value Decimal places 
14763          * @return {String} The formatted currency string
14764          */
14765         toFixed : function(v, n)
14766         {
14767             // why not use to fixed - precision is buggered???
14768             if (!n) {
14769                 return Math.round(v-0);
14770             }
14771             var fact = Math.pow(10,n+1);
14772             v = (Math.round((v-0)*fact))/fact;
14773             var z = (''+fact).substring(2);
14774             if (v == Math.floor(v)) {
14775                 return Math.floor(v) + '.' + z;
14776             }
14777             
14778             // now just padd decimals..
14779             var ps = String(v).split('.');
14780             var fd = (ps[1] + z);
14781             var r = fd.substring(0,n); 
14782             var rm = fd.substring(n); 
14783             if (rm < 5) {
14784                 return ps[0] + '.' + r;
14785             }
14786             r*=1; // turn it into a number;
14787             r++;
14788             if (String(r).length != n) {
14789                 ps[0]*=1;
14790                 ps[0]++;
14791                 r = String(r).substring(1); // chop the end off.
14792             }
14793             
14794             return ps[0] + '.' + r;
14795              
14796         },
14797         
14798         /**
14799          * Format a number as US currency
14800          * @param {Number/String} value The numeric value to format
14801          * @return {String} The formatted currency string
14802          */
14803         usMoney : function(v){
14804             return '$' + Roo.util.Format.number(v);
14805         },
14806         
14807         /**
14808          * Format a number
14809          * eventually this should probably emulate php's number_format
14810          * @param {Number/String} value The numeric value to format
14811          * @param {Number} decimals number of decimal places
14812          * @param {String} delimiter for thousands (default comma)
14813          * @return {String} The formatted currency string
14814          */
14815         number : function(v, decimals, thousandsDelimiter)
14816         {
14817             // multiply and round.
14818             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14819             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14820             
14821             var mul = Math.pow(10, decimals);
14822             var zero = String(mul).substring(1);
14823             v = (Math.round((v-0)*mul))/mul;
14824             
14825             // if it's '0' number.. then
14826             
14827             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14828             v = String(v);
14829             var ps = v.split('.');
14830             var whole = ps[0];
14831             
14832             var r = /(\d+)(\d{3})/;
14833             // add comma's
14834             
14835             if(thousandsDelimiter.length != 0) {
14836                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14837             } 
14838             
14839             var sub = ps[1] ?
14840                     // has decimals..
14841                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14842                     // does not have decimals
14843                     (decimals ? ('.' + zero) : '');
14844             
14845             
14846             return whole + sub ;
14847         },
14848         
14849         /**
14850          * Parse a value into a formatted date using the specified format pattern.
14851          * @param {Mixed} value The value to format
14852          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14853          * @return {String} The formatted date string
14854          */
14855         date : function(v, format){
14856             if(!v){
14857                 return "";
14858             }
14859             if(!(v instanceof Date)){
14860                 v = new Date(Date.parse(v));
14861             }
14862             return v.dateFormat(format || Roo.util.Format.defaults.date);
14863         },
14864
14865         /**
14866          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14867          * @param {String} format Any valid date format string
14868          * @return {Function} The date formatting function
14869          */
14870         dateRenderer : function(format){
14871             return function(v){
14872                 return Roo.util.Format.date(v, format);  
14873             };
14874         },
14875
14876         // private
14877         stripTagsRE : /<\/?[^>]+>/gi,
14878         
14879         /**
14880          * Strips all HTML tags
14881          * @param {Mixed} value The text from which to strip tags
14882          * @return {String} The stripped text
14883          */
14884         stripTags : function(v){
14885             return !v ? v : String(v).replace(this.stripTagsRE, "");
14886         },
14887         
14888         /**
14889          * Size in Mb,Gb etc.
14890          * @param {Number} value The number to be formated
14891          * @param {number} decimals how many decimal places
14892          * @return {String} the formated string
14893          */
14894         size : function(value, decimals)
14895         {
14896             var sizes = ['b', 'k', 'M', 'G', 'T'];
14897             if (value == 0) {
14898                 return 0;
14899             }
14900             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14901             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14902         }
14903         
14904         
14905         
14906     };
14907 }();
14908 Roo.util.Format.defaults = {
14909     date : 'd/M/Y'
14910 };/*
14911  * Based on:
14912  * Ext JS Library 1.1.1
14913  * Copyright(c) 2006-2007, Ext JS, LLC.
14914  *
14915  * Originally Released Under LGPL - original licence link has changed is not relivant.
14916  *
14917  * Fork - LGPL
14918  * <script type="text/javascript">
14919  */
14920
14921
14922  
14923
14924 /**
14925  * @class Roo.MasterTemplate
14926  * @extends Roo.Template
14927  * Provides a template that can have child templates. The syntax is:
14928 <pre><code>
14929 var t = new Roo.MasterTemplate(
14930         '&lt;select name="{name}"&gt;',
14931                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14932         '&lt;/select&gt;'
14933 );
14934 t.add('options', {value: 'foo', text: 'bar'});
14935 // or you can add multiple child elements in one shot
14936 t.addAll('options', [
14937     {value: 'foo', text: 'bar'},
14938     {value: 'foo2', text: 'bar2'},
14939     {value: 'foo3', text: 'bar3'}
14940 ]);
14941 // then append, applying the master template values
14942 t.append('my-form', {name: 'my-select'});
14943 </code></pre>
14944 * A name attribute for the child template is not required if you have only one child
14945 * template or you want to refer to them by index.
14946  */
14947 Roo.MasterTemplate = function(){
14948     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14949     this.originalHtml = this.html;
14950     var st = {};
14951     var m, re = this.subTemplateRe;
14952     re.lastIndex = 0;
14953     var subIndex = 0;
14954     while(m = re.exec(this.html)){
14955         var name = m[1], content = m[2];
14956         st[subIndex] = {
14957             name: name,
14958             index: subIndex,
14959             buffer: [],
14960             tpl : new Roo.Template(content)
14961         };
14962         if(name){
14963             st[name] = st[subIndex];
14964         }
14965         st[subIndex].tpl.compile();
14966         st[subIndex].tpl.call = this.call.createDelegate(this);
14967         subIndex++;
14968     }
14969     this.subCount = subIndex;
14970     this.subs = st;
14971 };
14972 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14973     /**
14974     * The regular expression used to match sub templates
14975     * @type RegExp
14976     * @property
14977     */
14978     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14979
14980     /**
14981      * Applies the passed values to a child template.
14982      * @param {String/Number} name (optional) The name or index of the child template
14983      * @param {Array/Object} values The values to be applied to the template
14984      * @return {MasterTemplate} this
14985      */
14986      add : function(name, values){
14987         if(arguments.length == 1){
14988             values = arguments[0];
14989             name = 0;
14990         }
14991         var s = this.subs[name];
14992         s.buffer[s.buffer.length] = s.tpl.apply(values);
14993         return this;
14994     },
14995
14996     /**
14997      * Applies all the passed values to a child template.
14998      * @param {String/Number} name (optional) The name or index of the child template
14999      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15000      * @param {Boolean} reset (optional) True to reset the template first
15001      * @return {MasterTemplate} this
15002      */
15003     fill : function(name, values, reset){
15004         var a = arguments;
15005         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15006             values = a[0];
15007             name = 0;
15008             reset = a[1];
15009         }
15010         if(reset){
15011             this.reset();
15012         }
15013         for(var i = 0, len = values.length; i < len; i++){
15014             this.add(name, values[i]);
15015         }
15016         return this;
15017     },
15018
15019     /**
15020      * Resets the template for reuse
15021      * @return {MasterTemplate} this
15022      */
15023      reset : function(){
15024         var s = this.subs;
15025         for(var i = 0; i < this.subCount; i++){
15026             s[i].buffer = [];
15027         }
15028         return this;
15029     },
15030
15031     applyTemplate : function(values){
15032         var s = this.subs;
15033         var replaceIndex = -1;
15034         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15035             return s[++replaceIndex].buffer.join("");
15036         });
15037         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15038     },
15039
15040     apply : function(){
15041         return this.applyTemplate.apply(this, arguments);
15042     },
15043
15044     compile : function(){return this;}
15045 });
15046
15047 /**
15048  * Alias for fill().
15049  * @method
15050  */
15051 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15052  /**
15053  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15054  * var tpl = Roo.MasterTemplate.from('element-id');
15055  * @param {String/HTMLElement} el
15056  * @param {Object} config
15057  * @static
15058  */
15059 Roo.MasterTemplate.from = function(el, config){
15060     el = Roo.getDom(el);
15061     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15062 };/*
15063  * Based on:
15064  * Ext JS Library 1.1.1
15065  * Copyright(c) 2006-2007, Ext JS, LLC.
15066  *
15067  * Originally Released Under LGPL - original licence link has changed is not relivant.
15068  *
15069  * Fork - LGPL
15070  * <script type="text/javascript">
15071  */
15072
15073  
15074 /**
15075  * @class Roo.util.CSS
15076  * Utility class for manipulating CSS rules
15077  * @static
15078
15079  */
15080 Roo.util.CSS = function(){
15081         var rules = null;
15082         var doc = document;
15083
15084     var camelRe = /(-[a-z])/gi;
15085     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15086
15087    return {
15088    /**
15089     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15090     * tag and appended to the HEAD of the document.
15091     * @param {String|Object} cssText The text containing the css rules
15092     * @param {String} id An id to add to the stylesheet for later removal
15093     * @return {StyleSheet}
15094     */
15095     createStyleSheet : function(cssText, id){
15096         var ss;
15097         var head = doc.getElementsByTagName("head")[0];
15098         var nrules = doc.createElement("style");
15099         nrules.setAttribute("type", "text/css");
15100         if(id){
15101             nrules.setAttribute("id", id);
15102         }
15103         if (typeof(cssText) != 'string') {
15104             // support object maps..
15105             // not sure if this a good idea.. 
15106             // perhaps it should be merged with the general css handling
15107             // and handle js style props.
15108             var cssTextNew = [];
15109             for(var n in cssText) {
15110                 var citems = [];
15111                 for(var k in cssText[n]) {
15112                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15113                 }
15114                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15115                 
15116             }
15117             cssText = cssTextNew.join("\n");
15118             
15119         }
15120        
15121        
15122        if(Roo.isIE){
15123            head.appendChild(nrules);
15124            ss = nrules.styleSheet;
15125            ss.cssText = cssText;
15126        }else{
15127            try{
15128                 nrules.appendChild(doc.createTextNode(cssText));
15129            }catch(e){
15130                nrules.cssText = cssText; 
15131            }
15132            head.appendChild(nrules);
15133            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15134        }
15135        this.cacheStyleSheet(ss);
15136        return ss;
15137    },
15138
15139    /**
15140     * Removes a style or link tag by id
15141     * @param {String} id The id of the tag
15142     */
15143    removeStyleSheet : function(id){
15144        var existing = doc.getElementById(id);
15145        if(existing){
15146            existing.parentNode.removeChild(existing);
15147        }
15148    },
15149
15150    /**
15151     * Dynamically swaps an existing stylesheet reference for a new one
15152     * @param {String} id The id of an existing link tag to remove
15153     * @param {String} url The href of the new stylesheet to include
15154     */
15155    swapStyleSheet : function(id, url){
15156        this.removeStyleSheet(id);
15157        var ss = doc.createElement("link");
15158        ss.setAttribute("rel", "stylesheet");
15159        ss.setAttribute("type", "text/css");
15160        ss.setAttribute("id", id);
15161        ss.setAttribute("href", url);
15162        doc.getElementsByTagName("head")[0].appendChild(ss);
15163    },
15164    
15165    /**
15166     * Refresh the rule cache if you have dynamically added stylesheets
15167     * @return {Object} An object (hash) of rules indexed by selector
15168     */
15169    refreshCache : function(){
15170        return this.getRules(true);
15171    },
15172
15173    // private
15174    cacheStyleSheet : function(stylesheet){
15175        if(!rules){
15176            rules = {};
15177        }
15178        try{// try catch for cross domain access issue
15179            var ssRules = stylesheet.cssRules || stylesheet.rules;
15180            for(var j = ssRules.length-1; j >= 0; --j){
15181                rules[ssRules[j].selectorText] = ssRules[j];
15182            }
15183        }catch(e){}
15184    },
15185    
15186    /**
15187     * Gets all css rules for the document
15188     * @param {Boolean} refreshCache true to refresh the internal cache
15189     * @return {Object} An object (hash) of rules indexed by selector
15190     */
15191    getRules : function(refreshCache){
15192                 if(rules == null || refreshCache){
15193                         rules = {};
15194                         var ds = doc.styleSheets;
15195                         for(var i =0, len = ds.length; i < len; i++){
15196                             try{
15197                         this.cacheStyleSheet(ds[i]);
15198                     }catch(e){} 
15199                 }
15200                 }
15201                 return rules;
15202         },
15203         
15204         /**
15205     * Gets an an individual CSS rule by selector(s)
15206     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15207     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15208     * @return {CSSRule} The CSS rule or null if one is not found
15209     */
15210    getRule : function(selector, refreshCache){
15211                 var rs = this.getRules(refreshCache);
15212                 if(!(selector instanceof Array)){
15213                     return rs[selector];
15214                 }
15215                 for(var i = 0; i < selector.length; i++){
15216                         if(rs[selector[i]]){
15217                                 return rs[selector[i]];
15218                         }
15219                 }
15220                 return null;
15221         },
15222         
15223         
15224         /**
15225     * Updates a rule property
15226     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15227     * @param {String} property The css property
15228     * @param {String} value The new value for the property
15229     * @return {Boolean} true If a rule was found and updated
15230     */
15231    updateRule : function(selector, property, value){
15232                 if(!(selector instanceof Array)){
15233                         var rule = this.getRule(selector);
15234                         if(rule){
15235                                 rule.style[property.replace(camelRe, camelFn)] = value;
15236                                 return true;
15237                         }
15238                 }else{
15239                         for(var i = 0; i < selector.length; i++){
15240                                 if(this.updateRule(selector[i], property, value)){
15241                                         return true;
15242                                 }
15243                         }
15244                 }
15245                 return false;
15246         }
15247    };   
15248 }();/*
15249  * Based on:
15250  * Ext JS Library 1.1.1
15251  * Copyright(c) 2006-2007, Ext JS, LLC.
15252  *
15253  * Originally Released Under LGPL - original licence link has changed is not relivant.
15254  *
15255  * Fork - LGPL
15256  * <script type="text/javascript">
15257  */
15258
15259  
15260
15261 /**
15262  * @class Roo.util.ClickRepeater
15263  * @extends Roo.util.Observable
15264  * 
15265  * A wrapper class which can be applied to any element. Fires a "click" event while the
15266  * mouse is pressed. The interval between firings may be specified in the config but
15267  * defaults to 10 milliseconds.
15268  * 
15269  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15270  * 
15271  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15272  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15273  * Similar to an autorepeat key delay.
15274  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15275  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15276  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15277  *           "interval" and "delay" are ignored. "immediate" is honored.
15278  * @cfg {Boolean} preventDefault True to prevent the default click event
15279  * @cfg {Boolean} stopDefault True to stop the default click event
15280  * 
15281  * @history
15282  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15283  *     2007-02-02 jvs Renamed to ClickRepeater
15284  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15285  *
15286  *  @constructor
15287  * @param {String/HTMLElement/Element} el The element to listen on
15288  * @param {Object} config
15289  **/
15290 Roo.util.ClickRepeater = function(el, config)
15291 {
15292     this.el = Roo.get(el);
15293     this.el.unselectable();
15294
15295     Roo.apply(this, config);
15296
15297     this.addEvents({
15298     /**
15299      * @event mousedown
15300      * Fires when the mouse button is depressed.
15301      * @param {Roo.util.ClickRepeater} this
15302      */
15303         "mousedown" : true,
15304     /**
15305      * @event click
15306      * Fires on a specified interval during the time the element is pressed.
15307      * @param {Roo.util.ClickRepeater} this
15308      */
15309         "click" : true,
15310     /**
15311      * @event mouseup
15312      * Fires when the mouse key is released.
15313      * @param {Roo.util.ClickRepeater} this
15314      */
15315         "mouseup" : true
15316     });
15317
15318     this.el.on("mousedown", this.handleMouseDown, this);
15319     if(this.preventDefault || this.stopDefault){
15320         this.el.on("click", function(e){
15321             if(this.preventDefault){
15322                 e.preventDefault();
15323             }
15324             if(this.stopDefault){
15325                 e.stopEvent();
15326             }
15327         }, this);
15328     }
15329
15330     // allow inline handler
15331     if(this.handler){
15332         this.on("click", this.handler,  this.scope || this);
15333     }
15334
15335     Roo.util.ClickRepeater.superclass.constructor.call(this);
15336 };
15337
15338 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15339     interval : 20,
15340     delay: 250,
15341     preventDefault : true,
15342     stopDefault : false,
15343     timer : 0,
15344
15345     // private
15346     handleMouseDown : function(){
15347         clearTimeout(this.timer);
15348         this.el.blur();
15349         if(this.pressClass){
15350             this.el.addClass(this.pressClass);
15351         }
15352         this.mousedownTime = new Date();
15353
15354         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15355         this.el.on("mouseout", this.handleMouseOut, this);
15356
15357         this.fireEvent("mousedown", this);
15358         this.fireEvent("click", this);
15359         
15360         this.timer = this.click.defer(this.delay || this.interval, this);
15361     },
15362
15363     // private
15364     click : function(){
15365         this.fireEvent("click", this);
15366         this.timer = this.click.defer(this.getInterval(), this);
15367     },
15368
15369     // private
15370     getInterval: function(){
15371         if(!this.accelerate){
15372             return this.interval;
15373         }
15374         var pressTime = this.mousedownTime.getElapsed();
15375         if(pressTime < 500){
15376             return 400;
15377         }else if(pressTime < 1700){
15378             return 320;
15379         }else if(pressTime < 2600){
15380             return 250;
15381         }else if(pressTime < 3500){
15382             return 180;
15383         }else if(pressTime < 4400){
15384             return 140;
15385         }else if(pressTime < 5300){
15386             return 80;
15387         }else if(pressTime < 6200){
15388             return 50;
15389         }else{
15390             return 10;
15391         }
15392     },
15393
15394     // private
15395     handleMouseOut : function(){
15396         clearTimeout(this.timer);
15397         if(this.pressClass){
15398             this.el.removeClass(this.pressClass);
15399         }
15400         this.el.on("mouseover", this.handleMouseReturn, this);
15401     },
15402
15403     // private
15404     handleMouseReturn : function(){
15405         this.el.un("mouseover", this.handleMouseReturn);
15406         if(this.pressClass){
15407             this.el.addClass(this.pressClass);
15408         }
15409         this.click();
15410     },
15411
15412     // private
15413     handleMouseUp : function(){
15414         clearTimeout(this.timer);
15415         this.el.un("mouseover", this.handleMouseReturn);
15416         this.el.un("mouseout", this.handleMouseOut);
15417         Roo.get(document).un("mouseup", this.handleMouseUp);
15418         this.el.removeClass(this.pressClass);
15419         this.fireEvent("mouseup", this);
15420     }
15421 });/**
15422  * @class Roo.util.Clipboard
15423  * @static
15424  * 
15425  * Clipboard UTILS
15426  * 
15427  **/
15428 Roo.util.Clipboard = {
15429     /**
15430      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15431      * @param {String} text to copy to clipboard
15432      */
15433     write : function(text) {
15434         // navigator clipboard api needs a secure context (https)
15435         if (navigator.clipboard && window.isSecureContext) {
15436             // navigator clipboard api method'
15437             navigator.clipboard.writeText(text);
15438             return ;
15439         } 
15440         // text area method
15441         var ta = document.createElement("textarea");
15442         ta.value = text;
15443         // make the textarea out of viewport
15444         ta.style.position = "fixed";
15445         ta.style.left = "-999999px";
15446         ta.style.top = "-999999px";
15447         document.body.appendChild(ta);
15448         ta.focus();
15449         ta.select();
15450         document.execCommand('copy');
15451         (function() {
15452             ta.remove();
15453         }).defer(100);
15454         
15455     }
15456         
15457 }
15458     /*
15459  * Based on:
15460  * Ext JS Library 1.1.1
15461  * Copyright(c) 2006-2007, Ext JS, LLC.
15462  *
15463  * Originally Released Under LGPL - original licence link has changed is not relivant.
15464  *
15465  * Fork - LGPL
15466  * <script type="text/javascript">
15467  */
15468
15469  
15470 /**
15471  * @class Roo.KeyNav
15472  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15473  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15474  * way to implement custom navigation schemes for any UI component.</p>
15475  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15476  * pageUp, pageDown, del, home, end.  Usage:</p>
15477  <pre><code>
15478 var nav = new Roo.KeyNav("my-element", {
15479     "left" : function(e){
15480         this.moveLeft(e.ctrlKey);
15481     },
15482     "right" : function(e){
15483         this.moveRight(e.ctrlKey);
15484     },
15485     "enter" : function(e){
15486         this.save();
15487     },
15488     scope : this
15489 });
15490 </code></pre>
15491  * @constructor
15492  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15493  * @param {Object} config The config
15494  */
15495 Roo.KeyNav = function(el, config){
15496     this.el = Roo.get(el);
15497     Roo.apply(this, config);
15498     if(!this.disabled){
15499         this.disabled = true;
15500         this.enable();
15501     }
15502 };
15503
15504 Roo.KeyNav.prototype = {
15505     /**
15506      * @cfg {Boolean} disabled
15507      * True to disable this KeyNav instance (defaults to false)
15508      */
15509     disabled : false,
15510     /**
15511      * @cfg {String} defaultEventAction
15512      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15513      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15514      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15515      */
15516     defaultEventAction: "stopEvent",
15517     /**
15518      * @cfg {Boolean} forceKeyDown
15519      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15520      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15521      * handle keydown instead of keypress.
15522      */
15523     forceKeyDown : false,
15524
15525     // private
15526     prepareEvent : function(e){
15527         var k = e.getKey();
15528         var h = this.keyToHandler[k];
15529         //if(h && this[h]){
15530         //    e.stopPropagation();
15531         //}
15532         if(Roo.isSafari && h && k >= 37 && k <= 40){
15533             e.stopEvent();
15534         }
15535     },
15536
15537     // private
15538     relay : function(e){
15539         var k = e.getKey();
15540         var h = this.keyToHandler[k];
15541         if(h && this[h]){
15542             if(this.doRelay(e, this[h], h) !== true){
15543                 e[this.defaultEventAction]();
15544             }
15545         }
15546     },
15547
15548     // private
15549     doRelay : function(e, h, hname){
15550         return h.call(this.scope || this, e);
15551     },
15552
15553     // possible handlers
15554     enter : false,
15555     left : false,
15556     right : false,
15557     up : false,
15558     down : false,
15559     tab : false,
15560     esc : false,
15561     pageUp : false,
15562     pageDown : false,
15563     del : false,
15564     home : false,
15565     end : false,
15566
15567     // quick lookup hash
15568     keyToHandler : {
15569         37 : "left",
15570         39 : "right",
15571         38 : "up",
15572         40 : "down",
15573         33 : "pageUp",
15574         34 : "pageDown",
15575         46 : "del",
15576         36 : "home",
15577         35 : "end",
15578         13 : "enter",
15579         27 : "esc",
15580         9  : "tab"
15581     },
15582
15583         /**
15584          * Enable this KeyNav
15585          */
15586         enable: function(){
15587                 if(this.disabled){
15588             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15589             // the EventObject will normalize Safari automatically
15590             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15591                 this.el.on("keydown", this.relay,  this);
15592             }else{
15593                 this.el.on("keydown", this.prepareEvent,  this);
15594                 this.el.on("keypress", this.relay,  this);
15595             }
15596                     this.disabled = false;
15597                 }
15598         },
15599
15600         /**
15601          * Disable this KeyNav
15602          */
15603         disable: function(){
15604                 if(!this.disabled){
15605                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15606                 this.el.un("keydown", this.relay);
15607             }else{
15608                 this.el.un("keydown", this.prepareEvent);
15609                 this.el.un("keypress", this.relay);
15610             }
15611                     this.disabled = true;
15612                 }
15613         }
15614 };/*
15615  * Based on:
15616  * Ext JS Library 1.1.1
15617  * Copyright(c) 2006-2007, Ext JS, LLC.
15618  *
15619  * Originally Released Under LGPL - original licence link has changed is not relivant.
15620  *
15621  * Fork - LGPL
15622  * <script type="text/javascript">
15623  */
15624
15625  
15626 /**
15627  * @class Roo.KeyMap
15628  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15629  * The constructor accepts the same config object as defined by {@link #addBinding}.
15630  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15631  * combination it will call the function with this signature (if the match is a multi-key
15632  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15633  * A KeyMap can also handle a string representation of keys.<br />
15634  * Usage:
15635  <pre><code>
15636 // map one key by key code
15637 var map = new Roo.KeyMap("my-element", {
15638     key: 13, // or Roo.EventObject.ENTER
15639     fn: myHandler,
15640     scope: myObject
15641 });
15642
15643 // map multiple keys to one action by string
15644 var map = new Roo.KeyMap("my-element", {
15645     key: "a\r\n\t",
15646     fn: myHandler,
15647     scope: myObject
15648 });
15649
15650 // map multiple keys to multiple actions by strings and array of codes
15651 var map = new Roo.KeyMap("my-element", [
15652     {
15653         key: [10,13],
15654         fn: function(){ alert("Return was pressed"); }
15655     }, {
15656         key: "abc",
15657         fn: function(){ alert('a, b or c was pressed'); }
15658     }, {
15659         key: "\t",
15660         ctrl:true,
15661         shift:true,
15662         fn: function(){ alert('Control + shift + tab was pressed.'); }
15663     }
15664 ]);
15665 </code></pre>
15666  * <b>Note: A KeyMap starts enabled</b>
15667  * @constructor
15668  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15669  * @param {Object} config The config (see {@link #addBinding})
15670  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15671  */
15672 Roo.KeyMap = function(el, config, eventName){
15673     this.el  = Roo.get(el);
15674     this.eventName = eventName || "keydown";
15675     this.bindings = [];
15676     if(config){
15677         this.addBinding(config);
15678     }
15679     this.enable();
15680 };
15681
15682 Roo.KeyMap.prototype = {
15683     /**
15684      * True to stop the event from bubbling and prevent the default browser action if the
15685      * key was handled by the KeyMap (defaults to false)
15686      * @type Boolean
15687      */
15688     stopEvent : false,
15689
15690     /**
15691      * Add a new binding to this KeyMap. The following config object properties are supported:
15692      * <pre>
15693 Property    Type             Description
15694 ----------  ---------------  ----------------------------------------------------------------------
15695 key         String/Array     A single keycode or an array of keycodes to handle
15696 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15697 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15698 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15699 fn          Function         The function to call when KeyMap finds the expected key combination
15700 scope       Object           The scope of the callback function
15701 </pre>
15702      *
15703      * Usage:
15704      * <pre><code>
15705 // Create a KeyMap
15706 var map = new Roo.KeyMap(document, {
15707     key: Roo.EventObject.ENTER,
15708     fn: handleKey,
15709     scope: this
15710 });
15711
15712 //Add a new binding to the existing KeyMap later
15713 map.addBinding({
15714     key: 'abc',
15715     shift: true,
15716     fn: handleKey,
15717     scope: this
15718 });
15719 </code></pre>
15720      * @param {Object/Array} config A single KeyMap config or an array of configs
15721      */
15722         addBinding : function(config){
15723         if(config instanceof Array){
15724             for(var i = 0, len = config.length; i < len; i++){
15725                 this.addBinding(config[i]);
15726             }
15727             return;
15728         }
15729         var keyCode = config.key,
15730             shift = config.shift, 
15731             ctrl = config.ctrl, 
15732             alt = config.alt,
15733             fn = config.fn,
15734             scope = config.scope;
15735         if(typeof keyCode == "string"){
15736             var ks = [];
15737             var keyString = keyCode.toUpperCase();
15738             for(var j = 0, len = keyString.length; j < len; j++){
15739                 ks.push(keyString.charCodeAt(j));
15740             }
15741             keyCode = ks;
15742         }
15743         var keyArray = keyCode instanceof Array;
15744         var handler = function(e){
15745             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15746                 var k = e.getKey();
15747                 if(keyArray){
15748                     for(var i = 0, len = keyCode.length; i < len; i++){
15749                         if(keyCode[i] == k){
15750                           if(this.stopEvent){
15751                               e.stopEvent();
15752                           }
15753                           fn.call(scope || window, k, e);
15754                           return;
15755                         }
15756                     }
15757                 }else{
15758                     if(k == keyCode){
15759                         if(this.stopEvent){
15760                            e.stopEvent();
15761                         }
15762                         fn.call(scope || window, k, e);
15763                     }
15764                 }
15765             }
15766         };
15767         this.bindings.push(handler);  
15768         },
15769
15770     /**
15771      * Shorthand for adding a single key listener
15772      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15773      * following options:
15774      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15775      * @param {Function} fn The function to call
15776      * @param {Object} scope (optional) The scope of the function
15777      */
15778     on : function(key, fn, scope){
15779         var keyCode, shift, ctrl, alt;
15780         if(typeof key == "object" && !(key instanceof Array)){
15781             keyCode = key.key;
15782             shift = key.shift;
15783             ctrl = key.ctrl;
15784             alt = key.alt;
15785         }else{
15786             keyCode = key;
15787         }
15788         this.addBinding({
15789             key: keyCode,
15790             shift: shift,
15791             ctrl: ctrl,
15792             alt: alt,
15793             fn: fn,
15794             scope: scope
15795         })
15796     },
15797
15798     // private
15799     handleKeyDown : function(e){
15800             if(this.enabled){ //just in case
15801             var b = this.bindings;
15802             for(var i = 0, len = b.length; i < len; i++){
15803                 b[i].call(this, e);
15804             }
15805             }
15806         },
15807         
15808         /**
15809          * Returns true if this KeyMap is enabled
15810          * @return {Boolean} 
15811          */
15812         isEnabled : function(){
15813             return this.enabled;  
15814         },
15815         
15816         /**
15817          * Enables this KeyMap
15818          */
15819         enable: function(){
15820                 if(!this.enabled){
15821                     this.el.on(this.eventName, this.handleKeyDown, this);
15822                     this.enabled = true;
15823                 }
15824         },
15825
15826         /**
15827          * Disable this KeyMap
15828          */
15829         disable: function(){
15830                 if(this.enabled){
15831                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15832                     this.enabled = false;
15833                 }
15834         }
15835 };/*
15836  * Based on:
15837  * Ext JS Library 1.1.1
15838  * Copyright(c) 2006-2007, Ext JS, LLC.
15839  *
15840  * Originally Released Under LGPL - original licence link has changed is not relivant.
15841  *
15842  * Fork - LGPL
15843  * <script type="text/javascript">
15844  */
15845
15846  
15847 /**
15848  * @class Roo.util.TextMetrics
15849  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15850  * wide, in pixels, a given block of text will be.
15851  * @static
15852  */
15853 Roo.util.TextMetrics = function(){
15854     var shared;
15855     return {
15856         /**
15857          * Measures the size of the specified text
15858          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15859          * that can affect the size of the rendered text
15860          * @param {String} text The text to measure
15861          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15862          * in order to accurately measure the text height
15863          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15864          */
15865         measure : function(el, text, fixedWidth){
15866             if(!shared){
15867                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15868             }
15869             shared.bind(el);
15870             shared.setFixedWidth(fixedWidth || 'auto');
15871             return shared.getSize(text);
15872         },
15873
15874         /**
15875          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15876          * the overhead of multiple calls to initialize the style properties on each measurement.
15877          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15878          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15879          * in order to accurately measure the text height
15880          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15881          */
15882         createInstance : function(el, fixedWidth){
15883             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15884         }
15885     };
15886 }();
15887
15888 /**
15889  * @class Roo.util.TextMetrics.Instance
15890  * Instance of  TextMetrics Calcuation
15891  * @constructor
15892  * Create a new TextMetrics Instance
15893  * @param {Object} bindto
15894  * @param {Boolean} fixedWidth
15895  */
15896
15897 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15898 {
15899     var ml = new Roo.Element(document.createElement('div'));
15900     document.body.appendChild(ml.dom);
15901     ml.position('absolute');
15902     ml.setLeftTop(-1000, -1000);
15903     ml.hide();
15904
15905     if(fixedWidth){
15906         ml.setWidth(fixedWidth);
15907     }
15908      
15909     var instance = {
15910         /**
15911          * Returns the size of the specified text based on the internal element's style and width properties
15912          * @param {String} text The text to measure
15913          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15914          */
15915         getSize : function(text){
15916             ml.update(text);
15917             var s = ml.getSize();
15918             ml.update('');
15919             return s;
15920         },
15921
15922         /**
15923          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15924          * that can affect the size of the rendered text
15925          * @param {String/HTMLElement} el The element, dom node or id
15926          */
15927         bind : function(el){
15928             ml.setStyle(
15929                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15930             );
15931         },
15932
15933         /**
15934          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15935          * to set a fixed width in order to accurately measure the text height.
15936          * @param {Number} width The width to set on the element
15937          */
15938         setFixedWidth : function(width){
15939             ml.setWidth(width);
15940         },
15941
15942         /**
15943          * Returns the measured width of the specified text
15944          * @param {String} text The text to measure
15945          * @return {Number} width The width in pixels
15946          */
15947         getWidth : function(text){
15948             ml.dom.style.width = 'auto';
15949             return this.getSize(text).width;
15950         },
15951
15952         /**
15953          * Returns the measured height of the specified text.  For multiline text, be sure to call
15954          * {@link #setFixedWidth} if necessary.
15955          * @param {String} text The text to measure
15956          * @return {Number} height The height in pixels
15957          */
15958         getHeight : function(text){
15959             return this.getSize(text).height;
15960         }
15961     };
15962
15963     instance.bind(bindTo);
15964
15965     return instance;
15966 };
15967
15968 // backwards compat
15969 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15970  * Based on:
15971  * Ext JS Library 1.1.1
15972  * Copyright(c) 2006-2007, Ext JS, LLC.
15973  *
15974  * Originally Released Under LGPL - original licence link has changed is not relivant.
15975  *
15976  * Fork - LGPL
15977  * <script type="text/javascript">
15978  */
15979
15980 /**
15981  * @class Roo.state.Provider
15982  * Abstract base class for state provider implementations. This class provides methods
15983  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15984  * Provider interface.
15985  */
15986 Roo.state.Provider = function(){
15987     /**
15988      * @event statechange
15989      * Fires when a state change occurs.
15990      * @param {Provider} this This state provider
15991      * @param {String} key The state key which was changed
15992      * @param {String} value The encoded value for the state
15993      */
15994     this.addEvents({
15995         "statechange": true
15996     });
15997     this.state = {};
15998     Roo.state.Provider.superclass.constructor.call(this);
15999 };
16000 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16001     /**
16002      * Returns the current value for a key
16003      * @param {String} name The key name
16004      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16005      * @return {Mixed} The state data
16006      */
16007     get : function(name, defaultValue){
16008         return typeof this.state[name] == "undefined" ?
16009             defaultValue : this.state[name];
16010     },
16011     
16012     /**
16013      * Clears a value from the state
16014      * @param {String} name The key name
16015      */
16016     clear : function(name){
16017         delete this.state[name];
16018         this.fireEvent("statechange", this, name, null);
16019     },
16020     
16021     /**
16022      * Sets the value for a key
16023      * @param {String} name The key name
16024      * @param {Mixed} value The value to set
16025      */
16026     set : function(name, value){
16027         this.state[name] = value;
16028         this.fireEvent("statechange", this, name, value);
16029     },
16030     
16031     /**
16032      * Decodes a string previously encoded with {@link #encodeValue}.
16033      * @param {String} value The value to decode
16034      * @return {Mixed} The decoded value
16035      */
16036     decodeValue : function(cookie){
16037         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16038         var matches = re.exec(unescape(cookie));
16039         if(!matches || !matches[1]) {
16040             return; // non state cookie
16041         }
16042         var type = matches[1];
16043         var v = matches[2];
16044         switch(type){
16045             case "n":
16046                 return parseFloat(v);
16047             case "d":
16048                 return new Date(Date.parse(v));
16049             case "b":
16050                 return (v == "1");
16051             case "a":
16052                 var all = [];
16053                 var values = v.split("^");
16054                 for(var i = 0, len = values.length; i < len; i++){
16055                     all.push(this.decodeValue(values[i]));
16056                 }
16057                 return all;
16058            case "o":
16059                 var all = {};
16060                 var values = v.split("^");
16061                 for(var i = 0, len = values.length; i < len; i++){
16062                     var kv = values[i].split("=");
16063                     all[kv[0]] = this.decodeValue(kv[1]);
16064                 }
16065                 return all;
16066            default:
16067                 return v;
16068         }
16069     },
16070     
16071     /**
16072      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16073      * @param {Mixed} value The value to encode
16074      * @return {String} The encoded value
16075      */
16076     encodeValue : function(v){
16077         var enc;
16078         if(typeof v == "number"){
16079             enc = "n:" + v;
16080         }else if(typeof v == "boolean"){
16081             enc = "b:" + (v ? "1" : "0");
16082         }else if(v instanceof Date){
16083             enc = "d:" + v.toGMTString();
16084         }else if(v instanceof Array){
16085             var flat = "";
16086             for(var i = 0, len = v.length; i < len; i++){
16087                 flat += this.encodeValue(v[i]);
16088                 if(i != len-1) {
16089                     flat += "^";
16090                 }
16091             }
16092             enc = "a:" + flat;
16093         }else if(typeof v == "object"){
16094             var flat = "";
16095             for(var key in v){
16096                 if(typeof v[key] != "function"){
16097                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16098                 }
16099             }
16100             enc = "o:" + flat.substring(0, flat.length-1);
16101         }else{
16102             enc = "s:" + v;
16103         }
16104         return escape(enc);        
16105     }
16106 });
16107
16108 /*
16109  * Based on:
16110  * Ext JS Library 1.1.1
16111  * Copyright(c) 2006-2007, Ext JS, LLC.
16112  *
16113  * Originally Released Under LGPL - original licence link has changed is not relivant.
16114  *
16115  * Fork - LGPL
16116  * <script type="text/javascript">
16117  */
16118 /**
16119  * @class Roo.state.Manager
16120  * This is the global state manager. By default all components that are "state aware" check this class
16121  * for state information if you don't pass them a custom state provider. In order for this class
16122  * to be useful, it must be initialized with a provider when your application initializes.
16123  <pre><code>
16124 // in your initialization function
16125 init : function(){
16126    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16127    ...
16128    // supposed you have a {@link Roo.BorderLayout}
16129    var layout = new Roo.BorderLayout(...);
16130    layout.restoreState();
16131    // or a {Roo.BasicDialog}
16132    var dialog = new Roo.BasicDialog(...);
16133    dialog.restoreState();
16134  </code></pre>
16135  * @static
16136  */
16137 Roo.state.Manager = function(){
16138     var provider = new Roo.state.Provider();
16139     
16140     return {
16141         /**
16142          * Configures the default state provider for your application
16143          * @param {Provider} stateProvider The state provider to set
16144          */
16145         setProvider : function(stateProvider){
16146             provider = stateProvider;
16147         },
16148         
16149         /**
16150          * Returns the current value for a key
16151          * @param {String} name The key name
16152          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16153          * @return {Mixed} The state data
16154          */
16155         get : function(key, defaultValue){
16156             return provider.get(key, defaultValue);
16157         },
16158         
16159         /**
16160          * Sets the value for a key
16161          * @param {String} name The key name
16162          * @param {Mixed} value The state data
16163          */
16164          set : function(key, value){
16165             provider.set(key, value);
16166         },
16167         
16168         /**
16169          * Clears a value from the state
16170          * @param {String} name The key name
16171          */
16172         clear : function(key){
16173             provider.clear(key);
16174         },
16175         
16176         /**
16177          * Gets the currently configured state provider
16178          * @return {Provider} The state provider
16179          */
16180         getProvider : function(){
16181             return provider;
16182         }
16183     };
16184 }();
16185 /*
16186  * Based on:
16187  * Ext JS Library 1.1.1
16188  * Copyright(c) 2006-2007, Ext JS, LLC.
16189  *
16190  * Originally Released Under LGPL - original licence link has changed is not relivant.
16191  *
16192  * Fork - LGPL
16193  * <script type="text/javascript">
16194  */
16195 /**
16196  * @class Roo.state.CookieProvider
16197  * @extends Roo.state.Provider
16198  * The default Provider implementation which saves state via cookies.
16199  * <br />Usage:
16200  <pre><code>
16201    var cp = new Roo.state.CookieProvider({
16202        path: "/cgi-bin/",
16203        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16204        domain: "roojs.com"
16205    })
16206    Roo.state.Manager.setProvider(cp);
16207  </code></pre>
16208  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16209  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16210  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16211  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16212  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16213  * domain the page is running on including the 'www' like 'www.roojs.com')
16214  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16215  * @constructor
16216  * Create a new CookieProvider
16217  * @param {Object} config The configuration object
16218  */
16219 Roo.state.CookieProvider = function(config){
16220     Roo.state.CookieProvider.superclass.constructor.call(this);
16221     this.path = "/";
16222     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16223     this.domain = null;
16224     this.secure = false;
16225     Roo.apply(this, config);
16226     this.state = this.readCookies();
16227 };
16228
16229 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16230     // private
16231     set : function(name, value){
16232         if(typeof value == "undefined" || value === null){
16233             this.clear(name);
16234             return;
16235         }
16236         this.setCookie(name, value);
16237         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16238     },
16239
16240     // private
16241     clear : function(name){
16242         this.clearCookie(name);
16243         Roo.state.CookieProvider.superclass.clear.call(this, name);
16244     },
16245
16246     // private
16247     readCookies : function(){
16248         var cookies = {};
16249         var c = document.cookie + ";";
16250         var re = /\s?(.*?)=(.*?);/g;
16251         var matches;
16252         while((matches = re.exec(c)) != null){
16253             var name = matches[1];
16254             var value = matches[2];
16255             if(name && name.substring(0,3) == "ys-"){
16256                 cookies[name.substr(3)] = this.decodeValue(value);
16257             }
16258         }
16259         return cookies;
16260     },
16261
16262     // private
16263     setCookie : function(name, value){
16264         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16265            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16266            ((this.path == null) ? "" : ("; path=" + this.path)) +
16267            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16268            ((this.secure == true) ? "; secure" : "");
16269     },
16270
16271     // private
16272     clearCookie : function(name){
16273         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16274            ((this.path == null) ? "" : ("; path=" + this.path)) +
16275            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16276            ((this.secure == true) ? "; secure" : "");
16277     }
16278 });/*
16279  * Based on:
16280  * Ext JS Library 1.1.1
16281  * Copyright(c) 2006-2007, Ext JS, LLC.
16282  *
16283  * Originally Released Under LGPL - original licence link has changed is not relivant.
16284  *
16285  * Fork - LGPL
16286  * <script type="text/javascript">
16287  */
16288  
16289
16290 /**
16291  * @class Roo.ComponentMgr
16292  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16293  * @static
16294  */
16295 Roo.ComponentMgr = function(){
16296     var all = new Roo.util.MixedCollection();
16297
16298     return {
16299         /**
16300          * Registers a component.
16301          * @param {Roo.Component} c The component
16302          */
16303         register : function(c){
16304             all.add(c);
16305         },
16306
16307         /**
16308          * Unregisters a component.
16309          * @param {Roo.Component} c The component
16310          */
16311         unregister : function(c){
16312             all.remove(c);
16313         },
16314
16315         /**
16316          * Returns a component by id
16317          * @param {String} id The component id
16318          */
16319         get : function(id){
16320             return all.get(id);
16321         },
16322
16323         /**
16324          * Registers a function that will be called when a specified component is added to ComponentMgr
16325          * @param {String} id The component id
16326          * @param {Funtction} fn The callback function
16327          * @param {Object} scope The scope of the callback
16328          */
16329         onAvailable : function(id, fn, scope){
16330             all.on("add", function(index, o){
16331                 if(o.id == id){
16332                     fn.call(scope || o, o);
16333                     all.un("add", fn, scope);
16334                 }
16335             });
16336         }
16337     };
16338 }();/*
16339  * Based on:
16340  * Ext JS Library 1.1.1
16341  * Copyright(c) 2006-2007, Ext JS, LLC.
16342  *
16343  * Originally Released Under LGPL - original licence link has changed is not relivant.
16344  *
16345  * Fork - LGPL
16346  * <script type="text/javascript">
16347  */
16348  
16349 /**
16350  * @class Roo.Component
16351  * @extends Roo.util.Observable
16352  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16353  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16354  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16355  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16356  * All visual components (widgets) that require rendering into a layout should subclass Component.
16357  * @constructor
16358  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16359  * 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
16360  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16361  */
16362 Roo.Component = function(config){
16363     config = config || {};
16364     if(config.tagName || config.dom || typeof config == "string"){ // element object
16365         config = {el: config, id: config.id || config};
16366     }
16367     this.initialConfig = config;
16368
16369     Roo.apply(this, config);
16370     this.addEvents({
16371         /**
16372          * @event disable
16373          * Fires after the component is disabled.
16374              * @param {Roo.Component} this
16375              */
16376         disable : true,
16377         /**
16378          * @event enable
16379          * Fires after the component is enabled.
16380              * @param {Roo.Component} this
16381              */
16382         enable : true,
16383         /**
16384          * @event beforeshow
16385          * Fires before the component is shown.  Return false to stop the show.
16386              * @param {Roo.Component} this
16387              */
16388         beforeshow : true,
16389         /**
16390          * @event show
16391          * Fires after the component is shown.
16392              * @param {Roo.Component} this
16393              */
16394         show : true,
16395         /**
16396          * @event beforehide
16397          * Fires before the component is hidden. Return false to stop the hide.
16398              * @param {Roo.Component} this
16399              */
16400         beforehide : true,
16401         /**
16402          * @event hide
16403          * Fires after the component is hidden.
16404              * @param {Roo.Component} this
16405              */
16406         hide : true,
16407         /**
16408          * @event beforerender
16409          * Fires before the component is rendered. Return false to stop the render.
16410              * @param {Roo.Component} this
16411              */
16412         beforerender : true,
16413         /**
16414          * @event render
16415          * Fires after the component is rendered.
16416              * @param {Roo.Component} this
16417              */
16418         render : true,
16419         /**
16420          * @event beforedestroy
16421          * Fires before the component is destroyed. Return false to stop the destroy.
16422              * @param {Roo.Component} this
16423              */
16424         beforedestroy : true,
16425         /**
16426          * @event destroy
16427          * Fires after the component is destroyed.
16428              * @param {Roo.Component} this
16429              */
16430         destroy : true
16431     });
16432     if(!this.id){
16433         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16434     }
16435     Roo.ComponentMgr.register(this);
16436     Roo.Component.superclass.constructor.call(this);
16437     this.initComponent();
16438     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16439         this.render(this.renderTo);
16440         delete this.renderTo;
16441     }
16442 };
16443
16444 /** @private */
16445 Roo.Component.AUTO_ID = 1000;
16446
16447 Roo.extend(Roo.Component, Roo.util.Observable, {
16448     /**
16449      * @scope Roo.Component.prototype
16450      * @type {Boolean}
16451      * true if this component is hidden. Read-only.
16452      */
16453     hidden : false,
16454     /**
16455      * @type {Boolean}
16456      * true if this component is disabled. Read-only.
16457      */
16458     disabled : false,
16459     /**
16460      * @type {Boolean}
16461      * true if this component has been rendered. Read-only.
16462      */
16463     rendered : false,
16464     
16465     /** @cfg {String} disableClass
16466      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16467      */
16468     disabledClass : "x-item-disabled",
16469         /** @cfg {Boolean} allowDomMove
16470          * Whether the component can move the Dom node when rendering (defaults to true).
16471          */
16472     allowDomMove : true,
16473     /** @cfg {String} hideMode (display|visibility)
16474      * How this component should hidden. Supported values are
16475      * "visibility" (css visibility), "offsets" (negative offset position) and
16476      * "display" (css display) - defaults to "display".
16477      */
16478     hideMode: 'display',
16479
16480     /** @private */
16481     ctype : "Roo.Component",
16482
16483     /**
16484      * @cfg {String} actionMode 
16485      * which property holds the element that used for  hide() / show() / disable() / enable()
16486      * default is 'el' for forms you probably want to set this to fieldEl 
16487      */
16488     actionMode : "el",
16489
16490     /** @private */
16491     getActionEl : function(){
16492         return this[this.actionMode];
16493     },
16494
16495     initComponent : Roo.emptyFn,
16496     /**
16497      * If this is a lazy rendering component, render it to its container element.
16498      * @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.
16499      */
16500     render : function(container, position){
16501         
16502         if(this.rendered){
16503             return this;
16504         }
16505         
16506         if(this.fireEvent("beforerender", this) === false){
16507             return false;
16508         }
16509         
16510         if(!container && this.el){
16511             this.el = Roo.get(this.el);
16512             container = this.el.dom.parentNode;
16513             this.allowDomMove = false;
16514         }
16515         this.container = Roo.get(container);
16516         this.rendered = true;
16517         if(position !== undefined){
16518             if(typeof position == 'number'){
16519                 position = this.container.dom.childNodes[position];
16520             }else{
16521                 position = Roo.getDom(position);
16522             }
16523         }
16524         this.onRender(this.container, position || null);
16525         if(this.cls){
16526             this.el.addClass(this.cls);
16527             delete this.cls;
16528         }
16529         if(this.style){
16530             this.el.applyStyles(this.style);
16531             delete this.style;
16532         }
16533         this.fireEvent("render", this);
16534         this.afterRender(this.container);
16535         if(this.hidden){
16536             this.hide();
16537         }
16538         if(this.disabled){
16539             this.disable();
16540         }
16541
16542         return this;
16543         
16544     },
16545
16546     /** @private */
16547     // default function is not really useful
16548     onRender : function(ct, position){
16549         if(this.el){
16550             this.el = Roo.get(this.el);
16551             if(this.allowDomMove !== false){
16552                 ct.dom.insertBefore(this.el.dom, position);
16553             }
16554         }
16555     },
16556
16557     /** @private */
16558     getAutoCreate : function(){
16559         var cfg = typeof this.autoCreate == "object" ?
16560                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16561         if(this.id && !cfg.id){
16562             cfg.id = this.id;
16563         }
16564         return cfg;
16565     },
16566
16567     /** @private */
16568     afterRender : Roo.emptyFn,
16569
16570     /**
16571      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16572      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16573      */
16574     destroy : function(){
16575         if(this.fireEvent("beforedestroy", this) !== false){
16576             this.purgeListeners();
16577             this.beforeDestroy();
16578             if(this.rendered){
16579                 this.el.removeAllListeners();
16580                 this.el.remove();
16581                 if(this.actionMode == "container"){
16582                     this.container.remove();
16583                 }
16584             }
16585             this.onDestroy();
16586             Roo.ComponentMgr.unregister(this);
16587             this.fireEvent("destroy", this);
16588         }
16589     },
16590
16591         /** @private */
16592     beforeDestroy : function(){
16593
16594     },
16595
16596         /** @private */
16597         onDestroy : function(){
16598
16599     },
16600
16601     /**
16602      * Returns the underlying {@link Roo.Element}.
16603      * @return {Roo.Element} The element
16604      */
16605     getEl : function(){
16606         return this.el;
16607     },
16608
16609     /**
16610      * Returns the id of this component.
16611      * @return {String}
16612      */
16613     getId : function(){
16614         return this.id;
16615     },
16616
16617     /**
16618      * Try to focus this component.
16619      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16620      * @return {Roo.Component} this
16621      */
16622     focus : function(selectText){
16623         if(this.rendered){
16624             this.el.focus();
16625             if(selectText === true){
16626                 this.el.dom.select();
16627             }
16628         }
16629         return this;
16630     },
16631
16632     /** @private */
16633     blur : function(){
16634         if(this.rendered){
16635             this.el.blur();
16636         }
16637         return this;
16638     },
16639
16640     /**
16641      * Disable this component.
16642      * @return {Roo.Component} this
16643      */
16644     disable : function(){
16645         if(this.rendered){
16646             this.onDisable();
16647         }
16648         this.disabled = true;
16649         this.fireEvent("disable", this);
16650         return this;
16651     },
16652
16653         // private
16654     onDisable : function(){
16655         this.getActionEl().addClass(this.disabledClass);
16656         this.el.dom.disabled = true;
16657     },
16658
16659     /**
16660      * Enable this component.
16661      * @return {Roo.Component} this
16662      */
16663     enable : function(){
16664         if(this.rendered){
16665             this.onEnable();
16666         }
16667         this.disabled = false;
16668         this.fireEvent("enable", this);
16669         return this;
16670     },
16671
16672         // private
16673     onEnable : function(){
16674         this.getActionEl().removeClass(this.disabledClass);
16675         this.el.dom.disabled = false;
16676     },
16677
16678     /**
16679      * Convenience function for setting disabled/enabled by boolean.
16680      * @param {Boolean} disabled
16681      */
16682     setDisabled : function(disabled){
16683         this[disabled ? "disable" : "enable"]();
16684     },
16685
16686     /**
16687      * Show this component.
16688      * @return {Roo.Component} this
16689      */
16690     show: function(){
16691         if(this.fireEvent("beforeshow", this) !== false){
16692             this.hidden = false;
16693             if(this.rendered){
16694                 this.onShow();
16695             }
16696             this.fireEvent("show", this);
16697         }
16698         return this;
16699     },
16700
16701     // private
16702     onShow : function(){
16703         var ae = this.getActionEl();
16704         if(this.hideMode == 'visibility'){
16705             ae.dom.style.visibility = "visible";
16706         }else if(this.hideMode == 'offsets'){
16707             ae.removeClass('x-hidden');
16708         }else{
16709             ae.dom.style.display = "";
16710         }
16711     },
16712
16713     /**
16714      * Hide this component.
16715      * @return {Roo.Component} this
16716      */
16717     hide: function(){
16718         if(this.fireEvent("beforehide", this) !== false){
16719             this.hidden = true;
16720             if(this.rendered){
16721                 this.onHide();
16722             }
16723             this.fireEvent("hide", this);
16724         }
16725         return this;
16726     },
16727
16728     // private
16729     onHide : function(){
16730         var ae = this.getActionEl();
16731         if(this.hideMode == 'visibility'){
16732             ae.dom.style.visibility = "hidden";
16733         }else if(this.hideMode == 'offsets'){
16734             ae.addClass('x-hidden');
16735         }else{
16736             ae.dom.style.display = "none";
16737         }
16738     },
16739
16740     /**
16741      * Convenience function to hide or show this component by boolean.
16742      * @param {Boolean} visible True to show, false to hide
16743      * @return {Roo.Component} this
16744      */
16745     setVisible: function(visible){
16746         if(visible) {
16747             this.show();
16748         }else{
16749             this.hide();
16750         }
16751         return this;
16752     },
16753
16754     /**
16755      * Returns true if this component is visible.
16756      */
16757     isVisible : function(){
16758         return this.getActionEl().isVisible();
16759     },
16760
16761     cloneConfig : function(overrides){
16762         overrides = overrides || {};
16763         var id = overrides.id || Roo.id();
16764         var cfg = Roo.applyIf(overrides, this.initialConfig);
16765         cfg.id = id; // prevent dup id
16766         return new this.constructor(cfg);
16767     }
16768 });/*
16769  * Based on:
16770  * Ext JS Library 1.1.1
16771  * Copyright(c) 2006-2007, Ext JS, LLC.
16772  *
16773  * Originally Released Under LGPL - original licence link has changed is not relivant.
16774  *
16775  * Fork - LGPL
16776  * <script type="text/javascript">
16777  */
16778
16779 /**
16780  * @class Roo.BoxComponent
16781  * @extends Roo.Component
16782  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16783  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16784  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16785  * layout containers.
16786  * @constructor
16787  * @param {Roo.Element/String/Object} config The configuration options.
16788  */
16789 Roo.BoxComponent = function(config){
16790     Roo.Component.call(this, config);
16791     this.addEvents({
16792         /**
16793          * @event resize
16794          * Fires after the component is resized.
16795              * @param {Roo.Component} this
16796              * @param {Number} adjWidth The box-adjusted width that was set
16797              * @param {Number} adjHeight The box-adjusted height that was set
16798              * @param {Number} rawWidth The width that was originally specified
16799              * @param {Number} rawHeight The height that was originally specified
16800              */
16801         resize : true,
16802         /**
16803          * @event move
16804          * Fires after the component is moved.
16805              * @param {Roo.Component} this
16806              * @param {Number} x The new x position
16807              * @param {Number} y The new y position
16808              */
16809         move : true
16810     });
16811 };
16812
16813 Roo.extend(Roo.BoxComponent, Roo.Component, {
16814     // private, set in afterRender to signify that the component has been rendered
16815     boxReady : false,
16816     // private, used to defer height settings to subclasses
16817     deferHeight: false,
16818     /** @cfg {Number} width
16819      * width (optional) size of component
16820      */
16821      /** @cfg {Number} height
16822      * height (optional) size of component
16823      */
16824      
16825     /**
16826      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16827      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16828      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16829      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16830      * @return {Roo.BoxComponent} this
16831      */
16832     setSize : function(w, h){
16833         // support for standard size objects
16834         if(typeof w == 'object'){
16835             h = w.height;
16836             w = w.width;
16837         }
16838         // not rendered
16839         if(!this.boxReady){
16840             this.width = w;
16841             this.height = h;
16842             return this;
16843         }
16844
16845         // prevent recalcs when not needed
16846         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16847             return this;
16848         }
16849         this.lastSize = {width: w, height: h};
16850
16851         var adj = this.adjustSize(w, h);
16852         var aw = adj.width, ah = adj.height;
16853         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16854             var rz = this.getResizeEl();
16855             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16856                 rz.setSize(aw, ah);
16857             }else if(!this.deferHeight && ah !== undefined){
16858                 rz.setHeight(ah);
16859             }else if(aw !== undefined){
16860                 rz.setWidth(aw);
16861             }
16862             this.onResize(aw, ah, w, h);
16863             this.fireEvent('resize', this, aw, ah, w, h);
16864         }
16865         return this;
16866     },
16867
16868     /**
16869      * Gets the current size of the component's underlying element.
16870      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16871      */
16872     getSize : function(){
16873         return this.el.getSize();
16874     },
16875
16876     /**
16877      * Gets the current XY position of the component's underlying element.
16878      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16879      * @return {Array} The XY position of the element (e.g., [100, 200])
16880      */
16881     getPosition : function(local){
16882         if(local === true){
16883             return [this.el.getLeft(true), this.el.getTop(true)];
16884         }
16885         return this.xy || this.el.getXY();
16886     },
16887
16888     /**
16889      * Gets the current box measurements of the component's underlying element.
16890      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16891      * @returns {Object} box An object in the format {x, y, width, height}
16892      */
16893     getBox : function(local){
16894         var s = this.el.getSize();
16895         if(local){
16896             s.x = this.el.getLeft(true);
16897             s.y = this.el.getTop(true);
16898         }else{
16899             var xy = this.xy || this.el.getXY();
16900             s.x = xy[0];
16901             s.y = xy[1];
16902         }
16903         return s;
16904     },
16905
16906     /**
16907      * Sets the current box measurements of the component's underlying element.
16908      * @param {Object} box An object in the format {x, y, width, height}
16909      * @returns {Roo.BoxComponent} this
16910      */
16911     updateBox : function(box){
16912         this.setSize(box.width, box.height);
16913         this.setPagePosition(box.x, box.y);
16914         return this;
16915     },
16916
16917     // protected
16918     getResizeEl : function(){
16919         return this.resizeEl || this.el;
16920     },
16921
16922     // protected
16923     getPositionEl : function(){
16924         return this.positionEl || this.el;
16925     },
16926
16927     /**
16928      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16929      * This method fires the move event.
16930      * @param {Number} left The new left
16931      * @param {Number} top The new top
16932      * @returns {Roo.BoxComponent} this
16933      */
16934     setPosition : function(x, y){
16935         this.x = x;
16936         this.y = y;
16937         if(!this.boxReady){
16938             return this;
16939         }
16940         var adj = this.adjustPosition(x, y);
16941         var ax = adj.x, ay = adj.y;
16942
16943         var el = this.getPositionEl();
16944         if(ax !== undefined || ay !== undefined){
16945             if(ax !== undefined && ay !== undefined){
16946                 el.setLeftTop(ax, ay);
16947             }else if(ax !== undefined){
16948                 el.setLeft(ax);
16949             }else if(ay !== undefined){
16950                 el.setTop(ay);
16951             }
16952             this.onPosition(ax, ay);
16953             this.fireEvent('move', this, ax, ay);
16954         }
16955         return this;
16956     },
16957
16958     /**
16959      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16960      * This method fires the move event.
16961      * @param {Number} x The new x position
16962      * @param {Number} y The new y position
16963      * @returns {Roo.BoxComponent} this
16964      */
16965     setPagePosition : function(x, y){
16966         this.pageX = x;
16967         this.pageY = y;
16968         if(!this.boxReady){
16969             return;
16970         }
16971         if(x === undefined || y === undefined){ // cannot translate undefined points
16972             return;
16973         }
16974         var p = this.el.translatePoints(x, y);
16975         this.setPosition(p.left, p.top);
16976         return this;
16977     },
16978
16979     // private
16980     onRender : function(ct, position){
16981         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16982         if(this.resizeEl){
16983             this.resizeEl = Roo.get(this.resizeEl);
16984         }
16985         if(this.positionEl){
16986             this.positionEl = Roo.get(this.positionEl);
16987         }
16988     },
16989
16990     // private
16991     afterRender : function(){
16992         Roo.BoxComponent.superclass.afterRender.call(this);
16993         this.boxReady = true;
16994         this.setSize(this.width, this.height);
16995         if(this.x || this.y){
16996             this.setPosition(this.x, this.y);
16997         }
16998         if(this.pageX || this.pageY){
16999             this.setPagePosition(this.pageX, this.pageY);
17000         }
17001     },
17002
17003     /**
17004      * Force the component's size to recalculate based on the underlying element's current height and width.
17005      * @returns {Roo.BoxComponent} this
17006      */
17007     syncSize : function(){
17008         delete this.lastSize;
17009         this.setSize(this.el.getWidth(), this.el.getHeight());
17010         return this;
17011     },
17012
17013     /**
17014      * Called after the component is resized, this method is empty by default but can be implemented by any
17015      * subclass that needs to perform custom logic after a resize occurs.
17016      * @param {Number} adjWidth The box-adjusted width that was set
17017      * @param {Number} adjHeight The box-adjusted height that was set
17018      * @param {Number} rawWidth The width that was originally specified
17019      * @param {Number} rawHeight The height that was originally specified
17020      */
17021     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17022
17023     },
17024
17025     /**
17026      * Called after the component is moved, this method is empty by default but can be implemented by any
17027      * subclass that needs to perform custom logic after a move occurs.
17028      * @param {Number} x The new x position
17029      * @param {Number} y The new y position
17030      */
17031     onPosition : function(x, y){
17032
17033     },
17034
17035     // private
17036     adjustSize : function(w, h){
17037         if(this.autoWidth){
17038             w = 'auto';
17039         }
17040         if(this.autoHeight){
17041             h = 'auto';
17042         }
17043         return {width : w, height: h};
17044     },
17045
17046     // private
17047     adjustPosition : function(x, y){
17048         return {x : x, y: y};
17049     }
17050 });/*
17051  * Based on:
17052  * Ext JS Library 1.1.1
17053  * Copyright(c) 2006-2007, Ext JS, LLC.
17054  *
17055  * Originally Released Under LGPL - original licence link has changed is not relivant.
17056  *
17057  * Fork - LGPL
17058  * <script type="text/javascript">
17059  */
17060  (function(){ 
17061 /**
17062  * @class Roo.Layer
17063  * @extends Roo.Element
17064  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17065  * automatic maintaining of shadow/shim positions.
17066  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17067  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17068  * you can pass a string with a CSS class name. False turns off the shadow.
17069  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17070  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17071  * @cfg {String} cls CSS class to add to the element
17072  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17073  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17074  * @constructor
17075  * @param {Object} config An object with config options.
17076  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17077  */
17078
17079 Roo.Layer = function(config, existingEl){
17080     config = config || {};
17081     var dh = Roo.DomHelper;
17082     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17083     if(existingEl){
17084         this.dom = Roo.getDom(existingEl);
17085     }
17086     if(!this.dom){
17087         var o = config.dh || {tag: "div", cls: "x-layer"};
17088         this.dom = dh.append(pel, o);
17089     }
17090     if(config.cls){
17091         this.addClass(config.cls);
17092     }
17093     this.constrain = config.constrain !== false;
17094     this.visibilityMode = Roo.Element.VISIBILITY;
17095     if(config.id){
17096         this.id = this.dom.id = config.id;
17097     }else{
17098         this.id = Roo.id(this.dom);
17099     }
17100     this.zindex = config.zindex || this.getZIndex();
17101     this.position("absolute", this.zindex);
17102     if(config.shadow){
17103         this.shadowOffset = config.shadowOffset || 4;
17104         this.shadow = new Roo.Shadow({
17105             offset : this.shadowOffset,
17106             mode : config.shadow
17107         });
17108     }else{
17109         this.shadowOffset = 0;
17110     }
17111     this.useShim = config.shim !== false && Roo.useShims;
17112     this.useDisplay = config.useDisplay;
17113     this.hide();
17114 };
17115
17116 var supr = Roo.Element.prototype;
17117
17118 // shims are shared among layer to keep from having 100 iframes
17119 var shims = [];
17120
17121 Roo.extend(Roo.Layer, Roo.Element, {
17122
17123     getZIndex : function(){
17124         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17125     },
17126
17127     getShim : function(){
17128         if(!this.useShim){
17129             return null;
17130         }
17131         if(this.shim){
17132             return this.shim;
17133         }
17134         var shim = shims.shift();
17135         if(!shim){
17136             shim = this.createShim();
17137             shim.enableDisplayMode('block');
17138             shim.dom.style.display = 'none';
17139             shim.dom.style.visibility = 'visible';
17140         }
17141         var pn = this.dom.parentNode;
17142         if(shim.dom.parentNode != pn){
17143             pn.insertBefore(shim.dom, this.dom);
17144         }
17145         shim.setStyle('z-index', this.getZIndex()-2);
17146         this.shim = shim;
17147         return shim;
17148     },
17149
17150     hideShim : function(){
17151         if(this.shim){
17152             this.shim.setDisplayed(false);
17153             shims.push(this.shim);
17154             delete this.shim;
17155         }
17156     },
17157
17158     disableShadow : function(){
17159         if(this.shadow){
17160             this.shadowDisabled = true;
17161             this.shadow.hide();
17162             this.lastShadowOffset = this.shadowOffset;
17163             this.shadowOffset = 0;
17164         }
17165     },
17166
17167     enableShadow : function(show){
17168         if(this.shadow){
17169             this.shadowDisabled = false;
17170             this.shadowOffset = this.lastShadowOffset;
17171             delete this.lastShadowOffset;
17172             if(show){
17173                 this.sync(true);
17174             }
17175         }
17176     },
17177
17178     // private
17179     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17180     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17181     sync : function(doShow){
17182         var sw = this.shadow;
17183         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17184             var sh = this.getShim();
17185
17186             var w = this.getWidth(),
17187                 h = this.getHeight();
17188
17189             var l = this.getLeft(true),
17190                 t = this.getTop(true);
17191
17192             if(sw && !this.shadowDisabled){
17193                 if(doShow && !sw.isVisible()){
17194                     sw.show(this);
17195                 }else{
17196                     sw.realign(l, t, w, h);
17197                 }
17198                 if(sh){
17199                     if(doShow){
17200                        sh.show();
17201                     }
17202                     // fit the shim behind the shadow, so it is shimmed too
17203                     var a = sw.adjusts, s = sh.dom.style;
17204                     s.left = (Math.min(l, l+a.l))+"px";
17205                     s.top = (Math.min(t, t+a.t))+"px";
17206                     s.width = (w+a.w)+"px";
17207                     s.height = (h+a.h)+"px";
17208                 }
17209             }else if(sh){
17210                 if(doShow){
17211                    sh.show();
17212                 }
17213                 sh.setSize(w, h);
17214                 sh.setLeftTop(l, t);
17215             }
17216             
17217         }
17218     },
17219
17220     // private
17221     destroy : function(){
17222         this.hideShim();
17223         if(this.shadow){
17224             this.shadow.hide();
17225         }
17226         this.removeAllListeners();
17227         var pn = this.dom.parentNode;
17228         if(pn){
17229             pn.removeChild(this.dom);
17230         }
17231         Roo.Element.uncache(this.id);
17232     },
17233
17234     remove : function(){
17235         this.destroy();
17236     },
17237
17238     // private
17239     beginUpdate : function(){
17240         this.updating = true;
17241     },
17242
17243     // private
17244     endUpdate : function(){
17245         this.updating = false;
17246         this.sync(true);
17247     },
17248
17249     // private
17250     hideUnders : function(negOffset){
17251         if(this.shadow){
17252             this.shadow.hide();
17253         }
17254         this.hideShim();
17255     },
17256
17257     // private
17258     constrainXY : function(){
17259         if(this.constrain){
17260             var vw = Roo.lib.Dom.getViewWidth(),
17261                 vh = Roo.lib.Dom.getViewHeight();
17262             var s = Roo.get(document).getScroll();
17263
17264             var xy = this.getXY();
17265             var x = xy[0], y = xy[1];   
17266             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17267             // only move it if it needs it
17268             var moved = false;
17269             // first validate right/bottom
17270             if((x + w) > vw+s.left){
17271                 x = vw - w - this.shadowOffset;
17272                 moved = true;
17273             }
17274             if((y + h) > vh+s.top){
17275                 y = vh - h - this.shadowOffset;
17276                 moved = true;
17277             }
17278             // then make sure top/left isn't negative
17279             if(x < s.left){
17280                 x = s.left;
17281                 moved = true;
17282             }
17283             if(y < s.top){
17284                 y = s.top;
17285                 moved = true;
17286             }
17287             if(moved){
17288                 if(this.avoidY){
17289                     var ay = this.avoidY;
17290                     if(y <= ay && (y+h) >= ay){
17291                         y = ay-h-5;   
17292                     }
17293                 }
17294                 xy = [x, y];
17295                 this.storeXY(xy);
17296                 supr.setXY.call(this, xy);
17297                 this.sync();
17298             }
17299         }
17300     },
17301
17302     isVisible : function(){
17303         return this.visible;    
17304     },
17305
17306     // private
17307     showAction : function(){
17308         this.visible = true; // track visibility to prevent getStyle calls
17309         if(this.useDisplay === true){
17310             this.setDisplayed("");
17311         }else if(this.lastXY){
17312             supr.setXY.call(this, this.lastXY);
17313         }else if(this.lastLT){
17314             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17315         }
17316     },
17317
17318     // private
17319     hideAction : function(){
17320         this.visible = false;
17321         if(this.useDisplay === true){
17322             this.setDisplayed(false);
17323         }else{
17324             this.setLeftTop(-10000,-10000);
17325         }
17326     },
17327
17328     // overridden Element method
17329     setVisible : function(v, a, d, c, e){
17330         if(v){
17331             this.showAction();
17332         }
17333         if(a && v){
17334             var cb = function(){
17335                 this.sync(true);
17336                 if(c){
17337                     c();
17338                 }
17339             }.createDelegate(this);
17340             supr.setVisible.call(this, true, true, d, cb, e);
17341         }else{
17342             if(!v){
17343                 this.hideUnders(true);
17344             }
17345             var cb = c;
17346             if(a){
17347                 cb = function(){
17348                     this.hideAction();
17349                     if(c){
17350                         c();
17351                     }
17352                 }.createDelegate(this);
17353             }
17354             supr.setVisible.call(this, v, a, d, cb, e);
17355             if(v){
17356                 this.sync(true);
17357             }else if(!a){
17358                 this.hideAction();
17359             }
17360         }
17361     },
17362
17363     storeXY : function(xy){
17364         delete this.lastLT;
17365         this.lastXY = xy;
17366     },
17367
17368     storeLeftTop : function(left, top){
17369         delete this.lastXY;
17370         this.lastLT = [left, top];
17371     },
17372
17373     // private
17374     beforeFx : function(){
17375         this.beforeAction();
17376         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17377     },
17378
17379     // private
17380     afterFx : function(){
17381         Roo.Layer.superclass.afterFx.apply(this, arguments);
17382         this.sync(this.isVisible());
17383     },
17384
17385     // private
17386     beforeAction : function(){
17387         if(!this.updating && this.shadow){
17388             this.shadow.hide();
17389         }
17390     },
17391
17392     // overridden Element method
17393     setLeft : function(left){
17394         this.storeLeftTop(left, this.getTop(true));
17395         supr.setLeft.apply(this, arguments);
17396         this.sync();
17397     },
17398
17399     setTop : function(top){
17400         this.storeLeftTop(this.getLeft(true), top);
17401         supr.setTop.apply(this, arguments);
17402         this.sync();
17403     },
17404
17405     setLeftTop : function(left, top){
17406         this.storeLeftTop(left, top);
17407         supr.setLeftTop.apply(this, arguments);
17408         this.sync();
17409     },
17410
17411     setXY : function(xy, a, d, c, e){
17412         this.fixDisplay();
17413         this.beforeAction();
17414         this.storeXY(xy);
17415         var cb = this.createCB(c);
17416         supr.setXY.call(this, xy, a, d, cb, e);
17417         if(!a){
17418             cb();
17419         }
17420     },
17421
17422     // private
17423     createCB : function(c){
17424         var el = this;
17425         return function(){
17426             el.constrainXY();
17427             el.sync(true);
17428             if(c){
17429                 c();
17430             }
17431         };
17432     },
17433
17434     // overridden Element method
17435     setX : function(x, a, d, c, e){
17436         this.setXY([x, this.getY()], a, d, c, e);
17437     },
17438
17439     // overridden Element method
17440     setY : function(y, a, d, c, e){
17441         this.setXY([this.getX(), y], a, d, c, e);
17442     },
17443
17444     // overridden Element method
17445     setSize : function(w, h, a, d, c, e){
17446         this.beforeAction();
17447         var cb = this.createCB(c);
17448         supr.setSize.call(this, w, h, a, d, cb, e);
17449         if(!a){
17450             cb();
17451         }
17452     },
17453
17454     // overridden Element method
17455     setWidth : function(w, a, d, c, e){
17456         this.beforeAction();
17457         var cb = this.createCB(c);
17458         supr.setWidth.call(this, w, a, d, cb, e);
17459         if(!a){
17460             cb();
17461         }
17462     },
17463
17464     // overridden Element method
17465     setHeight : function(h, a, d, c, e){
17466         this.beforeAction();
17467         var cb = this.createCB(c);
17468         supr.setHeight.call(this, h, a, d, cb, e);
17469         if(!a){
17470             cb();
17471         }
17472     },
17473
17474     // overridden Element method
17475     setBounds : function(x, y, w, h, a, d, c, e){
17476         this.beforeAction();
17477         var cb = this.createCB(c);
17478         if(!a){
17479             this.storeXY([x, y]);
17480             supr.setXY.call(this, [x, y]);
17481             supr.setSize.call(this, w, h, a, d, cb, e);
17482             cb();
17483         }else{
17484             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17485         }
17486         return this;
17487     },
17488     
17489     /**
17490      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17491      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17492      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17493      * @param {Number} zindex The new z-index to set
17494      * @return {this} The Layer
17495      */
17496     setZIndex : function(zindex){
17497         this.zindex = zindex;
17498         this.setStyle("z-index", zindex + 2);
17499         if(this.shadow){
17500             this.shadow.setZIndex(zindex + 1);
17501         }
17502         if(this.shim){
17503             this.shim.setStyle("z-index", zindex);
17504         }
17505     }
17506 });
17507 })();/*
17508  * Original code for Roojs - LGPL
17509  * <script type="text/javascript">
17510  */
17511  
17512 /**
17513  * @class Roo.XComponent
17514  * A delayed Element creator...
17515  * Or a way to group chunks of interface together.
17516  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17517  *  used in conjunction with XComponent.build() it will create an instance of each element,
17518  *  then call addxtype() to build the User interface.
17519  * 
17520  * Mypart.xyx = new Roo.XComponent({
17521
17522     parent : 'Mypart.xyz', // empty == document.element.!!
17523     order : '001',
17524     name : 'xxxx'
17525     region : 'xxxx'
17526     disabled : function() {} 
17527      
17528     tree : function() { // return an tree of xtype declared components
17529         var MODULE = this;
17530         return 
17531         {
17532             xtype : 'NestedLayoutPanel',
17533             // technicall
17534         }
17535      ]
17536  *})
17537  *
17538  *
17539  * It can be used to build a big heiracy, with parent etc.
17540  * or you can just use this to render a single compoent to a dom element
17541  * MYPART.render(Roo.Element | String(id) | dom_element )
17542  *
17543  *
17544  * Usage patterns.
17545  *
17546  * Classic Roo
17547  *
17548  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17549  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17550  *
17551  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17552  *
17553  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17554  * - if mulitple topModules exist, the last one is defined as the top module.
17555  *
17556  * Embeded Roo
17557  * 
17558  * When the top level or multiple modules are to embedded into a existing HTML page,
17559  * the parent element can container '#id' of the element where the module will be drawn.
17560  *
17561  * Bootstrap Roo
17562  *
17563  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17564  * it relies more on a include mechanism, where sub modules are included into an outer page.
17565  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17566  * 
17567  * Bootstrap Roo Included elements
17568  *
17569  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17570  * hence confusing the component builder as it thinks there are multiple top level elements. 
17571  *
17572  * String Over-ride & Translations
17573  *
17574  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17575  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17576  * are needed. @see Roo.XComponent.overlayString  
17577  * 
17578  * 
17579  * 
17580  * @extends Roo.util.Observable
17581  * @constructor
17582  * @param cfg {Object} configuration of component
17583  * 
17584  */
17585 Roo.XComponent = function(cfg) {
17586     Roo.apply(this, cfg);
17587     this.addEvents({ 
17588         /**
17589              * @event built
17590              * Fires when this the componnt is built
17591              * @param {Roo.XComponent} c the component
17592              */
17593         'built' : true
17594         
17595     });
17596     this.region = this.region || 'center'; // default..
17597     Roo.XComponent.register(this);
17598     this.modules = false;
17599     this.el = false; // where the layout goes..
17600     
17601     
17602 }
17603 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17604     /**
17605      * @property el
17606      * The created element (with Roo.factory())
17607      * @type {Roo.Layout}
17608      */
17609     el  : false,
17610     
17611     /**
17612      * @property el
17613      * for BC  - use el in new code
17614      * @type {Roo.Layout}
17615      */
17616     panel : false,
17617     
17618     /**
17619      * @property layout
17620      * for BC  - use el in new code
17621      * @type {Roo.Layout}
17622      */
17623     layout : false,
17624     
17625      /**
17626      * @cfg {Function|boolean} disabled
17627      * If this module is disabled by some rule, return true from the funtion
17628      */
17629     disabled : false,
17630     
17631     /**
17632      * @cfg {String} parent 
17633      * Name of parent element which it get xtype added to..
17634      */
17635     parent: false,
17636     
17637     /**
17638      * @cfg {String} order
17639      * Used to set the order in which elements are created (usefull for multiple tabs)
17640      */
17641     
17642     order : false,
17643     /**
17644      * @cfg {String} name
17645      * String to display while loading.
17646      */
17647     name : false,
17648     /**
17649      * @cfg {String} region
17650      * Region to render component to (defaults to center)
17651      */
17652     region : 'center',
17653     
17654     /**
17655      * @cfg {Array} items
17656      * A single item array - the first element is the root of the tree..
17657      * It's done this way to stay compatible with the Xtype system...
17658      */
17659     items : false,
17660     
17661     /**
17662      * @property _tree
17663      * The method that retuns the tree of parts that make up this compoennt 
17664      * @type {function}
17665      */
17666     _tree  : false,
17667     
17668      /**
17669      * render
17670      * render element to dom or tree
17671      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17672      */
17673     
17674     render : function(el)
17675     {
17676         
17677         el = el || false;
17678         var hp = this.parent ? 1 : 0;
17679         Roo.debug &&  Roo.log(this);
17680         
17681         var tree = this._tree ? this._tree() : this.tree();
17682
17683         
17684         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17685             // if parent is a '#.....' string, then let's use that..
17686             var ename = this.parent.substr(1);
17687             this.parent = false;
17688             Roo.debug && Roo.log(ename);
17689             switch (ename) {
17690                 case 'bootstrap-body':
17691                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17692                         // this is the BorderLayout standard?
17693                        this.parent = { el : true };
17694                        break;
17695                     }
17696                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17697                         // need to insert stuff...
17698                         this.parent =  {
17699                              el : new Roo.bootstrap.layout.Border({
17700                                  el : document.body, 
17701                      
17702                                  center: {
17703                                     titlebar: false,
17704                                     autoScroll:false,
17705                                     closeOnTab: true,
17706                                     tabPosition: 'top',
17707                                       //resizeTabs: true,
17708                                     alwaysShowTabs: true,
17709                                     hideTabs: false
17710                                      //minTabWidth: 140
17711                                  }
17712                              })
17713                         
17714                          };
17715                          break;
17716                     }
17717                          
17718                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17719                         this.parent = { el :  new  Roo.bootstrap.Body() };
17720                         Roo.debug && Roo.log("setting el to doc body");
17721                          
17722                     } else {
17723                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17724                     }
17725                     break;
17726                 case 'bootstrap':
17727                     this.parent = { el : true};
17728                     // fall through
17729                 default:
17730                     el = Roo.get(ename);
17731                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17732                         this.parent = { el : true};
17733                     }
17734                     
17735                     break;
17736             }
17737                 
17738             
17739             if (!el && !this.parent) {
17740                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17741                 return;
17742             }
17743         }
17744         
17745         Roo.debug && Roo.log("EL:");
17746         Roo.debug && Roo.log(el);
17747         Roo.debug && Roo.log("this.parent.el:");
17748         Roo.debug && Roo.log(this.parent.el);
17749         
17750
17751         // altertive root elements ??? - we need a better way to indicate these.
17752         var is_alt = Roo.XComponent.is_alt ||
17753                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17754                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17755                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17756         
17757         
17758         
17759         if (!this.parent && is_alt) {
17760             //el = Roo.get(document.body);
17761             this.parent = { el : true };
17762         }
17763             
17764             
17765         
17766         if (!this.parent) {
17767             
17768             Roo.debug && Roo.log("no parent - creating one");
17769             
17770             el = el ? Roo.get(el) : false;      
17771             
17772             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17773                 
17774                 this.parent =  {
17775                     el : new Roo.bootstrap.layout.Border({
17776                         el: el || document.body,
17777                     
17778                         center: {
17779                             titlebar: false,
17780                             autoScroll:false,
17781                             closeOnTab: true,
17782                             tabPosition: 'top',
17783                              //resizeTabs: true,
17784                             alwaysShowTabs: false,
17785                             hideTabs: true,
17786                             minTabWidth: 140,
17787                             overflow: 'visible'
17788                          }
17789                      })
17790                 };
17791             } else {
17792             
17793                 // it's a top level one..
17794                 this.parent =  {
17795                     el : new Roo.BorderLayout(el || document.body, {
17796                         center: {
17797                             titlebar: false,
17798                             autoScroll:false,
17799                             closeOnTab: true,
17800                             tabPosition: 'top',
17801                              //resizeTabs: true,
17802                             alwaysShowTabs: el && hp? false :  true,
17803                             hideTabs: el || !hp ? true :  false,
17804                             minTabWidth: 140
17805                          }
17806                     })
17807                 };
17808             }
17809         }
17810         
17811         if (!this.parent.el) {
17812                 // probably an old style ctor, which has been disabled.
17813                 return;
17814
17815         }
17816                 // The 'tree' method is  '_tree now' 
17817             
17818         tree.region = tree.region || this.region;
17819         var is_body = false;
17820         if (this.parent.el === true) {
17821             // bootstrap... - body..
17822             if (el) {
17823                 tree.el = el;
17824             }
17825             this.parent.el = Roo.factory(tree);
17826             is_body = true;
17827         }
17828         
17829         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17830         this.fireEvent('built', this);
17831         
17832         this.panel = this.el;
17833         this.layout = this.panel.layout;
17834         this.parentLayout = this.parent.layout  || false;  
17835          
17836     }
17837     
17838 });
17839
17840 Roo.apply(Roo.XComponent, {
17841     /**
17842      * @property  hideProgress
17843      * true to disable the building progress bar.. usefull on single page renders.
17844      * @type Boolean
17845      */
17846     hideProgress : false,
17847     /**
17848      * @property  buildCompleted
17849      * True when the builder has completed building the interface.
17850      * @type Boolean
17851      */
17852     buildCompleted : false,
17853      
17854     /**
17855      * @property  topModule
17856      * the upper most module - uses document.element as it's constructor.
17857      * @type Object
17858      */
17859      
17860     topModule  : false,
17861       
17862     /**
17863      * @property  modules
17864      * array of modules to be created by registration system.
17865      * @type {Array} of Roo.XComponent
17866      */
17867     
17868     modules : [],
17869     /**
17870      * @property  elmodules
17871      * array of modules to be created by which use #ID 
17872      * @type {Array} of Roo.XComponent
17873      */
17874      
17875     elmodules : [],
17876
17877      /**
17878      * @property  is_alt
17879      * Is an alternative Root - normally used by bootstrap or other systems,
17880      *    where the top element in the tree can wrap 'body' 
17881      * @type {boolean}  (default false)
17882      */
17883      
17884     is_alt : false,
17885     /**
17886      * @property  build_from_html
17887      * Build elements from html - used by bootstrap HTML stuff 
17888      *    - this is cleared after build is completed
17889      * @type {boolean}    (default false)
17890      */
17891      
17892     build_from_html : false,
17893     /**
17894      * Register components to be built later.
17895      *
17896      * This solves the following issues
17897      * - Building is not done on page load, but after an authentication process has occured.
17898      * - Interface elements are registered on page load
17899      * - Parent Interface elements may not be loaded before child, so this handles that..
17900      * 
17901      *
17902      * example:
17903      * 
17904      * MyApp.register({
17905           order : '000001',
17906           module : 'Pman.Tab.projectMgr',
17907           region : 'center',
17908           parent : 'Pman.layout',
17909           disabled : false,  // or use a function..
17910         })
17911      
17912      * * @param {Object} details about module
17913      */
17914     register : function(obj) {
17915                 
17916         Roo.XComponent.event.fireEvent('register', obj);
17917         switch(typeof(obj.disabled) ) {
17918                 
17919             case 'undefined':
17920                 break;
17921             
17922             case 'function':
17923                 if ( obj.disabled() ) {
17924                         return;
17925                 }
17926                 break;
17927             
17928             default:
17929                 if (obj.disabled || obj.region == '#disabled') {
17930                         return;
17931                 }
17932                 break;
17933         }
17934                 
17935         this.modules.push(obj);
17936          
17937     },
17938     /**
17939      * convert a string to an object..
17940      * eg. 'AAA.BBB' -> finds AAA.BBB
17941
17942      */
17943     
17944     toObject : function(str)
17945     {
17946         if (!str || typeof(str) == 'object') {
17947             return str;
17948         }
17949         if (str.substring(0,1) == '#') {
17950             return str;
17951         }
17952
17953         var ar = str.split('.');
17954         var rt, o;
17955         rt = ar.shift();
17956             /** eval:var:o */
17957         try {
17958             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17959         } catch (e) {
17960             throw "Module not found : " + str;
17961         }
17962         
17963         if (o === false) {
17964             throw "Module not found : " + str;
17965         }
17966         Roo.each(ar, function(e) {
17967             if (typeof(o[e]) == 'undefined') {
17968                 throw "Module not found : " + str;
17969             }
17970             o = o[e];
17971         });
17972         
17973         return o;
17974         
17975     },
17976     
17977     
17978     /**
17979      * move modules into their correct place in the tree..
17980      * 
17981      */
17982     preBuild : function ()
17983     {
17984         var _t = this;
17985         Roo.each(this.modules , function (obj)
17986         {
17987             Roo.XComponent.event.fireEvent('beforebuild', obj);
17988             
17989             var opar = obj.parent;
17990             try { 
17991                 obj.parent = this.toObject(opar);
17992             } catch(e) {
17993                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17994                 return;
17995             }
17996             
17997             if (!obj.parent) {
17998                 Roo.debug && Roo.log("GOT top level module");
17999                 Roo.debug && Roo.log(obj);
18000                 obj.modules = new Roo.util.MixedCollection(false, 
18001                     function(o) { return o.order + '' }
18002                 );
18003                 this.topModule = obj;
18004                 return;
18005             }
18006                         // parent is a string (usually a dom element name..)
18007             if (typeof(obj.parent) == 'string') {
18008                 this.elmodules.push(obj);
18009                 return;
18010             }
18011             if (obj.parent.constructor != Roo.XComponent) {
18012                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18013             }
18014             if (!obj.parent.modules) {
18015                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18016                     function(o) { return o.order + '' }
18017                 );
18018             }
18019             if (obj.parent.disabled) {
18020                 obj.disabled = true;
18021             }
18022             obj.parent.modules.add(obj);
18023         }, this);
18024     },
18025     
18026      /**
18027      * make a list of modules to build.
18028      * @return {Array} list of modules. 
18029      */ 
18030     
18031     buildOrder : function()
18032     {
18033         var _this = this;
18034         var cmp = function(a,b) {   
18035             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18036         };
18037         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18038             throw "No top level modules to build";
18039         }
18040         
18041         // make a flat list in order of modules to build.
18042         var mods = this.topModule ? [ this.topModule ] : [];
18043                 
18044         
18045         // elmodules (is a list of DOM based modules )
18046         Roo.each(this.elmodules, function(e) {
18047             mods.push(e);
18048             if (!this.topModule &&
18049                 typeof(e.parent) == 'string' &&
18050                 e.parent.substring(0,1) == '#' &&
18051                 Roo.get(e.parent.substr(1))
18052                ) {
18053                 
18054                 _this.topModule = e;
18055             }
18056             
18057         });
18058
18059         
18060         // add modules to their parents..
18061         var addMod = function(m) {
18062             Roo.debug && Roo.log("build Order: add: " + m.name);
18063                 
18064             mods.push(m);
18065             if (m.modules && !m.disabled) {
18066                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18067                 m.modules.keySort('ASC',  cmp );
18068                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18069     
18070                 m.modules.each(addMod);
18071             } else {
18072                 Roo.debug && Roo.log("build Order: no child modules");
18073             }
18074             // not sure if this is used any more..
18075             if (m.finalize) {
18076                 m.finalize.name = m.name + " (clean up) ";
18077                 mods.push(m.finalize);
18078             }
18079             
18080         }
18081         if (this.topModule && this.topModule.modules) { 
18082             this.topModule.modules.keySort('ASC',  cmp );
18083             this.topModule.modules.each(addMod);
18084         } 
18085         return mods;
18086     },
18087     
18088      /**
18089      * Build the registered modules.
18090      * @param {Object} parent element.
18091      * @param {Function} optional method to call after module has been added.
18092      * 
18093      */ 
18094    
18095     build : function(opts) 
18096     {
18097         
18098         if (typeof(opts) != 'undefined') {
18099             Roo.apply(this,opts);
18100         }
18101         
18102         this.preBuild();
18103         var mods = this.buildOrder();
18104       
18105         //this.allmods = mods;
18106         //Roo.debug && Roo.log(mods);
18107         //return;
18108         if (!mods.length) { // should not happen
18109             throw "NO modules!!!";
18110         }
18111         
18112         
18113         var msg = "Building Interface...";
18114         // flash it up as modal - so we store the mask!?
18115         if (!this.hideProgress && Roo.MessageBox) {
18116             Roo.MessageBox.show({ title: 'loading' });
18117             Roo.MessageBox.show({
18118                title: "Please wait...",
18119                msg: msg,
18120                width:450,
18121                progress:true,
18122                buttons : false,
18123                closable:false,
18124                modal: false
18125               
18126             });
18127         }
18128         var total = mods.length;
18129         
18130         var _this = this;
18131         var progressRun = function() {
18132             if (!mods.length) {
18133                 Roo.debug && Roo.log('hide?');
18134                 if (!this.hideProgress && Roo.MessageBox) {
18135                     Roo.MessageBox.hide();
18136                 }
18137                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18138                 
18139                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18140                 
18141                 // THE END...
18142                 return false;   
18143             }
18144             
18145             var m = mods.shift();
18146             
18147             
18148             Roo.debug && Roo.log(m);
18149             // not sure if this is supported any more.. - modules that are are just function
18150             if (typeof(m) == 'function') { 
18151                 m.call(this);
18152                 return progressRun.defer(10, _this);
18153             } 
18154             
18155             
18156             msg = "Building Interface " + (total  - mods.length) + 
18157                     " of " + total + 
18158                     (m.name ? (' - ' + m.name) : '');
18159                         Roo.debug && Roo.log(msg);
18160             if (!_this.hideProgress &&  Roo.MessageBox) { 
18161                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18162             }
18163             
18164          
18165             // is the module disabled?
18166             var disabled = (typeof(m.disabled) == 'function') ?
18167                 m.disabled.call(m.module.disabled) : m.disabled;    
18168             
18169             
18170             if (disabled) {
18171                 return progressRun(); // we do not update the display!
18172             }
18173             
18174             // now build 
18175             
18176                         
18177                         
18178             m.render();
18179             // it's 10 on top level, and 1 on others??? why...
18180             return progressRun.defer(10, _this);
18181              
18182         }
18183         progressRun.defer(1, _this);
18184      
18185         
18186         
18187     },
18188     /**
18189      * Overlay a set of modified strings onto a component
18190      * This is dependant on our builder exporting the strings and 'named strings' elements.
18191      * 
18192      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18193      * @param {Object} associative array of 'named' string and it's new value.
18194      * 
18195      */
18196         overlayStrings : function( component, strings )
18197     {
18198         if (typeof(component['_named_strings']) == 'undefined') {
18199             throw "ERROR: component does not have _named_strings";
18200         }
18201         for ( var k in strings ) {
18202             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18203             if (md !== false) {
18204                 component['_strings'][md] = strings[k];
18205             } else {
18206                 Roo.log('could not find named string: ' + k + ' in');
18207                 Roo.log(component);
18208             }
18209             
18210         }
18211         
18212     },
18213     
18214         
18215         /**
18216          * Event Object.
18217          *
18218          *
18219          */
18220         event: false, 
18221     /**
18222          * wrapper for event.on - aliased later..  
18223          * Typically use to register a event handler for register:
18224          *
18225          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18226          *
18227          */
18228     on : false
18229    
18230     
18231     
18232 });
18233
18234 Roo.XComponent.event = new Roo.util.Observable({
18235                 events : { 
18236                         /**
18237                          * @event register
18238                          * Fires when an Component is registered,
18239                          * set the disable property on the Component to stop registration.
18240                          * @param {Roo.XComponent} c the component being registerd.
18241                          * 
18242                          */
18243                         'register' : true,
18244             /**
18245                          * @event beforebuild
18246                          * Fires before each Component is built
18247                          * can be used to apply permissions.
18248                          * @param {Roo.XComponent} c the component being registerd.
18249                          * 
18250                          */
18251                         'beforebuild' : true,
18252                         /**
18253                          * @event buildcomplete
18254                          * Fires on the top level element when all elements have been built
18255                          * @param {Roo.XComponent} the top level component.
18256                          */
18257                         'buildcomplete' : true
18258                         
18259                 }
18260 });
18261
18262 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18263  //
18264  /**
18265  * marked - a markdown parser
18266  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18267  * https://github.com/chjj/marked
18268  */
18269
18270
18271 /**
18272  *
18273  * Roo.Markdown - is a very crude wrapper around marked..
18274  *
18275  * usage:
18276  * 
18277  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18278  * 
18279  * Note: move the sample code to the bottom of this
18280  * file before uncommenting it.
18281  *
18282  */
18283
18284 Roo.Markdown = {};
18285 Roo.Markdown.toHtml = function(text) {
18286     
18287     var c = new Roo.Markdown.marked.setOptions({
18288             renderer: new Roo.Markdown.marked.Renderer(),
18289             gfm: true,
18290             tables: true,
18291             breaks: false,
18292             pedantic: false,
18293             sanitize: false,
18294             smartLists: true,
18295             smartypants: false
18296           });
18297     // A FEW HACKS!!?
18298     
18299     text = text.replace(/\\\n/g,' ');
18300     return Roo.Markdown.marked(text);
18301 };
18302 //
18303 // converter
18304 //
18305 // Wraps all "globals" so that the only thing
18306 // exposed is makeHtml().
18307 //
18308 (function() {
18309     
18310      /**
18311          * eval:var:escape
18312          * eval:var:unescape
18313          * eval:var:replace
18314          */
18315       
18316     /**
18317      * Helpers
18318      */
18319     
18320     var escape = function (html, encode) {
18321       return html
18322         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18323         .replace(/</g, '&lt;')
18324         .replace(/>/g, '&gt;')
18325         .replace(/"/g, '&quot;')
18326         .replace(/'/g, '&#39;');
18327     }
18328     
18329     var unescape = function (html) {
18330         // explicitly match decimal, hex, and named HTML entities 
18331       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18332         n = n.toLowerCase();
18333         if (n === 'colon') { return ':'; }
18334         if (n.charAt(0) === '#') {
18335           return n.charAt(1) === 'x'
18336             ? String.fromCharCode(parseInt(n.substring(2), 16))
18337             : String.fromCharCode(+n.substring(1));
18338         }
18339         return '';
18340       });
18341     }
18342     
18343     var replace = function (regex, opt) {
18344       regex = regex.source;
18345       opt = opt || '';
18346       return function self(name, val) {
18347         if (!name) { return new RegExp(regex, opt); }
18348         val = val.source || val;
18349         val = val.replace(/(^|[^\[])\^/g, '$1');
18350         regex = regex.replace(name, val);
18351         return self;
18352       };
18353     }
18354
18355
18356          /**
18357          * eval:var:noop
18358     */
18359     var noop = function () {}
18360     noop.exec = noop;
18361     
18362          /**
18363          * eval:var:merge
18364     */
18365     var merge = function (obj) {
18366       var i = 1
18367         , target
18368         , key;
18369     
18370       for (; i < arguments.length; i++) {
18371         target = arguments[i];
18372         for (key in target) {
18373           if (Object.prototype.hasOwnProperty.call(target, key)) {
18374             obj[key] = target[key];
18375           }
18376         }
18377       }
18378     
18379       return obj;
18380     }
18381     
18382     
18383     /**
18384      * Block-Level Grammar
18385      */
18386     
18387     
18388     
18389     
18390     var block = {
18391       newline: /^\n+/,
18392       code: /^( {4}[^\n]+\n*)+/,
18393       fences: noop,
18394       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18395       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18396       nptable: noop,
18397       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18398       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18399       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18400       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18401       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18402       table: noop,
18403       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18404       text: /^[^\n]+/
18405     };
18406     
18407     block.bullet = /(?:[*+-]|\d+\.)/;
18408     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18409     block.item = replace(block.item, 'gm')
18410       (/bull/g, block.bullet)
18411       ();
18412     
18413     block.list = replace(block.list)
18414       (/bull/g, block.bullet)
18415       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18416       ('def', '\\n+(?=' + block.def.source + ')')
18417       ();
18418     
18419     block.blockquote = replace(block.blockquote)
18420       ('def', block.def)
18421       ();
18422     
18423     block._tag = '(?!(?:'
18424       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18425       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18426       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18427     
18428     block.html = replace(block.html)
18429       ('comment', /<!--[\s\S]*?-->/)
18430       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18431       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18432       (/tag/g, block._tag)
18433       ();
18434     
18435     block.paragraph = replace(block.paragraph)
18436       ('hr', block.hr)
18437       ('heading', block.heading)
18438       ('lheading', block.lheading)
18439       ('blockquote', block.blockquote)
18440       ('tag', '<' + block._tag)
18441       ('def', block.def)
18442       ();
18443     
18444     /**
18445      * Normal Block Grammar
18446      */
18447     
18448     block.normal = merge({}, block);
18449     
18450     /**
18451      * GFM Block Grammar
18452      */
18453     
18454     block.gfm = merge({}, block.normal, {
18455       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18456       paragraph: /^/,
18457       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18458     });
18459     
18460     block.gfm.paragraph = replace(block.paragraph)
18461       ('(?!', '(?!'
18462         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18463         + block.list.source.replace('\\1', '\\3') + '|')
18464       ();
18465     
18466     /**
18467      * GFM + Tables Block Grammar
18468      */
18469     
18470     block.tables = merge({}, block.gfm, {
18471       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18472       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18473     });
18474     
18475     /**
18476      * Block Lexer
18477      */
18478     
18479     var Lexer = function (options) {
18480       this.tokens = [];
18481       this.tokens.links = {};
18482       this.options = options || marked.defaults;
18483       this.rules = block.normal;
18484     
18485       if (this.options.gfm) {
18486         if (this.options.tables) {
18487           this.rules = block.tables;
18488         } else {
18489           this.rules = block.gfm;
18490         }
18491       }
18492     }
18493     
18494     /**
18495      * Expose Block Rules
18496      */
18497     
18498     Lexer.rules = block;
18499     
18500     /**
18501      * Static Lex Method
18502      */
18503     
18504     Lexer.lex = function(src, options) {
18505       var lexer = new Lexer(options);
18506       return lexer.lex(src);
18507     };
18508     
18509     /**
18510      * Preprocessing
18511      */
18512     
18513     Lexer.prototype.lex = function(src) {
18514       src = src
18515         .replace(/\r\n|\r/g, '\n')
18516         .replace(/\t/g, '    ')
18517         .replace(/\u00a0/g, ' ')
18518         .replace(/\u2424/g, '\n');
18519     
18520       return this.token(src, true);
18521     };
18522     
18523     /**
18524      * Lexing
18525      */
18526     
18527     Lexer.prototype.token = function(src, top, bq) {
18528       var src = src.replace(/^ +$/gm, '')
18529         , next
18530         , loose
18531         , cap
18532         , bull
18533         , b
18534         , item
18535         , space
18536         , i
18537         , l;
18538     
18539       while (src) {
18540         // newline
18541         if (cap = this.rules.newline.exec(src)) {
18542           src = src.substring(cap[0].length);
18543           if (cap[0].length > 1) {
18544             this.tokens.push({
18545               type: 'space'
18546             });
18547           }
18548         }
18549     
18550         // code
18551         if (cap = this.rules.code.exec(src)) {
18552           src = src.substring(cap[0].length);
18553           cap = cap[0].replace(/^ {4}/gm, '');
18554           this.tokens.push({
18555             type: 'code',
18556             text: !this.options.pedantic
18557               ? cap.replace(/\n+$/, '')
18558               : cap
18559           });
18560           continue;
18561         }
18562     
18563         // fences (gfm)
18564         if (cap = this.rules.fences.exec(src)) {
18565           src = src.substring(cap[0].length);
18566           this.tokens.push({
18567             type: 'code',
18568             lang: cap[2],
18569             text: cap[3] || ''
18570           });
18571           continue;
18572         }
18573     
18574         // heading
18575         if (cap = this.rules.heading.exec(src)) {
18576           src = src.substring(cap[0].length);
18577           this.tokens.push({
18578             type: 'heading',
18579             depth: cap[1].length,
18580             text: cap[2]
18581           });
18582           continue;
18583         }
18584     
18585         // table no leading pipe (gfm)
18586         if (top && (cap = this.rules.nptable.exec(src))) {
18587           src = src.substring(cap[0].length);
18588     
18589           item = {
18590             type: 'table',
18591             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18592             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18593             cells: cap[3].replace(/\n$/, '').split('\n')
18594           };
18595     
18596           for (i = 0; i < item.align.length; i++) {
18597             if (/^ *-+: *$/.test(item.align[i])) {
18598               item.align[i] = 'right';
18599             } else if (/^ *:-+: *$/.test(item.align[i])) {
18600               item.align[i] = 'center';
18601             } else if (/^ *:-+ *$/.test(item.align[i])) {
18602               item.align[i] = 'left';
18603             } else {
18604               item.align[i] = null;
18605             }
18606           }
18607     
18608           for (i = 0; i < item.cells.length; i++) {
18609             item.cells[i] = item.cells[i].split(/ *\| */);
18610           }
18611     
18612           this.tokens.push(item);
18613     
18614           continue;
18615         }
18616     
18617         // lheading
18618         if (cap = this.rules.lheading.exec(src)) {
18619           src = src.substring(cap[0].length);
18620           this.tokens.push({
18621             type: 'heading',
18622             depth: cap[2] === '=' ? 1 : 2,
18623             text: cap[1]
18624           });
18625           continue;
18626         }
18627     
18628         // hr
18629         if (cap = this.rules.hr.exec(src)) {
18630           src = src.substring(cap[0].length);
18631           this.tokens.push({
18632             type: 'hr'
18633           });
18634           continue;
18635         }
18636     
18637         // blockquote
18638         if (cap = this.rules.blockquote.exec(src)) {
18639           src = src.substring(cap[0].length);
18640     
18641           this.tokens.push({
18642             type: 'blockquote_start'
18643           });
18644     
18645           cap = cap[0].replace(/^ *> ?/gm, '');
18646     
18647           // Pass `top` to keep the current
18648           // "toplevel" state. This is exactly
18649           // how markdown.pl works.
18650           this.token(cap, top, true);
18651     
18652           this.tokens.push({
18653             type: 'blockquote_end'
18654           });
18655     
18656           continue;
18657         }
18658     
18659         // list
18660         if (cap = this.rules.list.exec(src)) {
18661           src = src.substring(cap[0].length);
18662           bull = cap[2];
18663     
18664           this.tokens.push({
18665             type: 'list_start',
18666             ordered: bull.length > 1
18667           });
18668     
18669           // Get each top-level item.
18670           cap = cap[0].match(this.rules.item);
18671     
18672           next = false;
18673           l = cap.length;
18674           i = 0;
18675     
18676           for (; i < l; i++) {
18677             item = cap[i];
18678     
18679             // Remove the list item's bullet
18680             // so it is seen as the next token.
18681             space = item.length;
18682             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18683     
18684             // Outdent whatever the
18685             // list item contains. Hacky.
18686             if (~item.indexOf('\n ')) {
18687               space -= item.length;
18688               item = !this.options.pedantic
18689                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18690                 : item.replace(/^ {1,4}/gm, '');
18691             }
18692     
18693             // Determine whether the next list item belongs here.
18694             // Backpedal if it does not belong in this list.
18695             if (this.options.smartLists && i !== l - 1) {
18696               b = block.bullet.exec(cap[i + 1])[0];
18697               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18698                 src = cap.slice(i + 1).join('\n') + src;
18699                 i = l - 1;
18700               }
18701             }
18702     
18703             // Determine whether item is loose or not.
18704             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18705             // for discount behavior.
18706             loose = next || /\n\n(?!\s*$)/.test(item);
18707             if (i !== l - 1) {
18708               next = item.charAt(item.length - 1) === '\n';
18709               if (!loose) { loose = next; }
18710             }
18711     
18712             this.tokens.push({
18713               type: loose
18714                 ? 'loose_item_start'
18715                 : 'list_item_start'
18716             });
18717     
18718             // Recurse.
18719             this.token(item, false, bq);
18720     
18721             this.tokens.push({
18722               type: 'list_item_end'
18723             });
18724           }
18725     
18726           this.tokens.push({
18727             type: 'list_end'
18728           });
18729     
18730           continue;
18731         }
18732     
18733         // html
18734         if (cap = this.rules.html.exec(src)) {
18735           src = src.substring(cap[0].length);
18736           this.tokens.push({
18737             type: this.options.sanitize
18738               ? 'paragraph'
18739               : 'html',
18740             pre: !this.options.sanitizer
18741               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18742             text: cap[0]
18743           });
18744           continue;
18745         }
18746     
18747         // def
18748         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18749           src = src.substring(cap[0].length);
18750           this.tokens.links[cap[1].toLowerCase()] = {
18751             href: cap[2],
18752             title: cap[3]
18753           };
18754           continue;
18755         }
18756     
18757         // table (gfm)
18758         if (top && (cap = this.rules.table.exec(src))) {
18759           src = src.substring(cap[0].length);
18760     
18761           item = {
18762             type: 'table',
18763             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18764             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18765             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18766           };
18767     
18768           for (i = 0; i < item.align.length; i++) {
18769             if (/^ *-+: *$/.test(item.align[i])) {
18770               item.align[i] = 'right';
18771             } else if (/^ *:-+: *$/.test(item.align[i])) {
18772               item.align[i] = 'center';
18773             } else if (/^ *:-+ *$/.test(item.align[i])) {
18774               item.align[i] = 'left';
18775             } else {
18776               item.align[i] = null;
18777             }
18778           }
18779     
18780           for (i = 0; i < item.cells.length; i++) {
18781             item.cells[i] = item.cells[i]
18782               .replace(/^ *\| *| *\| *$/g, '')
18783               .split(/ *\| */);
18784           }
18785     
18786           this.tokens.push(item);
18787     
18788           continue;
18789         }
18790     
18791         // top-level paragraph
18792         if (top && (cap = this.rules.paragraph.exec(src))) {
18793           src = src.substring(cap[0].length);
18794           this.tokens.push({
18795             type: 'paragraph',
18796             text: cap[1].charAt(cap[1].length - 1) === '\n'
18797               ? cap[1].slice(0, -1)
18798               : cap[1]
18799           });
18800           continue;
18801         }
18802     
18803         // text
18804         if (cap = this.rules.text.exec(src)) {
18805           // Top-level should never reach here.
18806           src = src.substring(cap[0].length);
18807           this.tokens.push({
18808             type: 'text',
18809             text: cap[0]
18810           });
18811           continue;
18812         }
18813     
18814         if (src) {
18815           throw new
18816             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18817         }
18818       }
18819     
18820       return this.tokens;
18821     };
18822     
18823     /**
18824      * Inline-Level Grammar
18825      */
18826     
18827     var inline = {
18828       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18829       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18830       url: noop,
18831       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18832       link: /^!?\[(inside)\]\(href\)/,
18833       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18834       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18835       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18836       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18837       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18838       br: /^ {2,}\n(?!\s*$)/,
18839       del: noop,
18840       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18841     };
18842     
18843     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18844     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18845     
18846     inline.link = replace(inline.link)
18847       ('inside', inline._inside)
18848       ('href', inline._href)
18849       ();
18850     
18851     inline.reflink = replace(inline.reflink)
18852       ('inside', inline._inside)
18853       ();
18854     
18855     /**
18856      * Normal Inline Grammar
18857      */
18858     
18859     inline.normal = merge({}, inline);
18860     
18861     /**
18862      * Pedantic Inline Grammar
18863      */
18864     
18865     inline.pedantic = merge({}, inline.normal, {
18866       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18867       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18868     });
18869     
18870     /**
18871      * GFM Inline Grammar
18872      */
18873     
18874     inline.gfm = merge({}, inline.normal, {
18875       escape: replace(inline.escape)('])', '~|])')(),
18876       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18877       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18878       text: replace(inline.text)
18879         (']|', '~]|')
18880         ('|', '|https?://|')
18881         ()
18882     });
18883     
18884     /**
18885      * GFM + Line Breaks Inline Grammar
18886      */
18887     
18888     inline.breaks = merge({}, inline.gfm, {
18889       br: replace(inline.br)('{2,}', '*')(),
18890       text: replace(inline.gfm.text)('{2,}', '*')()
18891     });
18892     
18893     /**
18894      * Inline Lexer & Compiler
18895      */
18896     
18897     var InlineLexer  = function (links, options) {
18898       this.options = options || marked.defaults;
18899       this.links = links;
18900       this.rules = inline.normal;
18901       this.renderer = this.options.renderer || new Renderer;
18902       this.renderer.options = this.options;
18903     
18904       if (!this.links) {
18905         throw new
18906           Error('Tokens array requires a `links` property.');
18907       }
18908     
18909       if (this.options.gfm) {
18910         if (this.options.breaks) {
18911           this.rules = inline.breaks;
18912         } else {
18913           this.rules = inline.gfm;
18914         }
18915       } else if (this.options.pedantic) {
18916         this.rules = inline.pedantic;
18917       }
18918     }
18919     
18920     /**
18921      * Expose Inline Rules
18922      */
18923     
18924     InlineLexer.rules = inline;
18925     
18926     /**
18927      * Static Lexing/Compiling Method
18928      */
18929     
18930     InlineLexer.output = function(src, links, options) {
18931       var inline = new InlineLexer(links, options);
18932       return inline.output(src);
18933     };
18934     
18935     /**
18936      * Lexing/Compiling
18937      */
18938     
18939     InlineLexer.prototype.output = function(src) {
18940       var out = ''
18941         , link
18942         , text
18943         , href
18944         , cap;
18945     
18946       while (src) {
18947         // escape
18948         if (cap = this.rules.escape.exec(src)) {
18949           src = src.substring(cap[0].length);
18950           out += cap[1];
18951           continue;
18952         }
18953     
18954         // autolink
18955         if (cap = this.rules.autolink.exec(src)) {
18956           src = src.substring(cap[0].length);
18957           if (cap[2] === '@') {
18958             text = cap[1].charAt(6) === ':'
18959               ? this.mangle(cap[1].substring(7))
18960               : this.mangle(cap[1]);
18961             href = this.mangle('mailto:') + text;
18962           } else {
18963             text = escape(cap[1]);
18964             href = text;
18965           }
18966           out += this.renderer.link(href, null, text);
18967           continue;
18968         }
18969     
18970         // url (gfm)
18971         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18972           src = src.substring(cap[0].length);
18973           text = escape(cap[1]);
18974           href = text;
18975           out += this.renderer.link(href, null, text);
18976           continue;
18977         }
18978     
18979         // tag
18980         if (cap = this.rules.tag.exec(src)) {
18981           if (!this.inLink && /^<a /i.test(cap[0])) {
18982             this.inLink = true;
18983           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18984             this.inLink = false;
18985           }
18986           src = src.substring(cap[0].length);
18987           out += this.options.sanitize
18988             ? this.options.sanitizer
18989               ? this.options.sanitizer(cap[0])
18990               : escape(cap[0])
18991             : cap[0];
18992           continue;
18993         }
18994     
18995         // link
18996         if (cap = this.rules.link.exec(src)) {
18997           src = src.substring(cap[0].length);
18998           this.inLink = true;
18999           out += this.outputLink(cap, {
19000             href: cap[2],
19001             title: cap[3]
19002           });
19003           this.inLink = false;
19004           continue;
19005         }
19006     
19007         // reflink, nolink
19008         if ((cap = this.rules.reflink.exec(src))
19009             || (cap = this.rules.nolink.exec(src))) {
19010           src = src.substring(cap[0].length);
19011           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19012           link = this.links[link.toLowerCase()];
19013           if (!link || !link.href) {
19014             out += cap[0].charAt(0);
19015             src = cap[0].substring(1) + src;
19016             continue;
19017           }
19018           this.inLink = true;
19019           out += this.outputLink(cap, link);
19020           this.inLink = false;
19021           continue;
19022         }
19023     
19024         // strong
19025         if (cap = this.rules.strong.exec(src)) {
19026           src = src.substring(cap[0].length);
19027           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19028           continue;
19029         }
19030     
19031         // em
19032         if (cap = this.rules.em.exec(src)) {
19033           src = src.substring(cap[0].length);
19034           out += this.renderer.em(this.output(cap[2] || cap[1]));
19035           continue;
19036         }
19037     
19038         // code
19039         if (cap = this.rules.code.exec(src)) {
19040           src = src.substring(cap[0].length);
19041           out += this.renderer.codespan(escape(cap[2], true));
19042           continue;
19043         }
19044     
19045         // br
19046         if (cap = this.rules.br.exec(src)) {
19047           src = src.substring(cap[0].length);
19048           out += this.renderer.br();
19049           continue;
19050         }
19051     
19052         // del (gfm)
19053         if (cap = this.rules.del.exec(src)) {
19054           src = src.substring(cap[0].length);
19055           out += this.renderer.del(this.output(cap[1]));
19056           continue;
19057         }
19058     
19059         // text
19060         if (cap = this.rules.text.exec(src)) {
19061           src = src.substring(cap[0].length);
19062           out += this.renderer.text(escape(this.smartypants(cap[0])));
19063           continue;
19064         }
19065     
19066         if (src) {
19067           throw new
19068             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19069         }
19070       }
19071     
19072       return out;
19073     };
19074     
19075     /**
19076      * Compile Link
19077      */
19078     
19079     InlineLexer.prototype.outputLink = function(cap, link) {
19080       var href = escape(link.href)
19081         , title = link.title ? escape(link.title) : null;
19082     
19083       return cap[0].charAt(0) !== '!'
19084         ? this.renderer.link(href, title, this.output(cap[1]))
19085         : this.renderer.image(href, title, escape(cap[1]));
19086     };
19087     
19088     /**
19089      * Smartypants Transformations
19090      */
19091     
19092     InlineLexer.prototype.smartypants = function(text) {
19093       if (!this.options.smartypants)  { return text; }
19094       return text
19095         // em-dashes
19096         .replace(/---/g, '\u2014')
19097         // en-dashes
19098         .replace(/--/g, '\u2013')
19099         // opening singles
19100         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19101         // closing singles & apostrophes
19102         .replace(/'/g, '\u2019')
19103         // opening doubles
19104         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19105         // closing doubles
19106         .replace(/"/g, '\u201d')
19107         // ellipses
19108         .replace(/\.{3}/g, '\u2026');
19109     };
19110     
19111     /**
19112      * Mangle Links
19113      */
19114     
19115     InlineLexer.prototype.mangle = function(text) {
19116       if (!this.options.mangle) { return text; }
19117       var out = ''
19118         , l = text.length
19119         , i = 0
19120         , ch;
19121     
19122       for (; i < l; i++) {
19123         ch = text.charCodeAt(i);
19124         if (Math.random() > 0.5) {
19125           ch = 'x' + ch.toString(16);
19126         }
19127         out += '&#' + ch + ';';
19128       }
19129     
19130       return out;
19131     };
19132     
19133     /**
19134      * Renderer
19135      */
19136     
19137      /**
19138          * eval:var:Renderer
19139     */
19140     
19141     var Renderer   = function (options) {
19142       this.options = options || {};
19143     }
19144     
19145     Renderer.prototype.code = function(code, lang, escaped) {
19146       if (this.options.highlight) {
19147         var out = this.options.highlight(code, lang);
19148         if (out != null && out !== code) {
19149           escaped = true;
19150           code = out;
19151         }
19152       } else {
19153             // hack!!! - it's already escapeD?
19154             escaped = true;
19155       }
19156     
19157       if (!lang) {
19158         return '<pre><code>'
19159           + (escaped ? code : escape(code, true))
19160           + '\n</code></pre>';
19161       }
19162     
19163       return '<pre><code class="'
19164         + this.options.langPrefix
19165         + escape(lang, true)
19166         + '">'
19167         + (escaped ? code : escape(code, true))
19168         + '\n</code></pre>\n';
19169     };
19170     
19171     Renderer.prototype.blockquote = function(quote) {
19172       return '<blockquote>\n' + quote + '</blockquote>\n';
19173     };
19174     
19175     Renderer.prototype.html = function(html) {
19176       return html;
19177     };
19178     
19179     Renderer.prototype.heading = function(text, level, raw) {
19180       return '<h'
19181         + level
19182         + ' id="'
19183         + this.options.headerPrefix
19184         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19185         + '">'
19186         + text
19187         + '</h'
19188         + level
19189         + '>\n';
19190     };
19191     
19192     Renderer.prototype.hr = function() {
19193       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19194     };
19195     
19196     Renderer.prototype.list = function(body, ordered) {
19197       var type = ordered ? 'ol' : 'ul';
19198       return '<' + type + '>\n' + body + '</' + type + '>\n';
19199     };
19200     
19201     Renderer.prototype.listitem = function(text) {
19202       return '<li>' + text + '</li>\n';
19203     };
19204     
19205     Renderer.prototype.paragraph = function(text) {
19206       return '<p>' + text + '</p>\n';
19207     };
19208     
19209     Renderer.prototype.table = function(header, body) {
19210       return '<table class="table table-striped">\n'
19211         + '<thead>\n'
19212         + header
19213         + '</thead>\n'
19214         + '<tbody>\n'
19215         + body
19216         + '</tbody>\n'
19217         + '</table>\n';
19218     };
19219     
19220     Renderer.prototype.tablerow = function(content) {
19221       return '<tr>\n' + content + '</tr>\n';
19222     };
19223     
19224     Renderer.prototype.tablecell = function(content, flags) {
19225       var type = flags.header ? 'th' : 'td';
19226       var tag = flags.align
19227         ? '<' + type + ' style="text-align:' + flags.align + '">'
19228         : '<' + type + '>';
19229       return tag + content + '</' + type + '>\n';
19230     };
19231     
19232     // span level renderer
19233     Renderer.prototype.strong = function(text) {
19234       return '<strong>' + text + '</strong>';
19235     };
19236     
19237     Renderer.prototype.em = function(text) {
19238       return '<em>' + text + '</em>';
19239     };
19240     
19241     Renderer.prototype.codespan = function(text) {
19242       return '<code>' + text + '</code>';
19243     };
19244     
19245     Renderer.prototype.br = function() {
19246       return this.options.xhtml ? '<br/>' : '<br>';
19247     };
19248     
19249     Renderer.prototype.del = function(text) {
19250       return '<del>' + text + '</del>';
19251     };
19252     
19253     Renderer.prototype.link = function(href, title, text) {
19254       if (this.options.sanitize) {
19255         try {
19256           var prot = decodeURIComponent(unescape(href))
19257             .replace(/[^\w:]/g, '')
19258             .toLowerCase();
19259         } catch (e) {
19260           return '';
19261         }
19262         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19263           return '';
19264         }
19265       }
19266       var out = '<a href="' + href + '"';
19267       if (title) {
19268         out += ' title="' + title + '"';
19269       }
19270       out += '>' + text + '</a>';
19271       return out;
19272     };
19273     
19274     Renderer.prototype.image = function(href, title, text) {
19275       var out = '<img src="' + href + '" alt="' + text + '"';
19276       if (title) {
19277         out += ' title="' + title + '"';
19278       }
19279       out += this.options.xhtml ? '/>' : '>';
19280       return out;
19281     };
19282     
19283     Renderer.prototype.text = function(text) {
19284       return text;
19285     };
19286     
19287     /**
19288      * Parsing & Compiling
19289      */
19290          /**
19291          * eval:var:Parser
19292     */
19293     
19294     var Parser= function (options) {
19295       this.tokens = [];
19296       this.token = null;
19297       this.options = options || marked.defaults;
19298       this.options.renderer = this.options.renderer || new Renderer;
19299       this.renderer = this.options.renderer;
19300       this.renderer.options = this.options;
19301     }
19302     
19303     /**
19304      * Static Parse Method
19305      */
19306     
19307     Parser.parse = function(src, options, renderer) {
19308       var parser = new Parser(options, renderer);
19309       return parser.parse(src);
19310     };
19311     
19312     /**
19313      * Parse Loop
19314      */
19315     
19316     Parser.prototype.parse = function(src) {
19317       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19318       this.tokens = src.reverse();
19319     
19320       var out = '';
19321       while (this.next()) {
19322         out += this.tok();
19323       }
19324     
19325       return out;
19326     };
19327     
19328     /**
19329      * Next Token
19330      */
19331     
19332     Parser.prototype.next = function() {
19333       return this.token = this.tokens.pop();
19334     };
19335     
19336     /**
19337      * Preview Next Token
19338      */
19339     
19340     Parser.prototype.peek = function() {
19341       return this.tokens[this.tokens.length - 1] || 0;
19342     };
19343     
19344     /**
19345      * Parse Text Tokens
19346      */
19347     
19348     Parser.prototype.parseText = function() {
19349       var body = this.token.text;
19350     
19351       while (this.peek().type === 'text') {
19352         body += '\n' + this.next().text;
19353       }
19354     
19355       return this.inline.output(body);
19356     };
19357     
19358     /**
19359      * Parse Current Token
19360      */
19361     
19362     Parser.prototype.tok = function() {
19363       switch (this.token.type) {
19364         case 'space': {
19365           return '';
19366         }
19367         case 'hr': {
19368           return this.renderer.hr();
19369         }
19370         case 'heading': {
19371           return this.renderer.heading(
19372             this.inline.output(this.token.text),
19373             this.token.depth,
19374             this.token.text);
19375         }
19376         case 'code': {
19377           return this.renderer.code(this.token.text,
19378             this.token.lang,
19379             this.token.escaped);
19380         }
19381         case 'table': {
19382           var header = ''
19383             , body = ''
19384             , i
19385             , row
19386             , cell
19387             , flags
19388             , j;
19389     
19390           // header
19391           cell = '';
19392           for (i = 0; i < this.token.header.length; i++) {
19393             flags = { header: true, align: this.token.align[i] };
19394             cell += this.renderer.tablecell(
19395               this.inline.output(this.token.header[i]),
19396               { header: true, align: this.token.align[i] }
19397             );
19398           }
19399           header += this.renderer.tablerow(cell);
19400     
19401           for (i = 0; i < this.token.cells.length; i++) {
19402             row = this.token.cells[i];
19403     
19404             cell = '';
19405             for (j = 0; j < row.length; j++) {
19406               cell += this.renderer.tablecell(
19407                 this.inline.output(row[j]),
19408                 { header: false, align: this.token.align[j] }
19409               );
19410             }
19411     
19412             body += this.renderer.tablerow(cell);
19413           }
19414           return this.renderer.table(header, body);
19415         }
19416         case 'blockquote_start': {
19417           var body = '';
19418     
19419           while (this.next().type !== 'blockquote_end') {
19420             body += this.tok();
19421           }
19422     
19423           return this.renderer.blockquote(body);
19424         }
19425         case 'list_start': {
19426           var body = ''
19427             , ordered = this.token.ordered;
19428     
19429           while (this.next().type !== 'list_end') {
19430             body += this.tok();
19431           }
19432     
19433           return this.renderer.list(body, ordered);
19434         }
19435         case 'list_item_start': {
19436           var body = '';
19437     
19438           while (this.next().type !== 'list_item_end') {
19439             body += this.token.type === 'text'
19440               ? this.parseText()
19441               : this.tok();
19442           }
19443     
19444           return this.renderer.listitem(body);
19445         }
19446         case 'loose_item_start': {
19447           var body = '';
19448     
19449           while (this.next().type !== 'list_item_end') {
19450             body += this.tok();
19451           }
19452     
19453           return this.renderer.listitem(body);
19454         }
19455         case 'html': {
19456           var html = !this.token.pre && !this.options.pedantic
19457             ? this.inline.output(this.token.text)
19458             : this.token.text;
19459           return this.renderer.html(html);
19460         }
19461         case 'paragraph': {
19462           return this.renderer.paragraph(this.inline.output(this.token.text));
19463         }
19464         case 'text': {
19465           return this.renderer.paragraph(this.parseText());
19466         }
19467       }
19468     };
19469   
19470     
19471     /**
19472      * Marked
19473      */
19474          /**
19475          * eval:var:marked
19476     */
19477     var marked = function (src, opt, callback) {
19478       if (callback || typeof opt === 'function') {
19479         if (!callback) {
19480           callback = opt;
19481           opt = null;
19482         }
19483     
19484         opt = merge({}, marked.defaults, opt || {});
19485     
19486         var highlight = opt.highlight
19487           , tokens
19488           , pending
19489           , i = 0;
19490     
19491         try {
19492           tokens = Lexer.lex(src, opt)
19493         } catch (e) {
19494           return callback(e);
19495         }
19496     
19497         pending = tokens.length;
19498          /**
19499          * eval:var:done
19500     */
19501         var done = function(err) {
19502           if (err) {
19503             opt.highlight = highlight;
19504             return callback(err);
19505           }
19506     
19507           var out;
19508     
19509           try {
19510             out = Parser.parse(tokens, opt);
19511           } catch (e) {
19512             err = e;
19513           }
19514     
19515           opt.highlight = highlight;
19516     
19517           return err
19518             ? callback(err)
19519             : callback(null, out);
19520         };
19521     
19522         if (!highlight || highlight.length < 3) {
19523           return done();
19524         }
19525     
19526         delete opt.highlight;
19527     
19528         if (!pending) { return done(); }
19529     
19530         for (; i < tokens.length; i++) {
19531           (function(token) {
19532             if (token.type !== 'code') {
19533               return --pending || done();
19534             }
19535             return highlight(token.text, token.lang, function(err, code) {
19536               if (err) { return done(err); }
19537               if (code == null || code === token.text) {
19538                 return --pending || done();
19539               }
19540               token.text = code;
19541               token.escaped = true;
19542               --pending || done();
19543             });
19544           })(tokens[i]);
19545         }
19546     
19547         return;
19548       }
19549       try {
19550         if (opt) { opt = merge({}, marked.defaults, opt); }
19551         return Parser.parse(Lexer.lex(src, opt), opt);
19552       } catch (e) {
19553         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19554         if ((opt || marked.defaults).silent) {
19555           return '<p>An error occured:</p><pre>'
19556             + escape(e.message + '', true)
19557             + '</pre>';
19558         }
19559         throw e;
19560       }
19561     }
19562     
19563     /**
19564      * Options
19565      */
19566     
19567     marked.options =
19568     marked.setOptions = function(opt) {
19569       merge(marked.defaults, opt);
19570       return marked;
19571     };
19572     
19573     marked.defaults = {
19574       gfm: true,
19575       tables: true,
19576       breaks: false,
19577       pedantic: false,
19578       sanitize: false,
19579       sanitizer: null,
19580       mangle: true,
19581       smartLists: false,
19582       silent: false,
19583       highlight: null,
19584       langPrefix: 'lang-',
19585       smartypants: false,
19586       headerPrefix: '',
19587       renderer: new Renderer,
19588       xhtml: false
19589     };
19590     
19591     /**
19592      * Expose
19593      */
19594     
19595     marked.Parser = Parser;
19596     marked.parser = Parser.parse;
19597     
19598     marked.Renderer = Renderer;
19599     
19600     marked.Lexer = Lexer;
19601     marked.lexer = Lexer.lex;
19602     
19603     marked.InlineLexer = InlineLexer;
19604     marked.inlineLexer = InlineLexer.output;
19605     
19606     marked.parse = marked;
19607     
19608     Roo.Markdown.marked = marked;
19609
19610 })();/*
19611  * Based on:
19612  * Ext JS Library 1.1.1
19613  * Copyright(c) 2006-2007, Ext JS, LLC.
19614  *
19615  * Originally Released Under LGPL - original licence link has changed is not relivant.
19616  *
19617  * Fork - LGPL
19618  * <script type="text/javascript">
19619  */
19620
19621
19622
19623 /*
19624  * These classes are derivatives of the similarly named classes in the YUI Library.
19625  * The original license:
19626  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19627  * Code licensed under the BSD License:
19628  * http://developer.yahoo.net/yui/license.txt
19629  */
19630
19631 (function() {
19632
19633 var Event=Roo.EventManager;
19634 var Dom=Roo.lib.Dom;
19635
19636 /**
19637  * @class Roo.dd.DragDrop
19638  * @extends Roo.util.Observable
19639  * Defines the interface and base operation of items that that can be
19640  * dragged or can be drop targets.  It was designed to be extended, overriding
19641  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19642  * Up to three html elements can be associated with a DragDrop instance:
19643  * <ul>
19644  * <li>linked element: the element that is passed into the constructor.
19645  * This is the element which defines the boundaries for interaction with
19646  * other DragDrop objects.</li>
19647  * <li>handle element(s): The drag operation only occurs if the element that
19648  * was clicked matches a handle element.  By default this is the linked
19649  * element, but there are times that you will want only a portion of the
19650  * linked element to initiate the drag operation, and the setHandleElId()
19651  * method provides a way to define this.</li>
19652  * <li>drag element: this represents the element that would be moved along
19653  * with the cursor during a drag operation.  By default, this is the linked
19654  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19655  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19656  * </li>
19657  * </ul>
19658  * This class should not be instantiated until the onload event to ensure that
19659  * the associated elements are available.
19660  * The following would define a DragDrop obj that would interact with any
19661  * other DragDrop obj in the "group1" group:
19662  * <pre>
19663  *  dd = new Roo.dd.DragDrop("div1", "group1");
19664  * </pre>
19665  * Since none of the event handlers have been implemented, nothing would
19666  * actually happen if you were to run the code above.  Normally you would
19667  * override this class or one of the default implementations, but you can
19668  * also override the methods you want on an instance of the class...
19669  * <pre>
19670  *  dd.onDragDrop = function(e, id) {
19671  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19672  *  }
19673  * </pre>
19674  * @constructor
19675  * @param {String} id of the element that is linked to this instance
19676  * @param {String} sGroup the group of related DragDrop objects
19677  * @param {object} config an object containing configurable attributes
19678  *                Valid properties for DragDrop:
19679  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19680  */
19681 Roo.dd.DragDrop = function(id, sGroup, config) {
19682     if (id) {
19683         this.init(id, sGroup, config);
19684     }
19685     
19686 };
19687
19688 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19689
19690     /**
19691      * The id of the element associated with this object.  This is what we
19692      * refer to as the "linked element" because the size and position of
19693      * this element is used to determine when the drag and drop objects have
19694      * interacted.
19695      * @property id
19696      * @type String
19697      */
19698     id: null,
19699
19700     /**
19701      * Configuration attributes passed into the constructor
19702      * @property config
19703      * @type object
19704      */
19705     config: null,
19706
19707     /**
19708      * The id of the element that will be dragged.  By default this is same
19709      * as the linked element , but could be changed to another element. Ex:
19710      * Roo.dd.DDProxy
19711      * @property dragElId
19712      * @type String
19713      * @private
19714      */
19715     dragElId: null,
19716
19717     /**
19718      * the id of the element that initiates the drag operation.  By default
19719      * this is the linked element, but could be changed to be a child of this
19720      * element.  This lets us do things like only starting the drag when the
19721      * header element within the linked html element is clicked.
19722      * @property handleElId
19723      * @type String
19724      * @private
19725      */
19726     handleElId: null,
19727
19728     /**
19729      * An associative array of HTML tags that will be ignored if clicked.
19730      * @property invalidHandleTypes
19731      * @type {string: string}
19732      */
19733     invalidHandleTypes: null,
19734
19735     /**
19736      * An associative array of ids for elements that will be ignored if clicked
19737      * @property invalidHandleIds
19738      * @type {string: string}
19739      */
19740     invalidHandleIds: null,
19741
19742     /**
19743      * An indexted array of css class names for elements that will be ignored
19744      * if clicked.
19745      * @property invalidHandleClasses
19746      * @type string[]
19747      */
19748     invalidHandleClasses: null,
19749
19750     /**
19751      * The linked element's absolute X position at the time the drag was
19752      * started
19753      * @property startPageX
19754      * @type int
19755      * @private
19756      */
19757     startPageX: 0,
19758
19759     /**
19760      * The linked element's absolute X position at the time the drag was
19761      * started
19762      * @property startPageY
19763      * @type int
19764      * @private
19765      */
19766     startPageY: 0,
19767
19768     /**
19769      * The group defines a logical collection of DragDrop objects that are
19770      * related.  Instances only get events when interacting with other
19771      * DragDrop object in the same group.  This lets us define multiple
19772      * groups using a single DragDrop subclass if we want.
19773      * @property groups
19774      * @type {string: string}
19775      */
19776     groups: null,
19777
19778     /**
19779      * Individual drag/drop instances can be locked.  This will prevent
19780      * onmousedown start drag.
19781      * @property locked
19782      * @type boolean
19783      * @private
19784      */
19785     locked: false,
19786
19787     /**
19788      * Lock this instance
19789      * @method lock
19790      */
19791     lock: function() { this.locked = true; },
19792
19793     /**
19794      * Unlock this instace
19795      * @method unlock
19796      */
19797     unlock: function() { this.locked = false; },
19798
19799     /**
19800      * By default, all insances can be a drop target.  This can be disabled by
19801      * setting isTarget to false.
19802      * @method isTarget
19803      * @type boolean
19804      */
19805     isTarget: true,
19806
19807     /**
19808      * The padding configured for this drag and drop object for calculating
19809      * the drop zone intersection with this object.
19810      * @method padding
19811      * @type int[]
19812      */
19813     padding: null,
19814
19815     /**
19816      * Cached reference to the linked element
19817      * @property _domRef
19818      * @private
19819      */
19820     _domRef: null,
19821
19822     /**
19823      * Internal typeof flag
19824      * @property __ygDragDrop
19825      * @private
19826      */
19827     __ygDragDrop: true,
19828
19829     /**
19830      * Set to true when horizontal contraints are applied
19831      * @property constrainX
19832      * @type boolean
19833      * @private
19834      */
19835     constrainX: false,
19836
19837     /**
19838      * Set to true when vertical contraints are applied
19839      * @property constrainY
19840      * @type boolean
19841      * @private
19842      */
19843     constrainY: false,
19844
19845     /**
19846      * The left constraint
19847      * @property minX
19848      * @type int
19849      * @private
19850      */
19851     minX: 0,
19852
19853     /**
19854      * The right constraint
19855      * @property maxX
19856      * @type int
19857      * @private
19858      */
19859     maxX: 0,
19860
19861     /**
19862      * The up constraint
19863      * @property minY
19864      * @type int
19865      * @type int
19866      * @private
19867      */
19868     minY: 0,
19869
19870     /**
19871      * The down constraint
19872      * @property maxY
19873      * @type int
19874      * @private
19875      */
19876     maxY: 0,
19877
19878     /**
19879      * Maintain offsets when we resetconstraints.  Set to true when you want
19880      * the position of the element relative to its parent to stay the same
19881      * when the page changes
19882      *
19883      * @property maintainOffset
19884      * @type boolean
19885      */
19886     maintainOffset: false,
19887
19888     /**
19889      * Array of pixel locations the element will snap to if we specified a
19890      * horizontal graduation/interval.  This array is generated automatically
19891      * when you define a tick interval.
19892      * @property xTicks
19893      * @type int[]
19894      */
19895     xTicks: null,
19896
19897     /**
19898      * Array of pixel locations the element will snap to if we specified a
19899      * vertical graduation/interval.  This array is generated automatically
19900      * when you define a tick interval.
19901      * @property yTicks
19902      * @type int[]
19903      */
19904     yTicks: null,
19905
19906     /**
19907      * By default the drag and drop instance will only respond to the primary
19908      * button click (left button for a right-handed mouse).  Set to true to
19909      * allow drag and drop to start with any mouse click that is propogated
19910      * by the browser
19911      * @property primaryButtonOnly
19912      * @type boolean
19913      */
19914     primaryButtonOnly: true,
19915
19916     /**
19917      * The availabe property is false until the linked dom element is accessible.
19918      * @property available
19919      * @type boolean
19920      */
19921     available: false,
19922
19923     /**
19924      * By default, drags can only be initiated if the mousedown occurs in the
19925      * region the linked element is.  This is done in part to work around a
19926      * bug in some browsers that mis-report the mousedown if the previous
19927      * mouseup happened outside of the window.  This property is set to true
19928      * if outer handles are defined.
19929      *
19930      * @property hasOuterHandles
19931      * @type boolean
19932      * @default false
19933      */
19934     hasOuterHandles: false,
19935
19936     /**
19937      * Code that executes immediately before the startDrag event
19938      * @method b4StartDrag
19939      * @private
19940      */
19941     b4StartDrag: function(x, y) { },
19942
19943     /**
19944      * Abstract method called after a drag/drop object is clicked
19945      * and the drag or mousedown time thresholds have beeen met.
19946      * @method startDrag
19947      * @param {int} X click location
19948      * @param {int} Y click location
19949      */
19950     startDrag: function(x, y) { /* override this */ },
19951
19952     /**
19953      * Code that executes immediately before the onDrag event
19954      * @method b4Drag
19955      * @private
19956      */
19957     b4Drag: function(e) { },
19958
19959     /**
19960      * Abstract method called during the onMouseMove event while dragging an
19961      * object.
19962      * @method onDrag
19963      * @param {Event} e the mousemove event
19964      */
19965     onDrag: function(e) { /* override this */ },
19966
19967     /**
19968      * Abstract method called when this element fist begins hovering over
19969      * another DragDrop obj
19970      * @method onDragEnter
19971      * @param {Event} e the mousemove event
19972      * @param {String|DragDrop[]} id In POINT mode, the element
19973      * id this is hovering over.  In INTERSECT mode, an array of one or more
19974      * dragdrop items being hovered over.
19975      */
19976     onDragEnter: function(e, id) { /* override this */ },
19977
19978     /**
19979      * Code that executes immediately before the onDragOver event
19980      * @method b4DragOver
19981      * @private
19982      */
19983     b4DragOver: function(e) { },
19984
19985     /**
19986      * Abstract method called when this element is hovering over another
19987      * DragDrop obj
19988      * @method onDragOver
19989      * @param {Event} e the mousemove event
19990      * @param {String|DragDrop[]} id In POINT mode, the element
19991      * id this is hovering over.  In INTERSECT mode, an array of dd items
19992      * being hovered over.
19993      */
19994     onDragOver: function(e, id) { /* override this */ },
19995
19996     /**
19997      * Code that executes immediately before the onDragOut event
19998      * @method b4DragOut
19999      * @private
20000      */
20001     b4DragOut: function(e) { },
20002
20003     /**
20004      * Abstract method called when we are no longer hovering over an element
20005      * @method onDragOut
20006      * @param {Event} e the mousemove event
20007      * @param {String|DragDrop[]} id In POINT mode, the element
20008      * id this was hovering over.  In INTERSECT mode, an array of dd items
20009      * that the mouse is no longer over.
20010      */
20011     onDragOut: function(e, id) { /* override this */ },
20012
20013     /**
20014      * Code that executes immediately before the onDragDrop event
20015      * @method b4DragDrop
20016      * @private
20017      */
20018     b4DragDrop: function(e) { },
20019
20020     /**
20021      * Abstract method called when this item is dropped on another DragDrop
20022      * obj
20023      * @method onDragDrop
20024      * @param {Event} e the mouseup event
20025      * @param {String|DragDrop[]} id In POINT mode, the element
20026      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20027      * was dropped on.
20028      */
20029     onDragDrop: function(e, id) { /* override this */ },
20030
20031     /**
20032      * Abstract method called when this item is dropped on an area with no
20033      * drop target
20034      * @method onInvalidDrop
20035      * @param {Event} e the mouseup event
20036      */
20037     onInvalidDrop: function(e) { /* override this */ },
20038
20039     /**
20040      * Code that executes immediately before the endDrag event
20041      * @method b4EndDrag
20042      * @private
20043      */
20044     b4EndDrag: function(e) { },
20045
20046     /**
20047      * Fired when we are done dragging the object
20048      * @method endDrag
20049      * @param {Event} e the mouseup event
20050      */
20051     endDrag: function(e) { /* override this */ },
20052
20053     /**
20054      * Code executed immediately before the onMouseDown event
20055      * @method b4MouseDown
20056      * @param {Event} e the mousedown event
20057      * @private
20058      */
20059     b4MouseDown: function(e) {  },
20060
20061     /**
20062      * Event handler that fires when a drag/drop obj gets a mousedown
20063      * @method onMouseDown
20064      * @param {Event} e the mousedown event
20065      */
20066     onMouseDown: function(e) { /* override this */ },
20067
20068     /**
20069      * Event handler that fires when a drag/drop obj gets a mouseup
20070      * @method onMouseUp
20071      * @param {Event} e the mouseup event
20072      */
20073     onMouseUp: function(e) { /* override this */ },
20074
20075     /**
20076      * Override the onAvailable method to do what is needed after the initial
20077      * position was determined.
20078      * @method onAvailable
20079      */
20080     onAvailable: function () {
20081     },
20082
20083     /*
20084      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20085      * @type Object
20086      */
20087     defaultPadding : {left:0, right:0, top:0, bottom:0},
20088
20089     /*
20090      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20091  *
20092  * Usage:
20093  <pre><code>
20094  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20095                 { dragElId: "existingProxyDiv" });
20096  dd.startDrag = function(){
20097      this.constrainTo("parent-id");
20098  };
20099  </code></pre>
20100  * Or you can initalize it using the {@link Roo.Element} object:
20101  <pre><code>
20102  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20103      startDrag : function(){
20104          this.constrainTo("parent-id");
20105      }
20106  });
20107  </code></pre>
20108      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20109      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20110      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20111      * an object containing the sides to pad. For example: {right:10, bottom:10}
20112      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20113      */
20114     constrainTo : function(constrainTo, pad, inContent){
20115         if(typeof pad == "number"){
20116             pad = {left: pad, right:pad, top:pad, bottom:pad};
20117         }
20118         pad = pad || this.defaultPadding;
20119         var b = Roo.get(this.getEl()).getBox();
20120         var ce = Roo.get(constrainTo);
20121         var s = ce.getScroll();
20122         var c, cd = ce.dom;
20123         if(cd == document.body){
20124             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20125         }else{
20126             xy = ce.getXY();
20127             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20128         }
20129
20130
20131         var topSpace = b.y - c.y;
20132         var leftSpace = b.x - c.x;
20133
20134         this.resetConstraints();
20135         this.setXConstraint(leftSpace - (pad.left||0), // left
20136                 c.width - leftSpace - b.width - (pad.right||0) //right
20137         );
20138         this.setYConstraint(topSpace - (pad.top||0), //top
20139                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20140         );
20141     },
20142
20143     /**
20144      * Returns a reference to the linked element
20145      * @method getEl
20146      * @return {HTMLElement} the html element
20147      */
20148     getEl: function() {
20149         if (!this._domRef) {
20150             this._domRef = Roo.getDom(this.id);
20151         }
20152
20153         return this._domRef;
20154     },
20155
20156     /**
20157      * Returns a reference to the actual element to drag.  By default this is
20158      * the same as the html element, but it can be assigned to another
20159      * element. An example of this can be found in Roo.dd.DDProxy
20160      * @method getDragEl
20161      * @return {HTMLElement} the html element
20162      */
20163     getDragEl: function() {
20164         return Roo.getDom(this.dragElId);
20165     },
20166
20167     /**
20168      * Sets up the DragDrop object.  Must be called in the constructor of any
20169      * Roo.dd.DragDrop subclass
20170      * @method init
20171      * @param id the id of the linked element
20172      * @param {String} sGroup the group of related items
20173      * @param {object} config configuration attributes
20174      */
20175     init: function(id, sGroup, config) {
20176         this.initTarget(id, sGroup, config);
20177         if (!Roo.isTouch) {
20178             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20179         }
20180         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20181         // Event.on(this.id, "selectstart", Event.preventDefault);
20182     },
20183
20184     /**
20185      * Initializes Targeting functionality only... the object does not
20186      * get a mousedown handler.
20187      * @method initTarget
20188      * @param id the id of the linked element
20189      * @param {String} sGroup the group of related items
20190      * @param {object} config configuration attributes
20191      */
20192     initTarget: function(id, sGroup, config) {
20193
20194         // configuration attributes
20195         this.config = config || {};
20196
20197         // create a local reference to the drag and drop manager
20198         this.DDM = Roo.dd.DDM;
20199         // initialize the groups array
20200         this.groups = {};
20201
20202         // assume that we have an element reference instead of an id if the
20203         // parameter is not a string
20204         if (typeof id !== "string") {
20205             id = Roo.id(id);
20206         }
20207
20208         // set the id
20209         this.id = id;
20210
20211         // add to an interaction group
20212         this.addToGroup((sGroup) ? sGroup : "default");
20213
20214         // We don't want to register this as the handle with the manager
20215         // so we just set the id rather than calling the setter.
20216         this.handleElId = id;
20217
20218         // the linked element is the element that gets dragged by default
20219         this.setDragElId(id);
20220
20221         // by default, clicked anchors will not start drag operations.
20222         this.invalidHandleTypes = { A: "A" };
20223         this.invalidHandleIds = {};
20224         this.invalidHandleClasses = [];
20225
20226         this.applyConfig();
20227
20228         this.handleOnAvailable();
20229     },
20230
20231     /**
20232      * Applies the configuration parameters that were passed into the constructor.
20233      * This is supposed to happen at each level through the inheritance chain.  So
20234      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20235      * DragDrop in order to get all of the parameters that are available in
20236      * each object.
20237      * @method applyConfig
20238      */
20239     applyConfig: function() {
20240
20241         // configurable properties:
20242         //    padding, isTarget, maintainOffset, primaryButtonOnly
20243         this.padding           = this.config.padding || [0, 0, 0, 0];
20244         this.isTarget          = (this.config.isTarget !== false);
20245         this.maintainOffset    = (this.config.maintainOffset);
20246         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20247
20248     },
20249
20250     /**
20251      * Executed when the linked element is available
20252      * @method handleOnAvailable
20253      * @private
20254      */
20255     handleOnAvailable: function() {
20256         this.available = true;
20257         this.resetConstraints();
20258         this.onAvailable();
20259     },
20260
20261      /**
20262      * Configures the padding for the target zone in px.  Effectively expands
20263      * (or reduces) the virtual object size for targeting calculations.
20264      * Supports css-style shorthand; if only one parameter is passed, all sides
20265      * will have that padding, and if only two are passed, the top and bottom
20266      * will have the first param, the left and right the second.
20267      * @method setPadding
20268      * @param {int} iTop    Top pad
20269      * @param {int} iRight  Right pad
20270      * @param {int} iBot    Bot pad
20271      * @param {int} iLeft   Left pad
20272      */
20273     setPadding: function(iTop, iRight, iBot, iLeft) {
20274         // this.padding = [iLeft, iRight, iTop, iBot];
20275         if (!iRight && 0 !== iRight) {
20276             this.padding = [iTop, iTop, iTop, iTop];
20277         } else if (!iBot && 0 !== iBot) {
20278             this.padding = [iTop, iRight, iTop, iRight];
20279         } else {
20280             this.padding = [iTop, iRight, iBot, iLeft];
20281         }
20282     },
20283
20284     /**
20285      * Stores the initial placement of the linked element.
20286      * @method setInitialPosition
20287      * @param {int} diffX   the X offset, default 0
20288      * @param {int} diffY   the Y offset, default 0
20289      */
20290     setInitPosition: function(diffX, diffY) {
20291         var el = this.getEl();
20292
20293         if (!this.DDM.verifyEl(el)) {
20294             return;
20295         }
20296
20297         var dx = diffX || 0;
20298         var dy = diffY || 0;
20299
20300         var p = Dom.getXY( el );
20301
20302         this.initPageX = p[0] - dx;
20303         this.initPageY = p[1] - dy;
20304
20305         this.lastPageX = p[0];
20306         this.lastPageY = p[1];
20307
20308
20309         this.setStartPosition(p);
20310     },
20311
20312     /**
20313      * Sets the start position of the element.  This is set when the obj
20314      * is initialized, the reset when a drag is started.
20315      * @method setStartPosition
20316      * @param pos current position (from previous lookup)
20317      * @private
20318      */
20319     setStartPosition: function(pos) {
20320         var p = pos || Dom.getXY( this.getEl() );
20321         this.deltaSetXY = null;
20322
20323         this.startPageX = p[0];
20324         this.startPageY = p[1];
20325     },
20326
20327     /**
20328      * Add this instance to a group of related drag/drop objects.  All
20329      * instances belong to at least one group, and can belong to as many
20330      * groups as needed.
20331      * @method addToGroup
20332      * @param sGroup {string} the name of the group
20333      */
20334     addToGroup: function(sGroup) {
20335         this.groups[sGroup] = true;
20336         this.DDM.regDragDrop(this, sGroup);
20337     },
20338
20339     /**
20340      * Remove's this instance from the supplied interaction group
20341      * @method removeFromGroup
20342      * @param {string}  sGroup  The group to drop
20343      */
20344     removeFromGroup: function(sGroup) {
20345         if (this.groups[sGroup]) {
20346             delete this.groups[sGroup];
20347         }
20348
20349         this.DDM.removeDDFromGroup(this, sGroup);
20350     },
20351
20352     /**
20353      * Allows you to specify that an element other than the linked element
20354      * will be moved with the cursor during a drag
20355      * @method setDragElId
20356      * @param id {string} the id of the element that will be used to initiate the drag
20357      */
20358     setDragElId: function(id) {
20359         this.dragElId = id;
20360     },
20361
20362     /**
20363      * Allows you to specify a child of the linked element that should be
20364      * used to initiate the drag operation.  An example of this would be if
20365      * you have a content div with text and links.  Clicking anywhere in the
20366      * content area would normally start the drag operation.  Use this method
20367      * to specify that an element inside of the content div is the element
20368      * that starts the drag operation.
20369      * @method setHandleElId
20370      * @param id {string} the id of the element that will be used to
20371      * initiate the drag.
20372      */
20373     setHandleElId: function(id) {
20374         if (typeof id !== "string") {
20375             id = Roo.id(id);
20376         }
20377         this.handleElId = id;
20378         this.DDM.regHandle(this.id, id);
20379     },
20380
20381     /**
20382      * Allows you to set an element outside of the linked element as a drag
20383      * handle
20384      * @method setOuterHandleElId
20385      * @param id the id of the element that will be used to initiate the drag
20386      */
20387     setOuterHandleElId: function(id) {
20388         if (typeof id !== "string") {
20389             id = Roo.id(id);
20390         }
20391         Event.on(id, "mousedown",
20392                 this.handleMouseDown, this);
20393         this.setHandleElId(id);
20394
20395         this.hasOuterHandles = true;
20396     },
20397
20398     /**
20399      * Remove all drag and drop hooks for this element
20400      * @method unreg
20401      */
20402     unreg: function() {
20403         Event.un(this.id, "mousedown",
20404                 this.handleMouseDown);
20405         Event.un(this.id, "touchstart",
20406                 this.handleMouseDown);
20407         this._domRef = null;
20408         this.DDM._remove(this);
20409     },
20410
20411     destroy : function(){
20412         this.unreg();
20413     },
20414
20415     /**
20416      * Returns true if this instance is locked, or the drag drop mgr is locked
20417      * (meaning that all drag/drop is disabled on the page.)
20418      * @method isLocked
20419      * @return {boolean} true if this obj or all drag/drop is locked, else
20420      * false
20421      */
20422     isLocked: function() {
20423         return (this.DDM.isLocked() || this.locked);
20424     },
20425
20426     /**
20427      * Fired when this object is clicked
20428      * @method handleMouseDown
20429      * @param {Event} e
20430      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20431      * @private
20432      */
20433     handleMouseDown: function(e, oDD){
20434      
20435         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20436             //Roo.log('not touch/ button !=0');
20437             return;
20438         }
20439         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20440             return; // double touch..
20441         }
20442         
20443
20444         if (this.isLocked()) {
20445             //Roo.log('locked');
20446             return;
20447         }
20448
20449         this.DDM.refreshCache(this.groups);
20450 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20451         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20452         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20453             //Roo.log('no outer handes or not over target');
20454                 // do nothing.
20455         } else {
20456 //            Roo.log('check validator');
20457             if (this.clickValidator(e)) {
20458 //                Roo.log('validate success');
20459                 // set the initial element position
20460                 this.setStartPosition();
20461
20462
20463                 this.b4MouseDown(e);
20464                 this.onMouseDown(e);
20465
20466                 this.DDM.handleMouseDown(e, this);
20467
20468                 this.DDM.stopEvent(e);
20469             } else {
20470
20471
20472             }
20473         }
20474     },
20475
20476     clickValidator: function(e) {
20477         var target = e.getTarget();
20478         return ( this.isValidHandleChild(target) &&
20479                     (this.id == this.handleElId ||
20480                         this.DDM.handleWasClicked(target, this.id)) );
20481     },
20482
20483     /**
20484      * Allows you to specify a tag name that should not start a drag operation
20485      * when clicked.  This is designed to facilitate embedding links within a
20486      * drag handle that do something other than start the drag.
20487      * @method addInvalidHandleType
20488      * @param {string} tagName the type of element to exclude
20489      */
20490     addInvalidHandleType: function(tagName) {
20491         var type = tagName.toUpperCase();
20492         this.invalidHandleTypes[type] = type;
20493     },
20494
20495     /**
20496      * Lets you to specify an element id for a child of a drag handle
20497      * that should not initiate a drag
20498      * @method addInvalidHandleId
20499      * @param {string} id the element id of the element you wish to ignore
20500      */
20501     addInvalidHandleId: function(id) {
20502         if (typeof id !== "string") {
20503             id = Roo.id(id);
20504         }
20505         this.invalidHandleIds[id] = id;
20506     },
20507
20508     /**
20509      * Lets you specify a css class of elements that will not initiate a drag
20510      * @method addInvalidHandleClass
20511      * @param {string} cssClass the class of the elements you wish to ignore
20512      */
20513     addInvalidHandleClass: function(cssClass) {
20514         this.invalidHandleClasses.push(cssClass);
20515     },
20516
20517     /**
20518      * Unsets an excluded tag name set by addInvalidHandleType
20519      * @method removeInvalidHandleType
20520      * @param {string} tagName the type of element to unexclude
20521      */
20522     removeInvalidHandleType: function(tagName) {
20523         var type = tagName.toUpperCase();
20524         // this.invalidHandleTypes[type] = null;
20525         delete this.invalidHandleTypes[type];
20526     },
20527
20528     /**
20529      * Unsets an invalid handle id
20530      * @method removeInvalidHandleId
20531      * @param {string} id the id of the element to re-enable
20532      */
20533     removeInvalidHandleId: function(id) {
20534         if (typeof id !== "string") {
20535             id = Roo.id(id);
20536         }
20537         delete this.invalidHandleIds[id];
20538     },
20539
20540     /**
20541      * Unsets an invalid css class
20542      * @method removeInvalidHandleClass
20543      * @param {string} cssClass the class of the element(s) you wish to
20544      * re-enable
20545      */
20546     removeInvalidHandleClass: function(cssClass) {
20547         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20548             if (this.invalidHandleClasses[i] == cssClass) {
20549                 delete this.invalidHandleClasses[i];
20550             }
20551         }
20552     },
20553
20554     /**
20555      * Checks the tag exclusion list to see if this click should be ignored
20556      * @method isValidHandleChild
20557      * @param {HTMLElement} node the HTMLElement to evaluate
20558      * @return {boolean} true if this is a valid tag type, false if not
20559      */
20560     isValidHandleChild: function(node) {
20561
20562         var valid = true;
20563         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20564         var nodeName;
20565         try {
20566             nodeName = node.nodeName.toUpperCase();
20567         } catch(e) {
20568             nodeName = node.nodeName;
20569         }
20570         valid = valid && !this.invalidHandleTypes[nodeName];
20571         valid = valid && !this.invalidHandleIds[node.id];
20572
20573         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20574             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20575         }
20576
20577
20578         return valid;
20579
20580     },
20581
20582     /**
20583      * Create the array of horizontal tick marks if an interval was specified
20584      * in setXConstraint().
20585      * @method setXTicks
20586      * @private
20587      */
20588     setXTicks: function(iStartX, iTickSize) {
20589         this.xTicks = [];
20590         this.xTickSize = iTickSize;
20591
20592         var tickMap = {};
20593
20594         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20595             if (!tickMap[i]) {
20596                 this.xTicks[this.xTicks.length] = i;
20597                 tickMap[i] = true;
20598             }
20599         }
20600
20601         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20602             if (!tickMap[i]) {
20603                 this.xTicks[this.xTicks.length] = i;
20604                 tickMap[i] = true;
20605             }
20606         }
20607
20608         this.xTicks.sort(this.DDM.numericSort) ;
20609     },
20610
20611     /**
20612      * Create the array of vertical tick marks if an interval was specified in
20613      * setYConstraint().
20614      * @method setYTicks
20615      * @private
20616      */
20617     setYTicks: function(iStartY, iTickSize) {
20618         this.yTicks = [];
20619         this.yTickSize = iTickSize;
20620
20621         var tickMap = {};
20622
20623         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20624             if (!tickMap[i]) {
20625                 this.yTicks[this.yTicks.length] = i;
20626                 tickMap[i] = true;
20627             }
20628         }
20629
20630         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20631             if (!tickMap[i]) {
20632                 this.yTicks[this.yTicks.length] = i;
20633                 tickMap[i] = true;
20634             }
20635         }
20636
20637         this.yTicks.sort(this.DDM.numericSort) ;
20638     },
20639
20640     /**
20641      * By default, the element can be dragged any place on the screen.  Use
20642      * this method to limit the horizontal travel of the element.  Pass in
20643      * 0,0 for the parameters if you want to lock the drag to the y axis.
20644      * @method setXConstraint
20645      * @param {int} iLeft the number of pixels the element can move to the left
20646      * @param {int} iRight the number of pixels the element can move to the
20647      * right
20648      * @param {int} iTickSize optional parameter for specifying that the
20649      * element
20650      * should move iTickSize pixels at a time.
20651      */
20652     setXConstraint: function(iLeft, iRight, iTickSize) {
20653         this.leftConstraint = iLeft;
20654         this.rightConstraint = iRight;
20655
20656         this.minX = this.initPageX - iLeft;
20657         this.maxX = this.initPageX + iRight;
20658         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20659
20660         this.constrainX = true;
20661     },
20662
20663     /**
20664      * Clears any constraints applied to this instance.  Also clears ticks
20665      * since they can't exist independent of a constraint at this time.
20666      * @method clearConstraints
20667      */
20668     clearConstraints: function() {
20669         this.constrainX = false;
20670         this.constrainY = false;
20671         this.clearTicks();
20672     },
20673
20674     /**
20675      * Clears any tick interval defined for this instance
20676      * @method clearTicks
20677      */
20678     clearTicks: function() {
20679         this.xTicks = null;
20680         this.yTicks = null;
20681         this.xTickSize = 0;
20682         this.yTickSize = 0;
20683     },
20684
20685     /**
20686      * By default, the element can be dragged any place on the screen.  Set
20687      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20688      * parameters if you want to lock the drag to the x axis.
20689      * @method setYConstraint
20690      * @param {int} iUp the number of pixels the element can move up
20691      * @param {int} iDown the number of pixels the element can move down
20692      * @param {int} iTickSize optional parameter for specifying that the
20693      * element should move iTickSize pixels at a time.
20694      */
20695     setYConstraint: function(iUp, iDown, iTickSize) {
20696         this.topConstraint = iUp;
20697         this.bottomConstraint = iDown;
20698
20699         this.minY = this.initPageY - iUp;
20700         this.maxY = this.initPageY + iDown;
20701         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20702
20703         this.constrainY = true;
20704
20705     },
20706
20707     /**
20708      * resetConstraints must be called if you manually reposition a dd element.
20709      * @method resetConstraints
20710      * @param {boolean} maintainOffset
20711      */
20712     resetConstraints: function() {
20713
20714
20715         // Maintain offsets if necessary
20716         if (this.initPageX || this.initPageX === 0) {
20717             // figure out how much this thing has moved
20718             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20719             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20720
20721             this.setInitPosition(dx, dy);
20722
20723         // This is the first time we have detected the element's position
20724         } else {
20725             this.setInitPosition();
20726         }
20727
20728         if (this.constrainX) {
20729             this.setXConstraint( this.leftConstraint,
20730                                  this.rightConstraint,
20731                                  this.xTickSize        );
20732         }
20733
20734         if (this.constrainY) {
20735             this.setYConstraint( this.topConstraint,
20736                                  this.bottomConstraint,
20737                                  this.yTickSize         );
20738         }
20739     },
20740
20741     /**
20742      * Normally the drag element is moved pixel by pixel, but we can specify
20743      * that it move a number of pixels at a time.  This method resolves the
20744      * location when we have it set up like this.
20745      * @method getTick
20746      * @param {int} val where we want to place the object
20747      * @param {int[]} tickArray sorted array of valid points
20748      * @return {int} the closest tick
20749      * @private
20750      */
20751     getTick: function(val, tickArray) {
20752
20753         if (!tickArray) {
20754             // If tick interval is not defined, it is effectively 1 pixel,
20755             // so we return the value passed to us.
20756             return val;
20757         } else if (tickArray[0] >= val) {
20758             // The value is lower than the first tick, so we return the first
20759             // tick.
20760             return tickArray[0];
20761         } else {
20762             for (var i=0, len=tickArray.length; i<len; ++i) {
20763                 var next = i + 1;
20764                 if (tickArray[next] && tickArray[next] >= val) {
20765                     var diff1 = val - tickArray[i];
20766                     var diff2 = tickArray[next] - val;
20767                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20768                 }
20769             }
20770
20771             // The value is larger than the last tick, so we return the last
20772             // tick.
20773             return tickArray[tickArray.length - 1];
20774         }
20775     },
20776
20777     /**
20778      * toString method
20779      * @method toString
20780      * @return {string} string representation of the dd obj
20781      */
20782     toString: function() {
20783         return ("DragDrop " + this.id);
20784     }
20785
20786 });
20787
20788 })();
20789 /*
20790  * Based on:
20791  * Ext JS Library 1.1.1
20792  * Copyright(c) 2006-2007, Ext JS, LLC.
20793  *
20794  * Originally Released Under LGPL - original licence link has changed is not relivant.
20795  *
20796  * Fork - LGPL
20797  * <script type="text/javascript">
20798  */
20799
20800
20801 /**
20802  * The drag and drop utility provides a framework for building drag and drop
20803  * applications.  In addition to enabling drag and drop for specific elements,
20804  * the drag and drop elements are tracked by the manager class, and the
20805  * interactions between the various elements are tracked during the drag and
20806  * the implementing code is notified about these important moments.
20807  */
20808
20809 // Only load the library once.  Rewriting the manager class would orphan
20810 // existing drag and drop instances.
20811 if (!Roo.dd.DragDropMgr) {
20812
20813 /**
20814  * @class Roo.dd.DragDropMgr
20815  * DragDropMgr is a singleton that tracks the element interaction for
20816  * all DragDrop items in the window.  Generally, you will not call
20817  * this class directly, but it does have helper methods that could
20818  * be useful in your DragDrop implementations.
20819  * @static
20820  */
20821 Roo.dd.DragDropMgr = function() {
20822
20823     var Event = Roo.EventManager;
20824
20825     return {
20826
20827         /**
20828          * Two dimensional Array of registered DragDrop objects.  The first
20829          * dimension is the DragDrop item group, the second the DragDrop
20830          * object.
20831          * @property ids
20832          * @type {string: string}
20833          * @private
20834          * @static
20835          */
20836         ids: {},
20837
20838         /**
20839          * Array of element ids defined as drag handles.  Used to determine
20840          * if the element that generated the mousedown event is actually the
20841          * handle and not the html element itself.
20842          * @property handleIds
20843          * @type {string: string}
20844          * @private
20845          * @static
20846          */
20847         handleIds: {},
20848
20849         /**
20850          * the DragDrop object that is currently being dragged
20851          * @property dragCurrent
20852          * @type DragDrop
20853          * @private
20854          * @static
20855          **/
20856         dragCurrent: null,
20857
20858         /**
20859          * the DragDrop object(s) that are being hovered over
20860          * @property dragOvers
20861          * @type Array
20862          * @private
20863          * @static
20864          */
20865         dragOvers: {},
20866
20867         /**
20868          * the X distance between the cursor and the object being dragged
20869          * @property deltaX
20870          * @type int
20871          * @private
20872          * @static
20873          */
20874         deltaX: 0,
20875
20876         /**
20877          * the Y distance between the cursor and the object being dragged
20878          * @property deltaY
20879          * @type int
20880          * @private
20881          * @static
20882          */
20883         deltaY: 0,
20884
20885         /**
20886          * Flag to determine if we should prevent the default behavior of the
20887          * events we define. By default this is true, but this can be set to
20888          * false if you need the default behavior (not recommended)
20889          * @property preventDefault
20890          * @type boolean
20891          * @static
20892          */
20893         preventDefault: true,
20894
20895         /**
20896          * Flag to determine if we should stop the propagation of the events
20897          * we generate. This is true by default but you may want to set it to
20898          * false if the html element contains other features that require the
20899          * mouse click.
20900          * @property stopPropagation
20901          * @type boolean
20902          * @static
20903          */
20904         stopPropagation: true,
20905
20906         /**
20907          * Internal flag that is set to true when drag and drop has been
20908          * intialized
20909          * @property initialized
20910          * @private
20911          * @static
20912          */
20913         initalized: false,
20914
20915         /**
20916          * All drag and drop can be disabled.
20917          * @property locked
20918          * @private
20919          * @static
20920          */
20921         locked: false,
20922
20923         /**
20924          * Called the first time an element is registered.
20925          * @method init
20926          * @private
20927          * @static
20928          */
20929         init: function() {
20930             this.initialized = true;
20931         },
20932
20933         /**
20934          * In point mode, drag and drop interaction is defined by the
20935          * location of the cursor during the drag/drop
20936          * @property POINT
20937          * @type int
20938          * @static
20939          */
20940         POINT: 0,
20941
20942         /**
20943          * In intersect mode, drag and drop interactio nis defined by the
20944          * overlap of two or more drag and drop objects.
20945          * @property INTERSECT
20946          * @type int
20947          * @static
20948          */
20949         INTERSECT: 1,
20950
20951         /**
20952          * The current drag and drop mode.  Default: POINT
20953          * @property mode
20954          * @type int
20955          * @static
20956          */
20957         mode: 0,
20958
20959         /**
20960          * Runs method on all drag and drop objects
20961          * @method _execOnAll
20962          * @private
20963          * @static
20964          */
20965         _execOnAll: function(sMethod, args) {
20966             for (var i in this.ids) {
20967                 for (var j in this.ids[i]) {
20968                     var oDD = this.ids[i][j];
20969                     if (! this.isTypeOfDD(oDD)) {
20970                         continue;
20971                     }
20972                     oDD[sMethod].apply(oDD, args);
20973                 }
20974             }
20975         },
20976
20977         /**
20978          * Drag and drop initialization.  Sets up the global event handlers
20979          * @method _onLoad
20980          * @private
20981          * @static
20982          */
20983         _onLoad: function() {
20984
20985             this.init();
20986
20987             if (!Roo.isTouch) {
20988                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20989                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20990             }
20991             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20992             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20993             
20994             Event.on(window,   "unload",    this._onUnload, this, true);
20995             Event.on(window,   "resize",    this._onResize, this, true);
20996             // Event.on(window,   "mouseout",    this._test);
20997
20998         },
20999
21000         /**
21001          * Reset constraints on all drag and drop objs
21002          * @method _onResize
21003          * @private
21004          * @static
21005          */
21006         _onResize: function(e) {
21007             this._execOnAll("resetConstraints", []);
21008         },
21009
21010         /**
21011          * Lock all drag and drop functionality
21012          * @method lock
21013          * @static
21014          */
21015         lock: function() { this.locked = true; },
21016
21017         /**
21018          * Unlock all drag and drop functionality
21019          * @method unlock
21020          * @static
21021          */
21022         unlock: function() { this.locked = false; },
21023
21024         /**
21025          * Is drag and drop locked?
21026          * @method isLocked
21027          * @return {boolean} True if drag and drop is locked, false otherwise.
21028          * @static
21029          */
21030         isLocked: function() { return this.locked; },
21031
21032         /**
21033          * Location cache that is set for all drag drop objects when a drag is
21034          * initiated, cleared when the drag is finished.
21035          * @property locationCache
21036          * @private
21037          * @static
21038          */
21039         locationCache: {},
21040
21041         /**
21042          * Set useCache to false if you want to force object the lookup of each
21043          * drag and drop linked element constantly during a drag.
21044          * @property useCache
21045          * @type boolean
21046          * @static
21047          */
21048         useCache: true,
21049
21050         /**
21051          * The number of pixels that the mouse needs to move after the
21052          * mousedown before the drag is initiated.  Default=3;
21053          * @property clickPixelThresh
21054          * @type int
21055          * @static
21056          */
21057         clickPixelThresh: 3,
21058
21059         /**
21060          * The number of milliseconds after the mousedown event to initiate the
21061          * drag if we don't get a mouseup event. Default=1000
21062          * @property clickTimeThresh
21063          * @type int
21064          * @static
21065          */
21066         clickTimeThresh: 350,
21067
21068         /**
21069          * Flag that indicates that either the drag pixel threshold or the
21070          * mousdown time threshold has been met
21071          * @property dragThreshMet
21072          * @type boolean
21073          * @private
21074          * @static
21075          */
21076         dragThreshMet: false,
21077
21078         /**
21079          * Timeout used for the click time threshold
21080          * @property clickTimeout
21081          * @type Object
21082          * @private
21083          * @static
21084          */
21085         clickTimeout: null,
21086
21087         /**
21088          * The X position of the mousedown event stored for later use when a
21089          * drag threshold is met.
21090          * @property startX
21091          * @type int
21092          * @private
21093          * @static
21094          */
21095         startX: 0,
21096
21097         /**
21098          * The Y position of the mousedown event stored for later use when a
21099          * drag threshold is met.
21100          * @property startY
21101          * @type int
21102          * @private
21103          * @static
21104          */
21105         startY: 0,
21106
21107         /**
21108          * Each DragDrop instance must be registered with the DragDropMgr.
21109          * This is executed in DragDrop.init()
21110          * @method regDragDrop
21111          * @param {DragDrop} oDD the DragDrop object to register
21112          * @param {String} sGroup the name of the group this element belongs to
21113          * @static
21114          */
21115         regDragDrop: function(oDD, sGroup) {
21116             if (!this.initialized) { this.init(); }
21117
21118             if (!this.ids[sGroup]) {
21119                 this.ids[sGroup] = {};
21120             }
21121             this.ids[sGroup][oDD.id] = oDD;
21122         },
21123
21124         /**
21125          * Removes the supplied dd instance from the supplied group. Executed
21126          * by DragDrop.removeFromGroup, so don't call this function directly.
21127          * @method removeDDFromGroup
21128          * @private
21129          * @static
21130          */
21131         removeDDFromGroup: function(oDD, sGroup) {
21132             if (!this.ids[sGroup]) {
21133                 this.ids[sGroup] = {};
21134             }
21135
21136             var obj = this.ids[sGroup];
21137             if (obj && obj[oDD.id]) {
21138                 delete obj[oDD.id];
21139             }
21140         },
21141
21142         /**
21143          * Unregisters a drag and drop item.  This is executed in
21144          * DragDrop.unreg, use that method instead of calling this directly.
21145          * @method _remove
21146          * @private
21147          * @static
21148          */
21149         _remove: function(oDD) {
21150             for (var g in oDD.groups) {
21151                 if (g && this.ids[g][oDD.id]) {
21152                     delete this.ids[g][oDD.id];
21153                 }
21154             }
21155             delete this.handleIds[oDD.id];
21156         },
21157
21158         /**
21159          * Each DragDrop handle element must be registered.  This is done
21160          * automatically when executing DragDrop.setHandleElId()
21161          * @method regHandle
21162          * @param {String} sDDId the DragDrop id this element is a handle for
21163          * @param {String} sHandleId the id of the element that is the drag
21164          * handle
21165          * @static
21166          */
21167         regHandle: function(sDDId, sHandleId) {
21168             if (!this.handleIds[sDDId]) {
21169                 this.handleIds[sDDId] = {};
21170             }
21171             this.handleIds[sDDId][sHandleId] = sHandleId;
21172         },
21173
21174         /**
21175          * Utility function to determine if a given element has been
21176          * registered as a drag drop item.
21177          * @method isDragDrop
21178          * @param {String} id the element id to check
21179          * @return {boolean} true if this element is a DragDrop item,
21180          * false otherwise
21181          * @static
21182          */
21183         isDragDrop: function(id) {
21184             return ( this.getDDById(id) ) ? true : false;
21185         },
21186
21187         /**
21188          * Returns the drag and drop instances that are in all groups the
21189          * passed in instance belongs to.
21190          * @method getRelated
21191          * @param {DragDrop} p_oDD the obj to get related data for
21192          * @param {boolean} bTargetsOnly if true, only return targetable objs
21193          * @return {DragDrop[]} the related instances
21194          * @static
21195          */
21196         getRelated: function(p_oDD, bTargetsOnly) {
21197             var oDDs = [];
21198             for (var i in p_oDD.groups) {
21199                 for (j in this.ids[i]) {
21200                     var dd = this.ids[i][j];
21201                     if (! this.isTypeOfDD(dd)) {
21202                         continue;
21203                     }
21204                     if (!bTargetsOnly || dd.isTarget) {
21205                         oDDs[oDDs.length] = dd;
21206                     }
21207                 }
21208             }
21209
21210             return oDDs;
21211         },
21212
21213         /**
21214          * Returns true if the specified dd target is a legal target for
21215          * the specifice drag obj
21216          * @method isLegalTarget
21217          * @param {DragDrop} the drag obj
21218          * @param {DragDrop} the target
21219          * @return {boolean} true if the target is a legal target for the
21220          * dd obj
21221          * @static
21222          */
21223         isLegalTarget: function (oDD, oTargetDD) {
21224             var targets = this.getRelated(oDD, true);
21225             for (var i=0, len=targets.length;i<len;++i) {
21226                 if (targets[i].id == oTargetDD.id) {
21227                     return true;
21228                 }
21229             }
21230
21231             return false;
21232         },
21233
21234         /**
21235          * My goal is to be able to transparently determine if an object is
21236          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21237          * returns "object", oDD.constructor.toString() always returns
21238          * "DragDrop" and not the name of the subclass.  So for now it just
21239          * evaluates a well-known variable in DragDrop.
21240          * @method isTypeOfDD
21241          * @param {Object} the object to evaluate
21242          * @return {boolean} true if typeof oDD = DragDrop
21243          * @static
21244          */
21245         isTypeOfDD: function (oDD) {
21246             return (oDD && oDD.__ygDragDrop);
21247         },
21248
21249         /**
21250          * Utility function to determine if a given element has been
21251          * registered as a drag drop handle for the given Drag Drop object.
21252          * @method isHandle
21253          * @param {String} id the element id to check
21254          * @return {boolean} true if this element is a DragDrop handle, false
21255          * otherwise
21256          * @static
21257          */
21258         isHandle: function(sDDId, sHandleId) {
21259             return ( this.handleIds[sDDId] &&
21260                             this.handleIds[sDDId][sHandleId] );
21261         },
21262
21263         /**
21264          * Returns the DragDrop instance for a given id
21265          * @method getDDById
21266          * @param {String} id the id of the DragDrop object
21267          * @return {DragDrop} the drag drop object, null if it is not found
21268          * @static
21269          */
21270         getDDById: function(id) {
21271             for (var i in this.ids) {
21272                 if (this.ids[i][id]) {
21273                     return this.ids[i][id];
21274                 }
21275             }
21276             return null;
21277         },
21278
21279         /**
21280          * Fired after a registered DragDrop object gets the mousedown event.
21281          * Sets up the events required to track the object being dragged
21282          * @method handleMouseDown
21283          * @param {Event} e the event
21284          * @param oDD the DragDrop object being dragged
21285          * @private
21286          * @static
21287          */
21288         handleMouseDown: function(e, oDD) {
21289             if(Roo.QuickTips){
21290                 Roo.QuickTips.disable();
21291             }
21292             this.currentTarget = e.getTarget();
21293
21294             this.dragCurrent = oDD;
21295
21296             var el = oDD.getEl();
21297
21298             // track start position
21299             this.startX = e.getPageX();
21300             this.startY = e.getPageY();
21301
21302             this.deltaX = this.startX - el.offsetLeft;
21303             this.deltaY = this.startY - el.offsetTop;
21304
21305             this.dragThreshMet = false;
21306
21307             this.clickTimeout = setTimeout(
21308                     function() {
21309                         var DDM = Roo.dd.DDM;
21310                         DDM.startDrag(DDM.startX, DDM.startY);
21311                     },
21312                     this.clickTimeThresh );
21313         },
21314
21315         /**
21316          * Fired when either the drag pixel threshol or the mousedown hold
21317          * time threshold has been met.
21318          * @method startDrag
21319          * @param x {int} the X position of the original mousedown
21320          * @param y {int} the Y position of the original mousedown
21321          * @static
21322          */
21323         startDrag: function(x, y) {
21324             clearTimeout(this.clickTimeout);
21325             if (this.dragCurrent) {
21326                 this.dragCurrent.b4StartDrag(x, y);
21327                 this.dragCurrent.startDrag(x, y);
21328             }
21329             this.dragThreshMet = true;
21330         },
21331
21332         /**
21333          * Internal function to handle the mouseup event.  Will be invoked
21334          * from the context of the document.
21335          * @method handleMouseUp
21336          * @param {Event} e the event
21337          * @private
21338          * @static
21339          */
21340         handleMouseUp: function(e) {
21341
21342             if(Roo.QuickTips){
21343                 Roo.QuickTips.enable();
21344             }
21345             if (! this.dragCurrent) {
21346                 return;
21347             }
21348
21349             clearTimeout(this.clickTimeout);
21350
21351             if (this.dragThreshMet) {
21352                 this.fireEvents(e, true);
21353             } else {
21354             }
21355
21356             this.stopDrag(e);
21357
21358             this.stopEvent(e);
21359         },
21360
21361         /**
21362          * Utility to stop event propagation and event default, if these
21363          * features are turned on.
21364          * @method stopEvent
21365          * @param {Event} e the event as returned by this.getEvent()
21366          * @static
21367          */
21368         stopEvent: function(e){
21369             if(this.stopPropagation) {
21370                 e.stopPropagation();
21371             }
21372
21373             if (this.preventDefault) {
21374                 e.preventDefault();
21375             }
21376         },
21377
21378         /**
21379          * Internal function to clean up event handlers after the drag
21380          * operation is complete
21381          * @method stopDrag
21382          * @param {Event} e the event
21383          * @private
21384          * @static
21385          */
21386         stopDrag: function(e) {
21387             // Fire the drag end event for the item that was dragged
21388             if (this.dragCurrent) {
21389                 if (this.dragThreshMet) {
21390                     this.dragCurrent.b4EndDrag(e);
21391                     this.dragCurrent.endDrag(e);
21392                 }
21393
21394                 this.dragCurrent.onMouseUp(e);
21395             }
21396
21397             this.dragCurrent = null;
21398             this.dragOvers = {};
21399         },
21400
21401         /**
21402          * Internal function to handle the mousemove event.  Will be invoked
21403          * from the context of the html element.
21404          *
21405          * @TODO figure out what we can do about mouse events lost when the
21406          * user drags objects beyond the window boundary.  Currently we can
21407          * detect this in internet explorer by verifying that the mouse is
21408          * down during the mousemove event.  Firefox doesn't give us the
21409          * button state on the mousemove event.
21410          * @method handleMouseMove
21411          * @param {Event} e the event
21412          * @private
21413          * @static
21414          */
21415         handleMouseMove: function(e) {
21416             if (! this.dragCurrent) {
21417                 return true;
21418             }
21419
21420             // var button = e.which || e.button;
21421
21422             // check for IE mouseup outside of page boundary
21423             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21424                 this.stopEvent(e);
21425                 return this.handleMouseUp(e);
21426             }
21427
21428             if (!this.dragThreshMet) {
21429                 var diffX = Math.abs(this.startX - e.getPageX());
21430                 var diffY = Math.abs(this.startY - e.getPageY());
21431                 if (diffX > this.clickPixelThresh ||
21432                             diffY > this.clickPixelThresh) {
21433                     this.startDrag(this.startX, this.startY);
21434                 }
21435             }
21436
21437             if (this.dragThreshMet) {
21438                 this.dragCurrent.b4Drag(e);
21439                 this.dragCurrent.onDrag(e);
21440                 if(!this.dragCurrent.moveOnly){
21441                     this.fireEvents(e, false);
21442                 }
21443             }
21444
21445             this.stopEvent(e);
21446
21447             return true;
21448         },
21449
21450         /**
21451          * Iterates over all of the DragDrop elements to find ones we are
21452          * hovering over or dropping on
21453          * @method fireEvents
21454          * @param {Event} e the event
21455          * @param {boolean} isDrop is this a drop op or a mouseover op?
21456          * @private
21457          * @static
21458          */
21459         fireEvents: function(e, isDrop) {
21460             var dc = this.dragCurrent;
21461
21462             // If the user did the mouse up outside of the window, we could
21463             // get here even though we have ended the drag.
21464             if (!dc || dc.isLocked()) {
21465                 return;
21466             }
21467
21468             var pt = e.getPoint();
21469
21470             // cache the previous dragOver array
21471             var oldOvers = [];
21472
21473             var outEvts   = [];
21474             var overEvts  = [];
21475             var dropEvts  = [];
21476             var enterEvts = [];
21477
21478             // Check to see if the object(s) we were hovering over is no longer
21479             // being hovered over so we can fire the onDragOut event
21480             for (var i in this.dragOvers) {
21481
21482                 var ddo = this.dragOvers[i];
21483
21484                 if (! this.isTypeOfDD(ddo)) {
21485                     continue;
21486                 }
21487
21488                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21489                     outEvts.push( ddo );
21490                 }
21491
21492                 oldOvers[i] = true;
21493                 delete this.dragOvers[i];
21494             }
21495
21496             for (var sGroup in dc.groups) {
21497
21498                 if ("string" != typeof sGroup) {
21499                     continue;
21500                 }
21501
21502                 for (i in this.ids[sGroup]) {
21503                     var oDD = this.ids[sGroup][i];
21504                     if (! this.isTypeOfDD(oDD)) {
21505                         continue;
21506                     }
21507
21508                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21509                         if (this.isOverTarget(pt, oDD, this.mode)) {
21510                             // look for drop interactions
21511                             if (isDrop) {
21512                                 dropEvts.push( oDD );
21513                             // look for drag enter and drag over interactions
21514                             } else {
21515
21516                                 // initial drag over: dragEnter fires
21517                                 if (!oldOvers[oDD.id]) {
21518                                     enterEvts.push( oDD );
21519                                 // subsequent drag overs: dragOver fires
21520                                 } else {
21521                                     overEvts.push( oDD );
21522                                 }
21523
21524                                 this.dragOvers[oDD.id] = oDD;
21525                             }
21526                         }
21527                     }
21528                 }
21529             }
21530
21531             if (this.mode) {
21532                 if (outEvts.length) {
21533                     dc.b4DragOut(e, outEvts);
21534                     dc.onDragOut(e, outEvts);
21535                 }
21536
21537                 if (enterEvts.length) {
21538                     dc.onDragEnter(e, enterEvts);
21539                 }
21540
21541                 if (overEvts.length) {
21542                     dc.b4DragOver(e, overEvts);
21543                     dc.onDragOver(e, overEvts);
21544                 }
21545
21546                 if (dropEvts.length) {
21547                     dc.b4DragDrop(e, dropEvts);
21548                     dc.onDragDrop(e, dropEvts);
21549                 }
21550
21551             } else {
21552                 // fire dragout events
21553                 var len = 0;
21554                 for (i=0, len=outEvts.length; i<len; ++i) {
21555                     dc.b4DragOut(e, outEvts[i].id);
21556                     dc.onDragOut(e, outEvts[i].id);
21557                 }
21558
21559                 // fire enter events
21560                 for (i=0,len=enterEvts.length; i<len; ++i) {
21561                     // dc.b4DragEnter(e, oDD.id);
21562                     dc.onDragEnter(e, enterEvts[i].id);
21563                 }
21564
21565                 // fire over events
21566                 for (i=0,len=overEvts.length; i<len; ++i) {
21567                     dc.b4DragOver(e, overEvts[i].id);
21568                     dc.onDragOver(e, overEvts[i].id);
21569                 }
21570
21571                 // fire drop events
21572                 for (i=0, len=dropEvts.length; i<len; ++i) {
21573                     dc.b4DragDrop(e, dropEvts[i].id);
21574                     dc.onDragDrop(e, dropEvts[i].id);
21575                 }
21576
21577             }
21578
21579             // notify about a drop that did not find a target
21580             if (isDrop && !dropEvts.length) {
21581                 dc.onInvalidDrop(e);
21582             }
21583
21584         },
21585
21586         /**
21587          * Helper function for getting the best match from the list of drag
21588          * and drop objects returned by the drag and drop events when we are
21589          * in INTERSECT mode.  It returns either the first object that the
21590          * cursor is over, or the object that has the greatest overlap with
21591          * the dragged element.
21592          * @method getBestMatch
21593          * @param  {DragDrop[]} dds The array of drag and drop objects
21594          * targeted
21595          * @return {DragDrop}       The best single match
21596          * @static
21597          */
21598         getBestMatch: function(dds) {
21599             var winner = null;
21600             // Return null if the input is not what we expect
21601             //if (!dds || !dds.length || dds.length == 0) {
21602                // winner = null;
21603             // If there is only one item, it wins
21604             //} else if (dds.length == 1) {
21605
21606             var len = dds.length;
21607
21608             if (len == 1) {
21609                 winner = dds[0];
21610             } else {
21611                 // Loop through the targeted items
21612                 for (var i=0; i<len; ++i) {
21613                     var dd = dds[i];
21614                     // If the cursor is over the object, it wins.  If the
21615                     // cursor is over multiple matches, the first one we come
21616                     // to wins.
21617                     if (dd.cursorIsOver) {
21618                         winner = dd;
21619                         break;
21620                     // Otherwise the object with the most overlap wins
21621                     } else {
21622                         if (!winner ||
21623                             winner.overlap.getArea() < dd.overlap.getArea()) {
21624                             winner = dd;
21625                         }
21626                     }
21627                 }
21628             }
21629
21630             return winner;
21631         },
21632
21633         /**
21634          * Refreshes the cache of the top-left and bottom-right points of the
21635          * drag and drop objects in the specified group(s).  This is in the
21636          * format that is stored in the drag and drop instance, so typical
21637          * usage is:
21638          * <code>
21639          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21640          * </code>
21641          * Alternatively:
21642          * <code>
21643          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21644          * </code>
21645          * @TODO this really should be an indexed array.  Alternatively this
21646          * method could accept both.
21647          * @method refreshCache
21648          * @param {Object} groups an associative array of groups to refresh
21649          * @static
21650          */
21651         refreshCache: function(groups) {
21652             for (var sGroup in groups) {
21653                 if ("string" != typeof sGroup) {
21654                     continue;
21655                 }
21656                 for (var i in this.ids[sGroup]) {
21657                     var oDD = this.ids[sGroup][i];
21658
21659                     if (this.isTypeOfDD(oDD)) {
21660                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21661                         var loc = this.getLocation(oDD);
21662                         if (loc) {
21663                             this.locationCache[oDD.id] = loc;
21664                         } else {
21665                             delete this.locationCache[oDD.id];
21666                             // this will unregister the drag and drop object if
21667                             // the element is not in a usable state
21668                             // oDD.unreg();
21669                         }
21670                     }
21671                 }
21672             }
21673         },
21674
21675         /**
21676          * This checks to make sure an element exists and is in the DOM.  The
21677          * main purpose is to handle cases where innerHTML is used to remove
21678          * drag and drop objects from the DOM.  IE provides an 'unspecified
21679          * error' when trying to access the offsetParent of such an element
21680          * @method verifyEl
21681          * @param {HTMLElement} el the element to check
21682          * @return {boolean} true if the element looks usable
21683          * @static
21684          */
21685         verifyEl: function(el) {
21686             if (el) {
21687                 var parent;
21688                 if(Roo.isIE){
21689                     try{
21690                         parent = el.offsetParent;
21691                     }catch(e){}
21692                 }else{
21693                     parent = el.offsetParent;
21694                 }
21695                 if (parent) {
21696                     return true;
21697                 }
21698             }
21699
21700             return false;
21701         },
21702
21703         /**
21704          * Returns a Region object containing the drag and drop element's position
21705          * and size, including the padding configured for it
21706          * @method getLocation
21707          * @param {DragDrop} oDD the drag and drop object to get the
21708          *                       location for
21709          * @return {Roo.lib.Region} a Region object representing the total area
21710          *                             the element occupies, including any padding
21711          *                             the instance is configured for.
21712          * @static
21713          */
21714         getLocation: function(oDD) {
21715             if (! this.isTypeOfDD(oDD)) {
21716                 return null;
21717             }
21718
21719             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21720
21721             try {
21722                 pos= Roo.lib.Dom.getXY(el);
21723             } catch (e) { }
21724
21725             if (!pos) {
21726                 return null;
21727             }
21728
21729             x1 = pos[0];
21730             x2 = x1 + el.offsetWidth;
21731             y1 = pos[1];
21732             y2 = y1 + el.offsetHeight;
21733
21734             t = y1 - oDD.padding[0];
21735             r = x2 + oDD.padding[1];
21736             b = y2 + oDD.padding[2];
21737             l = x1 - oDD.padding[3];
21738
21739             return new Roo.lib.Region( t, r, b, l );
21740         },
21741
21742         /**
21743          * Checks the cursor location to see if it over the target
21744          * @method isOverTarget
21745          * @param {Roo.lib.Point} pt The point to evaluate
21746          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21747          * @return {boolean} true if the mouse is over the target
21748          * @private
21749          * @static
21750          */
21751         isOverTarget: function(pt, oTarget, intersect) {
21752             // use cache if available
21753             var loc = this.locationCache[oTarget.id];
21754             if (!loc || !this.useCache) {
21755                 loc = this.getLocation(oTarget);
21756                 this.locationCache[oTarget.id] = loc;
21757
21758             }
21759
21760             if (!loc) {
21761                 return false;
21762             }
21763
21764             oTarget.cursorIsOver = loc.contains( pt );
21765
21766             // DragDrop is using this as a sanity check for the initial mousedown
21767             // in this case we are done.  In POINT mode, if the drag obj has no
21768             // contraints, we are also done. Otherwise we need to evaluate the
21769             // location of the target as related to the actual location of the
21770             // dragged element.
21771             var dc = this.dragCurrent;
21772             if (!dc || !dc.getTargetCoord ||
21773                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21774                 return oTarget.cursorIsOver;
21775             }
21776
21777             oTarget.overlap = null;
21778
21779             // Get the current location of the drag element, this is the
21780             // location of the mouse event less the delta that represents
21781             // where the original mousedown happened on the element.  We
21782             // need to consider constraints and ticks as well.
21783             var pos = dc.getTargetCoord(pt.x, pt.y);
21784
21785             var el = dc.getDragEl();
21786             var curRegion = new Roo.lib.Region( pos.y,
21787                                                    pos.x + el.offsetWidth,
21788                                                    pos.y + el.offsetHeight,
21789                                                    pos.x );
21790
21791             var overlap = curRegion.intersect(loc);
21792
21793             if (overlap) {
21794                 oTarget.overlap = overlap;
21795                 return (intersect) ? true : oTarget.cursorIsOver;
21796             } else {
21797                 return false;
21798             }
21799         },
21800
21801         /**
21802          * unload event handler
21803          * @method _onUnload
21804          * @private
21805          * @static
21806          */
21807         _onUnload: function(e, me) {
21808             Roo.dd.DragDropMgr.unregAll();
21809         },
21810
21811         /**
21812          * Cleans up the drag and drop events and objects.
21813          * @method unregAll
21814          * @private
21815          * @static
21816          */
21817         unregAll: function() {
21818
21819             if (this.dragCurrent) {
21820                 this.stopDrag();
21821                 this.dragCurrent = null;
21822             }
21823
21824             this._execOnAll("unreg", []);
21825
21826             for (i in this.elementCache) {
21827                 delete this.elementCache[i];
21828             }
21829
21830             this.elementCache = {};
21831             this.ids = {};
21832         },
21833
21834         /**
21835          * A cache of DOM elements
21836          * @property elementCache
21837          * @private
21838          * @static
21839          */
21840         elementCache: {},
21841
21842         /**
21843          * Get the wrapper for the DOM element specified
21844          * @method getElWrapper
21845          * @param {String} id the id of the element to get
21846          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21847          * @private
21848          * @deprecated This wrapper isn't that useful
21849          * @static
21850          */
21851         getElWrapper: function(id) {
21852             var oWrapper = this.elementCache[id];
21853             if (!oWrapper || !oWrapper.el) {
21854                 oWrapper = this.elementCache[id] =
21855                     new this.ElementWrapper(Roo.getDom(id));
21856             }
21857             return oWrapper;
21858         },
21859
21860         /**
21861          * Returns the actual DOM element
21862          * @method getElement
21863          * @param {String} id the id of the elment to get
21864          * @return {Object} The element
21865          * @deprecated use Roo.getDom instead
21866          * @static
21867          */
21868         getElement: function(id) {
21869             return Roo.getDom(id);
21870         },
21871
21872         /**
21873          * Returns the style property for the DOM element (i.e.,
21874          * document.getElById(id).style)
21875          * @method getCss
21876          * @param {String} id the id of the elment to get
21877          * @return {Object} The style property of the element
21878          * @deprecated use Roo.getDom instead
21879          * @static
21880          */
21881         getCss: function(id) {
21882             var el = Roo.getDom(id);
21883             return (el) ? el.style : null;
21884         },
21885
21886         /**
21887          * Inner class for cached elements
21888          * @class DragDropMgr.ElementWrapper
21889          * @for DragDropMgr
21890          * @private
21891          * @deprecated
21892          */
21893         ElementWrapper: function(el) {
21894                 /**
21895                  * The element
21896                  * @property el
21897                  */
21898                 this.el = el || null;
21899                 /**
21900                  * The element id
21901                  * @property id
21902                  */
21903                 this.id = this.el && el.id;
21904                 /**
21905                  * A reference to the style property
21906                  * @property css
21907                  */
21908                 this.css = this.el && el.style;
21909             },
21910
21911         /**
21912          * Returns the X position of an html element
21913          * @method getPosX
21914          * @param el the element for which to get the position
21915          * @return {int} the X coordinate
21916          * @for DragDropMgr
21917          * @deprecated use Roo.lib.Dom.getX instead
21918          * @static
21919          */
21920         getPosX: function(el) {
21921             return Roo.lib.Dom.getX(el);
21922         },
21923
21924         /**
21925          * Returns the Y position of an html element
21926          * @method getPosY
21927          * @param el the element for which to get the position
21928          * @return {int} the Y coordinate
21929          * @deprecated use Roo.lib.Dom.getY instead
21930          * @static
21931          */
21932         getPosY: function(el) {
21933             return Roo.lib.Dom.getY(el);
21934         },
21935
21936         /**
21937          * Swap two nodes.  In IE, we use the native method, for others we
21938          * emulate the IE behavior
21939          * @method swapNode
21940          * @param n1 the first node to swap
21941          * @param n2 the other node to swap
21942          * @static
21943          */
21944         swapNode: function(n1, n2) {
21945             if (n1.swapNode) {
21946                 n1.swapNode(n2);
21947             } else {
21948                 var p = n2.parentNode;
21949                 var s = n2.nextSibling;
21950
21951                 if (s == n1) {
21952                     p.insertBefore(n1, n2);
21953                 } else if (n2 == n1.nextSibling) {
21954                     p.insertBefore(n2, n1);
21955                 } else {
21956                     n1.parentNode.replaceChild(n2, n1);
21957                     p.insertBefore(n1, s);
21958                 }
21959             }
21960         },
21961
21962         /**
21963          * Returns the current scroll position
21964          * @method getScroll
21965          * @private
21966          * @static
21967          */
21968         getScroll: function () {
21969             var t, l, dde=document.documentElement, db=document.body;
21970             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21971                 t = dde.scrollTop;
21972                 l = dde.scrollLeft;
21973             } else if (db) {
21974                 t = db.scrollTop;
21975                 l = db.scrollLeft;
21976             } else {
21977
21978             }
21979             return { top: t, left: l };
21980         },
21981
21982         /**
21983          * Returns the specified element style property
21984          * @method getStyle
21985          * @param {HTMLElement} el          the element
21986          * @param {string}      styleProp   the style property
21987          * @return {string} The value of the style property
21988          * @deprecated use Roo.lib.Dom.getStyle
21989          * @static
21990          */
21991         getStyle: function(el, styleProp) {
21992             return Roo.fly(el).getStyle(styleProp);
21993         },
21994
21995         /**
21996          * Gets the scrollTop
21997          * @method getScrollTop
21998          * @return {int} the document's scrollTop
21999          * @static
22000          */
22001         getScrollTop: function () { return this.getScroll().top; },
22002
22003         /**
22004          * Gets the scrollLeft
22005          * @method getScrollLeft
22006          * @return {int} the document's scrollTop
22007          * @static
22008          */
22009         getScrollLeft: function () { return this.getScroll().left; },
22010
22011         /**
22012          * Sets the x/y position of an element to the location of the
22013          * target element.
22014          * @method moveToEl
22015          * @param {HTMLElement} moveEl      The element to move
22016          * @param {HTMLElement} targetEl    The position reference element
22017          * @static
22018          */
22019         moveToEl: function (moveEl, targetEl) {
22020             var aCoord = Roo.lib.Dom.getXY(targetEl);
22021             Roo.lib.Dom.setXY(moveEl, aCoord);
22022         },
22023
22024         /**
22025          * Numeric array sort function
22026          * @method numericSort
22027          * @static
22028          */
22029         numericSort: function(a, b) { return (a - b); },
22030
22031         /**
22032          * Internal counter
22033          * @property _timeoutCount
22034          * @private
22035          * @static
22036          */
22037         _timeoutCount: 0,
22038
22039         /**
22040          * Trying to make the load order less important.  Without this we get
22041          * an error if this file is loaded before the Event Utility.
22042          * @method _addListeners
22043          * @private
22044          * @static
22045          */
22046         _addListeners: function() {
22047             var DDM = Roo.dd.DDM;
22048             if ( Roo.lib.Event && document ) {
22049                 DDM._onLoad();
22050             } else {
22051                 if (DDM._timeoutCount > 2000) {
22052                 } else {
22053                     setTimeout(DDM._addListeners, 10);
22054                     if (document && document.body) {
22055                         DDM._timeoutCount += 1;
22056                     }
22057                 }
22058             }
22059         },
22060
22061         /**
22062          * Recursively searches the immediate parent and all child nodes for
22063          * the handle element in order to determine wheter or not it was
22064          * clicked.
22065          * @method handleWasClicked
22066          * @param node the html element to inspect
22067          * @static
22068          */
22069         handleWasClicked: function(node, id) {
22070             if (this.isHandle(id, node.id)) {
22071                 return true;
22072             } else {
22073                 // check to see if this is a text node child of the one we want
22074                 var p = node.parentNode;
22075
22076                 while (p) {
22077                     if (this.isHandle(id, p.id)) {
22078                         return true;
22079                     } else {
22080                         p = p.parentNode;
22081                     }
22082                 }
22083             }
22084
22085             return false;
22086         }
22087
22088     };
22089
22090 }();
22091
22092 // shorter alias, save a few bytes
22093 Roo.dd.DDM = Roo.dd.DragDropMgr;
22094 Roo.dd.DDM._addListeners();
22095
22096 }/*
22097  * Based on:
22098  * Ext JS Library 1.1.1
22099  * Copyright(c) 2006-2007, Ext JS, LLC.
22100  *
22101  * Originally Released Under LGPL - original licence link has changed is not relivant.
22102  *
22103  * Fork - LGPL
22104  * <script type="text/javascript">
22105  */
22106
22107 /**
22108  * @class Roo.dd.DD
22109  * A DragDrop implementation where the linked element follows the
22110  * mouse cursor during a drag.
22111  * @extends Roo.dd.DragDrop
22112  * @constructor
22113  * @param {String} id the id of the linked element
22114  * @param {String} sGroup the group of related DragDrop items
22115  * @param {object} config an object containing configurable attributes
22116  *                Valid properties for DD:
22117  *                    scroll
22118  */
22119 Roo.dd.DD = function(id, sGroup, config) {
22120     if (id) {
22121         this.init(id, sGroup, config);
22122     }
22123 };
22124
22125 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22126
22127     /**
22128      * When set to true, the utility automatically tries to scroll the browser
22129      * window wehn a drag and drop element is dragged near the viewport boundary.
22130      * Defaults to true.
22131      * @property scroll
22132      * @type boolean
22133      */
22134     scroll: true,
22135
22136     /**
22137      * Sets the pointer offset to the distance between the linked element's top
22138      * left corner and the location the element was clicked
22139      * @method autoOffset
22140      * @param {int} iPageX the X coordinate of the click
22141      * @param {int} iPageY the Y coordinate of the click
22142      */
22143     autoOffset: function(iPageX, iPageY) {
22144         var x = iPageX - this.startPageX;
22145         var y = iPageY - this.startPageY;
22146         this.setDelta(x, y);
22147     },
22148
22149     /**
22150      * Sets the pointer offset.  You can call this directly to force the
22151      * offset to be in a particular location (e.g., pass in 0,0 to set it
22152      * to the center of the object)
22153      * @method setDelta
22154      * @param {int} iDeltaX the distance from the left
22155      * @param {int} iDeltaY the distance from the top
22156      */
22157     setDelta: function(iDeltaX, iDeltaY) {
22158         this.deltaX = iDeltaX;
22159         this.deltaY = iDeltaY;
22160     },
22161
22162     /**
22163      * Sets the drag element to the location of the mousedown or click event,
22164      * maintaining the cursor location relative to the location on the element
22165      * that was clicked.  Override this if you want to place the element in a
22166      * location other than where the cursor is.
22167      * @method setDragElPos
22168      * @param {int} iPageX the X coordinate of the mousedown or drag event
22169      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22170      */
22171     setDragElPos: function(iPageX, iPageY) {
22172         // the first time we do this, we are going to check to make sure
22173         // the element has css positioning
22174
22175         var el = this.getDragEl();
22176         this.alignElWithMouse(el, iPageX, iPageY);
22177     },
22178
22179     /**
22180      * Sets the element to the location of the mousedown or click event,
22181      * maintaining the cursor location relative to the location on the element
22182      * that was clicked.  Override this if you want to place the element in a
22183      * location other than where the cursor is.
22184      * @method alignElWithMouse
22185      * @param {HTMLElement} el the element to move
22186      * @param {int} iPageX the X coordinate of the mousedown or drag event
22187      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22188      */
22189     alignElWithMouse: function(el, iPageX, iPageY) {
22190         var oCoord = this.getTargetCoord(iPageX, iPageY);
22191         var fly = el.dom ? el : Roo.fly(el);
22192         if (!this.deltaSetXY) {
22193             var aCoord = [oCoord.x, oCoord.y];
22194             fly.setXY(aCoord);
22195             var newLeft = fly.getLeft(true);
22196             var newTop  = fly.getTop(true);
22197             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22198         } else {
22199             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22200         }
22201
22202         this.cachePosition(oCoord.x, oCoord.y);
22203         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22204         return oCoord;
22205     },
22206
22207     /**
22208      * Saves the most recent position so that we can reset the constraints and
22209      * tick marks on-demand.  We need to know this so that we can calculate the
22210      * number of pixels the element is offset from its original position.
22211      * @method cachePosition
22212      * @param iPageX the current x position (optional, this just makes it so we
22213      * don't have to look it up again)
22214      * @param iPageY the current y position (optional, this just makes it so we
22215      * don't have to look it up again)
22216      */
22217     cachePosition: function(iPageX, iPageY) {
22218         if (iPageX) {
22219             this.lastPageX = iPageX;
22220             this.lastPageY = iPageY;
22221         } else {
22222             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22223             this.lastPageX = aCoord[0];
22224             this.lastPageY = aCoord[1];
22225         }
22226     },
22227
22228     /**
22229      * Auto-scroll the window if the dragged object has been moved beyond the
22230      * visible window boundary.
22231      * @method autoScroll
22232      * @param {int} x the drag element's x position
22233      * @param {int} y the drag element's y position
22234      * @param {int} h the height of the drag element
22235      * @param {int} w the width of the drag element
22236      * @private
22237      */
22238     autoScroll: function(x, y, h, w) {
22239
22240         if (this.scroll) {
22241             // The client height
22242             var clientH = Roo.lib.Dom.getViewWidth();
22243
22244             // The client width
22245             var clientW = Roo.lib.Dom.getViewHeight();
22246
22247             // The amt scrolled down
22248             var st = this.DDM.getScrollTop();
22249
22250             // The amt scrolled right
22251             var sl = this.DDM.getScrollLeft();
22252
22253             // Location of the bottom of the element
22254             var bot = h + y;
22255
22256             // Location of the right of the element
22257             var right = w + x;
22258
22259             // The distance from the cursor to the bottom of the visible area,
22260             // adjusted so that we don't scroll if the cursor is beyond the
22261             // element drag constraints
22262             var toBot = (clientH + st - y - this.deltaY);
22263
22264             // The distance from the cursor to the right of the visible area
22265             var toRight = (clientW + sl - x - this.deltaX);
22266
22267
22268             // How close to the edge the cursor must be before we scroll
22269             // var thresh = (document.all) ? 100 : 40;
22270             var thresh = 40;
22271
22272             // How many pixels to scroll per autoscroll op.  This helps to reduce
22273             // clunky scrolling. IE is more sensitive about this ... it needs this
22274             // value to be higher.
22275             var scrAmt = (document.all) ? 80 : 30;
22276
22277             // Scroll down if we are near the bottom of the visible page and the
22278             // obj extends below the crease
22279             if ( bot > clientH && toBot < thresh ) {
22280                 window.scrollTo(sl, st + scrAmt);
22281             }
22282
22283             // Scroll up if the window is scrolled down and the top of the object
22284             // goes above the top border
22285             if ( y < st && st > 0 && y - st < thresh ) {
22286                 window.scrollTo(sl, st - scrAmt);
22287             }
22288
22289             // Scroll right if the obj is beyond the right border and the cursor is
22290             // near the border.
22291             if ( right > clientW && toRight < thresh ) {
22292                 window.scrollTo(sl + scrAmt, st);
22293             }
22294
22295             // Scroll left if the window has been scrolled to the right and the obj
22296             // extends past the left border
22297             if ( x < sl && sl > 0 && x - sl < thresh ) {
22298                 window.scrollTo(sl - scrAmt, st);
22299             }
22300         }
22301     },
22302
22303     /**
22304      * Finds the location the element should be placed if we want to move
22305      * it to where the mouse location less the click offset would place us.
22306      * @method getTargetCoord
22307      * @param {int} iPageX the X coordinate of the click
22308      * @param {int} iPageY the Y coordinate of the click
22309      * @return an object that contains the coordinates (Object.x and Object.y)
22310      * @private
22311      */
22312     getTargetCoord: function(iPageX, iPageY) {
22313
22314
22315         var x = iPageX - this.deltaX;
22316         var y = iPageY - this.deltaY;
22317
22318         if (this.constrainX) {
22319             if (x < this.minX) { x = this.minX; }
22320             if (x > this.maxX) { x = this.maxX; }
22321         }
22322
22323         if (this.constrainY) {
22324             if (y < this.minY) { y = this.minY; }
22325             if (y > this.maxY) { y = this.maxY; }
22326         }
22327
22328         x = this.getTick(x, this.xTicks);
22329         y = this.getTick(y, this.yTicks);
22330
22331
22332         return {x:x, y:y};
22333     },
22334
22335     /*
22336      * Sets up config options specific to this class. Overrides
22337      * Roo.dd.DragDrop, but all versions of this method through the
22338      * inheritance chain are called
22339      */
22340     applyConfig: function() {
22341         Roo.dd.DD.superclass.applyConfig.call(this);
22342         this.scroll = (this.config.scroll !== false);
22343     },
22344
22345     /*
22346      * Event that fires prior to the onMouseDown event.  Overrides
22347      * Roo.dd.DragDrop.
22348      */
22349     b4MouseDown: function(e) {
22350         // this.resetConstraints();
22351         this.autoOffset(e.getPageX(),
22352                             e.getPageY());
22353     },
22354
22355     /*
22356      * Event that fires prior to the onDrag event.  Overrides
22357      * Roo.dd.DragDrop.
22358      */
22359     b4Drag: function(e) {
22360         this.setDragElPos(e.getPageX(),
22361                             e.getPageY());
22362     },
22363
22364     toString: function() {
22365         return ("DD " + this.id);
22366     }
22367
22368     //////////////////////////////////////////////////////////////////////////
22369     // Debugging ygDragDrop events that can be overridden
22370     //////////////////////////////////////////////////////////////////////////
22371     /*
22372     startDrag: function(x, y) {
22373     },
22374
22375     onDrag: function(e) {
22376     },
22377
22378     onDragEnter: function(e, id) {
22379     },
22380
22381     onDragOver: function(e, id) {
22382     },
22383
22384     onDragOut: function(e, id) {
22385     },
22386
22387     onDragDrop: function(e, id) {
22388     },
22389
22390     endDrag: function(e) {
22391     }
22392
22393     */
22394
22395 });/*
22396  * Based on:
22397  * Ext JS Library 1.1.1
22398  * Copyright(c) 2006-2007, Ext JS, LLC.
22399  *
22400  * Originally Released Under LGPL - original licence link has changed is not relivant.
22401  *
22402  * Fork - LGPL
22403  * <script type="text/javascript">
22404  */
22405
22406 /**
22407  * @class Roo.dd.DDProxy
22408  * A DragDrop implementation that inserts an empty, bordered div into
22409  * the document that follows the cursor during drag operations.  At the time of
22410  * the click, the frame div is resized to the dimensions of the linked html
22411  * element, and moved to the exact location of the linked element.
22412  *
22413  * References to the "frame" element refer to the single proxy element that
22414  * was created to be dragged in place of all DDProxy elements on the
22415  * page.
22416  *
22417  * @extends Roo.dd.DD
22418  * @constructor
22419  * @param {String} id the id of the linked html element
22420  * @param {String} sGroup the group of related DragDrop objects
22421  * @param {object} config an object containing configurable attributes
22422  *                Valid properties for DDProxy in addition to those in DragDrop:
22423  *                   resizeFrame, centerFrame, dragElId
22424  */
22425 Roo.dd.DDProxy = function(id, sGroup, config) {
22426     if (id) {
22427         this.init(id, sGroup, config);
22428         this.initFrame();
22429     }
22430 };
22431
22432 /**
22433  * The default drag frame div id
22434  * @property Roo.dd.DDProxy.dragElId
22435  * @type String
22436  * @static
22437  */
22438 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22439
22440 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22441
22442     /**
22443      * By default we resize the drag frame to be the same size as the element
22444      * we want to drag (this is to get the frame effect).  We can turn it off
22445      * if we want a different behavior.
22446      * @property resizeFrame
22447      * @type boolean
22448      */
22449     resizeFrame: true,
22450
22451     /**
22452      * By default the frame is positioned exactly where the drag element is, so
22453      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22454      * you do not have constraints on the obj is to have the drag frame centered
22455      * around the cursor.  Set centerFrame to true for this effect.
22456      * @property centerFrame
22457      * @type boolean
22458      */
22459     centerFrame: false,
22460
22461     /**
22462      * Creates the proxy element if it does not yet exist
22463      * @method createFrame
22464      */
22465     createFrame: function() {
22466         var self = this;
22467         var body = document.body;
22468
22469         if (!body || !body.firstChild) {
22470             setTimeout( function() { self.createFrame(); }, 50 );
22471             return;
22472         }
22473
22474         var div = this.getDragEl();
22475
22476         if (!div) {
22477             div    = document.createElement("div");
22478             div.id = this.dragElId;
22479             var s  = div.style;
22480
22481             s.position   = "absolute";
22482             s.visibility = "hidden";
22483             s.cursor     = "move";
22484             s.border     = "2px solid #aaa";
22485             s.zIndex     = 999;
22486
22487             // appendChild can blow up IE if invoked prior to the window load event
22488             // while rendering a table.  It is possible there are other scenarios
22489             // that would cause this to happen as well.
22490             body.insertBefore(div, body.firstChild);
22491         }
22492     },
22493
22494     /**
22495      * Initialization for the drag frame element.  Must be called in the
22496      * constructor of all subclasses
22497      * @method initFrame
22498      */
22499     initFrame: function() {
22500         this.createFrame();
22501     },
22502
22503     applyConfig: function() {
22504         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22505
22506         this.resizeFrame = (this.config.resizeFrame !== false);
22507         this.centerFrame = (this.config.centerFrame);
22508         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22509     },
22510
22511     /**
22512      * Resizes the drag frame to the dimensions of the clicked object, positions
22513      * it over the object, and finally displays it
22514      * @method showFrame
22515      * @param {int} iPageX X click position
22516      * @param {int} iPageY Y click position
22517      * @private
22518      */
22519     showFrame: function(iPageX, iPageY) {
22520         var el = this.getEl();
22521         var dragEl = this.getDragEl();
22522         var s = dragEl.style;
22523
22524         this._resizeProxy();
22525
22526         if (this.centerFrame) {
22527             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22528                            Math.round(parseInt(s.height, 10)/2) );
22529         }
22530
22531         this.setDragElPos(iPageX, iPageY);
22532
22533         Roo.fly(dragEl).show();
22534     },
22535
22536     /**
22537      * The proxy is automatically resized to the dimensions of the linked
22538      * element when a drag is initiated, unless resizeFrame is set to false
22539      * @method _resizeProxy
22540      * @private
22541      */
22542     _resizeProxy: function() {
22543         if (this.resizeFrame) {
22544             var el = this.getEl();
22545             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22546         }
22547     },
22548
22549     // overrides Roo.dd.DragDrop
22550     b4MouseDown: function(e) {
22551         var x = e.getPageX();
22552         var y = e.getPageY();
22553         this.autoOffset(x, y);
22554         this.setDragElPos(x, y);
22555     },
22556
22557     // overrides Roo.dd.DragDrop
22558     b4StartDrag: function(x, y) {
22559         // show the drag frame
22560         this.showFrame(x, y);
22561     },
22562
22563     // overrides Roo.dd.DragDrop
22564     b4EndDrag: function(e) {
22565         Roo.fly(this.getDragEl()).hide();
22566     },
22567
22568     // overrides Roo.dd.DragDrop
22569     // By default we try to move the element to the last location of the frame.
22570     // This is so that the default behavior mirrors that of Roo.dd.DD.
22571     endDrag: function(e) {
22572
22573         var lel = this.getEl();
22574         var del = this.getDragEl();
22575
22576         // Show the drag frame briefly so we can get its position
22577         del.style.visibility = "";
22578
22579         this.beforeMove();
22580         // Hide the linked element before the move to get around a Safari
22581         // rendering bug.
22582         lel.style.visibility = "hidden";
22583         Roo.dd.DDM.moveToEl(lel, del);
22584         del.style.visibility = "hidden";
22585         lel.style.visibility = "";
22586
22587         this.afterDrag();
22588     },
22589
22590     beforeMove : function(){
22591
22592     },
22593
22594     afterDrag : function(){
22595
22596     },
22597
22598     toString: function() {
22599         return ("DDProxy " + this.id);
22600     }
22601
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.DDTarget
22616  * A DragDrop implementation that does not move, but can be a drop
22617  * target.  You would get the same result by simply omitting implementation
22618  * for the event callbacks, but this way we reduce the processing cost of the
22619  * event listener and the callbacks.
22620  * @extends Roo.dd.DragDrop
22621  * @constructor
22622  * @param {String} id the id of the element that is a drop target
22623  * @param {String} sGroup the group of related DragDrop objects
22624  * @param {object} config an object containing configurable attributes
22625  *                 Valid properties for DDTarget in addition to those in
22626  *                 DragDrop:
22627  *                    none
22628  */
22629 Roo.dd.DDTarget = function(id, sGroup, config) {
22630     if (id) {
22631         this.initTarget(id, sGroup, config);
22632     }
22633     if (config && (config.listeners || config.events)) { 
22634         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22635             listeners : config.listeners || {}, 
22636             events : config.events || {} 
22637         });    
22638     }
22639 };
22640
22641 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22642 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22643     toString: function() {
22644         return ("DDTarget " + this.id);
22645     }
22646 });
22647 /*
22648  * Based on:
22649  * Ext JS Library 1.1.1
22650  * Copyright(c) 2006-2007, Ext JS, LLC.
22651  *
22652  * Originally Released Under LGPL - original licence link has changed is not relivant.
22653  *
22654  * Fork - LGPL
22655  * <script type="text/javascript">
22656  */
22657  
22658
22659 /**
22660  * @class Roo.dd.ScrollManager
22661  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22662  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22663  * @static
22664  */
22665 Roo.dd.ScrollManager = function(){
22666     var ddm = Roo.dd.DragDropMgr;
22667     var els = {};
22668     var dragEl = null;
22669     var proc = {};
22670     
22671     
22672     
22673     var onStop = function(e){
22674         dragEl = null;
22675         clearProc();
22676     };
22677     
22678     var triggerRefresh = function(){
22679         if(ddm.dragCurrent){
22680              ddm.refreshCache(ddm.dragCurrent.groups);
22681         }
22682     };
22683     
22684     var doScroll = function(){
22685         if(ddm.dragCurrent){
22686             var dds = Roo.dd.ScrollManager;
22687             if(!dds.animate){
22688                 if(proc.el.scroll(proc.dir, dds.increment)){
22689                     triggerRefresh();
22690                 }
22691             }else{
22692                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22693             }
22694         }
22695     };
22696     
22697     var clearProc = function(){
22698         if(proc.id){
22699             clearInterval(proc.id);
22700         }
22701         proc.id = 0;
22702         proc.el = null;
22703         proc.dir = "";
22704     };
22705     
22706     var startProc = function(el, dir){
22707          Roo.log('scroll startproc');
22708         clearProc();
22709         proc.el = el;
22710         proc.dir = dir;
22711         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22712     };
22713     
22714     var onFire = function(e, isDrop){
22715        
22716         if(isDrop || !ddm.dragCurrent){ return; }
22717         var dds = Roo.dd.ScrollManager;
22718         if(!dragEl || dragEl != ddm.dragCurrent){
22719             dragEl = ddm.dragCurrent;
22720             // refresh regions on drag start
22721             dds.refreshCache();
22722         }
22723         
22724         var xy = Roo.lib.Event.getXY(e);
22725         var pt = new Roo.lib.Point(xy[0], xy[1]);
22726         for(var id in els){
22727             var el = els[id], r = el._region;
22728             if(r && r.contains(pt) && el.isScrollable()){
22729                 if(r.bottom - pt.y <= dds.thresh){
22730                     if(proc.el != el){
22731                         startProc(el, "down");
22732                     }
22733                     return;
22734                 }else if(r.right - pt.x <= dds.thresh){
22735                     if(proc.el != el){
22736                         startProc(el, "left");
22737                     }
22738                     return;
22739                 }else if(pt.y - r.top <= dds.thresh){
22740                     if(proc.el != el){
22741                         startProc(el, "up");
22742                     }
22743                     return;
22744                 }else if(pt.x - r.left <= dds.thresh){
22745                     if(proc.el != el){
22746                         startProc(el, "right");
22747                     }
22748                     return;
22749                 }
22750             }
22751         }
22752         clearProc();
22753     };
22754     
22755     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22756     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22757     
22758     return {
22759         /**
22760          * Registers new overflow element(s) to auto scroll
22761          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22762          */
22763         register : function(el){
22764             if(el instanceof Array){
22765                 for(var i = 0, len = el.length; i < len; i++) {
22766                         this.register(el[i]);
22767                 }
22768             }else{
22769                 el = Roo.get(el);
22770                 els[el.id] = el;
22771             }
22772             Roo.dd.ScrollManager.els = els;
22773         },
22774         
22775         /**
22776          * Unregisters overflow element(s) so they are no longer scrolled
22777          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22778          */
22779         unregister : function(el){
22780             if(el instanceof Array){
22781                 for(var i = 0, len = el.length; i < len; i++) {
22782                         this.unregister(el[i]);
22783                 }
22784             }else{
22785                 el = Roo.get(el);
22786                 delete els[el.id];
22787             }
22788         },
22789         
22790         /**
22791          * The number of pixels from the edge of a container the pointer needs to be to 
22792          * trigger scrolling (defaults to 25)
22793          * @type Number
22794          */
22795         thresh : 25,
22796         
22797         /**
22798          * The number of pixels to scroll in each scroll increment (defaults to 50)
22799          * @type Number
22800          */
22801         increment : 100,
22802         
22803         /**
22804          * The frequency of scrolls in milliseconds (defaults to 500)
22805          * @type Number
22806          */
22807         frequency : 500,
22808         
22809         /**
22810          * True to animate the scroll (defaults to true)
22811          * @type Boolean
22812          */
22813         animate: true,
22814         
22815         /**
22816          * The animation duration in seconds - 
22817          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22818          * @type Number
22819          */
22820         animDuration: .4,
22821         
22822         /**
22823          * Manually trigger a cache refresh.
22824          */
22825         refreshCache : function(){
22826             for(var id in els){
22827                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22828                     els[id]._region = els[id].getRegion();
22829                 }
22830             }
22831         }
22832     };
22833 }();/*
22834  * Based on:
22835  * Ext JS Library 1.1.1
22836  * Copyright(c) 2006-2007, Ext JS, LLC.
22837  *
22838  * Originally Released Under LGPL - original licence link has changed is not relivant.
22839  *
22840  * Fork - LGPL
22841  * <script type="text/javascript">
22842  */
22843  
22844
22845 /**
22846  * @class Roo.dd.Registry
22847  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22848  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22849  * @static
22850  */
22851 Roo.dd.Registry = function(){
22852     var elements = {}; 
22853     var handles = {}; 
22854     var autoIdSeed = 0;
22855
22856     var getId = function(el, autogen){
22857         if(typeof el == "string"){
22858             return el;
22859         }
22860         var id = el.id;
22861         if(!id && autogen !== false){
22862             id = "roodd-" + (++autoIdSeed);
22863             el.id = id;
22864         }
22865         return id;
22866     };
22867     
22868     return {
22869     /**
22870      * Register a drag drop element
22871      * @param {String|HTMLElement} element The id or DOM node to register
22872      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22873      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22874      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22875      * populated in the data object (if applicable):
22876      * <pre>
22877 Value      Description<br />
22878 ---------  ------------------------------------------<br />
22879 handles    Array of DOM nodes that trigger dragging<br />
22880            for the element being registered<br />
22881 isHandle   True if the element passed in triggers<br />
22882            dragging itself, else false
22883 </pre>
22884      */
22885         register : function(el, data){
22886             data = data || {};
22887             if(typeof el == "string"){
22888                 el = document.getElementById(el);
22889             }
22890             data.ddel = el;
22891             elements[getId(el)] = data;
22892             if(data.isHandle !== false){
22893                 handles[data.ddel.id] = data;
22894             }
22895             if(data.handles){
22896                 var hs = data.handles;
22897                 for(var i = 0, len = hs.length; i < len; i++){
22898                         handles[getId(hs[i])] = data;
22899                 }
22900             }
22901         },
22902
22903     /**
22904      * Unregister a drag drop element
22905      * @param {String|HTMLElement}  element The id or DOM node to unregister
22906      */
22907         unregister : function(el){
22908             var id = getId(el, false);
22909             var data = elements[id];
22910             if(data){
22911                 delete elements[id];
22912                 if(data.handles){
22913                     var hs = data.handles;
22914                     for(var i = 0, len = hs.length; i < len; i++){
22915                         delete handles[getId(hs[i], false)];
22916                     }
22917                 }
22918             }
22919         },
22920
22921     /**
22922      * Returns the handle registered for a DOM Node by id
22923      * @param {String|HTMLElement} id The DOM node or id to look up
22924      * @return {Object} handle The custom handle data
22925      */
22926         getHandle : function(id){
22927             if(typeof id != "string"){ // must be element?
22928                 id = id.id;
22929             }
22930             return handles[id];
22931         },
22932
22933     /**
22934      * Returns the handle that is registered for the DOM node that is the target of the event
22935      * @param {Event} e The event
22936      * @return {Object} handle The custom handle data
22937      */
22938         getHandleFromEvent : function(e){
22939             var t = Roo.lib.Event.getTarget(e);
22940             return t ? handles[t.id] : null;
22941         },
22942
22943     /**
22944      * Returns a custom data object that is registered for a DOM node by id
22945      * @param {String|HTMLElement} id The DOM node or id to look up
22946      * @return {Object} data The custom data
22947      */
22948         getTarget : function(id){
22949             if(typeof id != "string"){ // must be element?
22950                 id = id.id;
22951             }
22952             return elements[id];
22953         },
22954
22955     /**
22956      * Returns a custom data object that is registered for the DOM node that is the target of the event
22957      * @param {Event} e The event
22958      * @return {Object} data The custom data
22959      */
22960         getTargetFromEvent : function(e){
22961             var t = Roo.lib.Event.getTarget(e);
22962             return t ? elements[t.id] || handles[t.id] : null;
22963         }
22964     };
22965 }();/*
22966  * Based on:
22967  * Ext JS Library 1.1.1
22968  * Copyright(c) 2006-2007, Ext JS, LLC.
22969  *
22970  * Originally Released Under LGPL - original licence link has changed is not relivant.
22971  *
22972  * Fork - LGPL
22973  * <script type="text/javascript">
22974  */
22975  
22976
22977 /**
22978  * @class Roo.dd.StatusProxy
22979  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22980  * default drag proxy used by all Roo.dd components.
22981  * @constructor
22982  * @param {Object} config
22983  */
22984 Roo.dd.StatusProxy = function(config){
22985     Roo.apply(this, config);
22986     this.id = this.id || Roo.id();
22987     this.el = new Roo.Layer({
22988         dh: {
22989             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22990                 {tag: "div", cls: "x-dd-drop-icon"},
22991                 {tag: "div", cls: "x-dd-drag-ghost"}
22992             ]
22993         }, 
22994         shadow: !config || config.shadow !== false
22995     });
22996     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22997     this.dropStatus = this.dropNotAllowed;
22998 };
22999
23000 Roo.dd.StatusProxy.prototype = {
23001     /**
23002      * @cfg {String} dropAllowed
23003      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23004      */
23005     dropAllowed : "x-dd-drop-ok",
23006     /**
23007      * @cfg {String} dropNotAllowed
23008      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23009      */
23010     dropNotAllowed : "x-dd-drop-nodrop",
23011
23012     /**
23013      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23014      * over the current target element.
23015      * @param {String} cssClass The css class for the new drop status indicator image
23016      */
23017     setStatus : function(cssClass){
23018         cssClass = cssClass || this.dropNotAllowed;
23019         if(this.dropStatus != cssClass){
23020             this.el.replaceClass(this.dropStatus, cssClass);
23021             this.dropStatus = cssClass;
23022         }
23023     },
23024
23025     /**
23026      * Resets the status indicator to the default dropNotAllowed value
23027      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23028      */
23029     reset : function(clearGhost){
23030         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23031         this.dropStatus = this.dropNotAllowed;
23032         if(clearGhost){
23033             this.ghost.update("");
23034         }
23035     },
23036
23037     /**
23038      * Updates the contents of the ghost element
23039      * @param {String} html The html that will replace the current innerHTML of the ghost element
23040      */
23041     update : function(html){
23042         if(typeof html == "string"){
23043             this.ghost.update(html);
23044         }else{
23045             this.ghost.update("");
23046             html.style.margin = "0";
23047             this.ghost.dom.appendChild(html);
23048         }
23049         // ensure float = none set?? cant remember why though.
23050         var el = this.ghost.dom.firstChild;
23051                 if(el){
23052                         Roo.fly(el).setStyle('float', 'none');
23053                 }
23054     },
23055     
23056     /**
23057      * Returns the underlying proxy {@link Roo.Layer}
23058      * @return {Roo.Layer} el
23059     */
23060     getEl : function(){
23061         return this.el;
23062     },
23063
23064     /**
23065      * Returns the ghost element
23066      * @return {Roo.Element} el
23067      */
23068     getGhost : function(){
23069         return this.ghost;
23070     },
23071
23072     /**
23073      * Hides the proxy
23074      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23075      */
23076     hide : function(clear){
23077         this.el.hide();
23078         if(clear){
23079             this.reset(true);
23080         }
23081     },
23082
23083     /**
23084      * Stops the repair animation if it's currently running
23085      */
23086     stop : function(){
23087         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23088             this.anim.stop();
23089         }
23090     },
23091
23092     /**
23093      * Displays this proxy
23094      */
23095     show : function(){
23096         this.el.show();
23097     },
23098
23099     /**
23100      * Force the Layer to sync its shadow and shim positions to the element
23101      */
23102     sync : function(){
23103         this.el.sync();
23104     },
23105
23106     /**
23107      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23108      * invalid drop operation by the item being dragged.
23109      * @param {Array} xy The XY position of the element ([x, y])
23110      * @param {Function} callback The function to call after the repair is complete
23111      * @param {Object} scope The scope in which to execute the callback
23112      */
23113     repair : function(xy, callback, scope){
23114         this.callback = callback;
23115         this.scope = scope;
23116         if(xy && this.animRepair !== false){
23117             this.el.addClass("x-dd-drag-repair");
23118             this.el.hideUnders(true);
23119             this.anim = this.el.shift({
23120                 duration: this.repairDuration || .5,
23121                 easing: 'easeOut',
23122                 xy: xy,
23123                 stopFx: true,
23124                 callback: this.afterRepair,
23125                 scope: this
23126             });
23127         }else{
23128             this.afterRepair();
23129         }
23130     },
23131
23132     // private
23133     afterRepair : function(){
23134         this.hide(true);
23135         if(typeof this.callback == "function"){
23136             this.callback.call(this.scope || this);
23137         }
23138         this.callback = null;
23139         this.scope = null;
23140     }
23141 };/*
23142  * Based on:
23143  * Ext JS Library 1.1.1
23144  * Copyright(c) 2006-2007, Ext JS, LLC.
23145  *
23146  * Originally Released Under LGPL - original licence link has changed is not relivant.
23147  *
23148  * Fork - LGPL
23149  * <script type="text/javascript">
23150  */
23151
23152 /**
23153  * @class Roo.dd.DragSource
23154  * @extends Roo.dd.DDProxy
23155  * A simple class that provides the basic implementation needed to make any element draggable.
23156  * @constructor
23157  * @param {String/HTMLElement/Element} el The container element
23158  * @param {Object} config
23159  */
23160 Roo.dd.DragSource = function(el, config){
23161     this.el = Roo.get(el);
23162     this.dragData = {};
23163     
23164     Roo.apply(this, config);
23165     
23166     if(!this.proxy){
23167         this.proxy = new Roo.dd.StatusProxy();
23168     }
23169
23170     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23171           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23172     
23173     this.dragging = false;
23174 };
23175
23176 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23177     /**
23178      * @cfg {String} dropAllowed
23179      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23180      */
23181     dropAllowed : "x-dd-drop-ok",
23182     /**
23183      * @cfg {String} dropNotAllowed
23184      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23185      */
23186     dropNotAllowed : "x-dd-drop-nodrop",
23187
23188     /**
23189      * Returns the data object associated with this drag source
23190      * @return {Object} data An object containing arbitrary data
23191      */
23192     getDragData : function(e){
23193         return this.dragData;
23194     },
23195
23196     // private
23197     onDragEnter : function(e, id){
23198         var target = Roo.dd.DragDropMgr.getDDById(id);
23199         this.cachedTarget = target;
23200         if(this.beforeDragEnter(target, e, id) !== false){
23201             if(target.isNotifyTarget){
23202                 var status = target.notifyEnter(this, e, this.dragData);
23203                 this.proxy.setStatus(status);
23204             }else{
23205                 this.proxy.setStatus(this.dropAllowed);
23206             }
23207             
23208             if(this.afterDragEnter){
23209                 /**
23210                  * An empty function by default, but provided so that you can perform a custom action
23211                  * when the dragged item enters the drop target by providing an implementation.
23212                  * @param {Roo.dd.DragDrop} target The drop target
23213                  * @param {Event} e The event object
23214                  * @param {String} id The id of the dragged element
23215                  * @method afterDragEnter
23216                  */
23217                 this.afterDragEnter(target, e, id);
23218             }
23219         }
23220     },
23221
23222     /**
23223      * An empty function by default, but provided so that you can perform a custom action
23224      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23225      * @param {Roo.dd.DragDrop} target The drop target
23226      * @param {Event} e The event object
23227      * @param {String} id The id of the dragged element
23228      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23229      */
23230     beforeDragEnter : function(target, e, id){
23231         return true;
23232     },
23233
23234     // private
23235     alignElWithMouse: function() {
23236         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23237         this.proxy.sync();
23238     },
23239
23240     // private
23241     onDragOver : function(e, id){
23242         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23243         if(this.beforeDragOver(target, e, id) !== false){
23244             if(target.isNotifyTarget){
23245                 var status = target.notifyOver(this, e, this.dragData);
23246                 this.proxy.setStatus(status);
23247             }
23248
23249             if(this.afterDragOver){
23250                 /**
23251                  * An empty function by default, but provided so that you can perform a custom action
23252                  * while the dragged item is over the drop target by providing an implementation.
23253                  * @param {Roo.dd.DragDrop} target The drop target
23254                  * @param {Event} e The event object
23255                  * @param {String} id The id of the dragged element
23256                  * @method afterDragOver
23257                  */
23258                 this.afterDragOver(target, e, id);
23259             }
23260         }
23261     },
23262
23263     /**
23264      * An empty function by default, but provided so that you can perform a custom action
23265      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23266      * @param {Roo.dd.DragDrop} target The drop target
23267      * @param {Event} e The event object
23268      * @param {String} id The id of the dragged element
23269      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23270      */
23271     beforeDragOver : function(target, e, id){
23272         return true;
23273     },
23274
23275     // private
23276     onDragOut : function(e, id){
23277         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23278         if(this.beforeDragOut(target, e, id) !== false){
23279             if(target.isNotifyTarget){
23280                 target.notifyOut(this, e, this.dragData);
23281             }
23282             this.proxy.reset();
23283             if(this.afterDragOut){
23284                 /**
23285                  * An empty function by default, but provided so that you can perform a custom action
23286                  * after the dragged item is dragged out of the target without dropping.
23287                  * @param {Roo.dd.DragDrop} target The drop target
23288                  * @param {Event} e The event object
23289                  * @param {String} id The id of the dragged element
23290                  * @method afterDragOut
23291                  */
23292                 this.afterDragOut(target, e, id);
23293             }
23294         }
23295         this.cachedTarget = null;
23296     },
23297
23298     /**
23299      * An empty function by default, but provided so that you can perform a custom action before the dragged
23300      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23301      * @param {Roo.dd.DragDrop} target The drop target
23302      * @param {Event} e The event object
23303      * @param {String} id The id of the dragged element
23304      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23305      */
23306     beforeDragOut : function(target, e, id){
23307         return true;
23308     },
23309     
23310     // private
23311     onDragDrop : function(e, id){
23312         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23313         if(this.beforeDragDrop(target, e, id) !== false){
23314             if(target.isNotifyTarget){
23315                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23316                     this.onValidDrop(target, e, id);
23317                 }else{
23318                     this.onInvalidDrop(target, e, id);
23319                 }
23320             }else{
23321                 this.onValidDrop(target, e, id);
23322             }
23323             
23324             if(this.afterDragDrop){
23325                 /**
23326                  * An empty function by default, but provided so that you can perform a custom action
23327                  * after a valid drag drop has occurred by providing an implementation.
23328                  * @param {Roo.dd.DragDrop} target The drop target
23329                  * @param {Event} e The event object
23330                  * @param {String} id The id of the dropped element
23331                  * @method afterDragDrop
23332                  */
23333                 this.afterDragDrop(target, e, id);
23334             }
23335         }
23336         delete this.cachedTarget;
23337     },
23338
23339     /**
23340      * An empty function by default, but provided so that you can perform a custom action before the dragged
23341      * item is dropped onto the target and optionally cancel the onDragDrop.
23342      * @param {Roo.dd.DragDrop} target The drop target
23343      * @param {Event} e The event object
23344      * @param {String} id The id of the dragged element
23345      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23346      */
23347     beforeDragDrop : function(target, e, id){
23348         return true;
23349     },
23350
23351     // private
23352     onValidDrop : function(target, e, id){
23353         this.hideProxy();
23354         if(this.afterValidDrop){
23355             /**
23356              * An empty function by default, but provided so that you can perform a custom action
23357              * after a valid drop has occurred by providing an implementation.
23358              * @param {Object} target The target DD 
23359              * @param {Event} e The event object
23360              * @param {String} id The id of the dropped element
23361              * @method afterInvalidDrop
23362              */
23363             this.afterValidDrop(target, e, id);
23364         }
23365     },
23366
23367     // private
23368     getRepairXY : function(e, data){
23369         return this.el.getXY();  
23370     },
23371
23372     // private
23373     onInvalidDrop : function(target, e, id){
23374         this.beforeInvalidDrop(target, e, id);
23375         if(this.cachedTarget){
23376             if(this.cachedTarget.isNotifyTarget){
23377                 this.cachedTarget.notifyOut(this, e, this.dragData);
23378             }
23379             this.cacheTarget = null;
23380         }
23381         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23382
23383         if(this.afterInvalidDrop){
23384             /**
23385              * An empty function by default, but provided so that you can perform a custom action
23386              * after an invalid drop has occurred by providing an implementation.
23387              * @param {Event} e The event object
23388              * @param {String} id The id of the dropped element
23389              * @method afterInvalidDrop
23390              */
23391             this.afterInvalidDrop(e, id);
23392         }
23393     },
23394
23395     // private
23396     afterRepair : function(){
23397         if(Roo.enableFx){
23398             this.el.highlight(this.hlColor || "c3daf9");
23399         }
23400         this.dragging = false;
23401     },
23402
23403     /**
23404      * An empty function by default, but provided so that you can perform a custom action after an invalid
23405      * drop has occurred.
23406      * @param {Roo.dd.DragDrop} target The drop target
23407      * @param {Event} e The event object
23408      * @param {String} id The id of the dragged element
23409      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23410      */
23411     beforeInvalidDrop : function(target, e, id){
23412         return true;
23413     },
23414
23415     // private
23416     handleMouseDown : function(e){
23417         if(this.dragging) {
23418             return;
23419         }
23420         var data = this.getDragData(e);
23421         if(data && this.onBeforeDrag(data, e) !== false){
23422             this.dragData = data;
23423             this.proxy.stop();
23424             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23425         } 
23426     },
23427
23428     /**
23429      * An empty function by default, but provided so that you can perform a custom action before the initial
23430      * drag event begins and optionally cancel it.
23431      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23432      * @param {Event} e The event object
23433      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23434      */
23435     onBeforeDrag : function(data, e){
23436         return true;
23437     },
23438
23439     /**
23440      * An empty function by default, but provided so that you can perform a custom action once the initial
23441      * drag event has begun.  The drag cannot be canceled from this function.
23442      * @param {Number} x The x position of the click on the dragged object
23443      * @param {Number} y The y position of the click on the dragged object
23444      */
23445     onStartDrag : Roo.emptyFn,
23446
23447     // private - YUI override
23448     startDrag : function(x, y){
23449         this.proxy.reset();
23450         this.dragging = true;
23451         this.proxy.update("");
23452         this.onInitDrag(x, y);
23453         this.proxy.show();
23454     },
23455
23456     // private
23457     onInitDrag : function(x, y){
23458         var clone = this.el.dom.cloneNode(true);
23459         clone.id = Roo.id(); // prevent duplicate ids
23460         this.proxy.update(clone);
23461         this.onStartDrag(x, y);
23462         return true;
23463     },
23464
23465     /**
23466      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23467      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23468      */
23469     getProxy : function(){
23470         return this.proxy;  
23471     },
23472
23473     /**
23474      * Hides the drag source's {@link Roo.dd.StatusProxy}
23475      */
23476     hideProxy : function(){
23477         this.proxy.hide();  
23478         this.proxy.reset(true);
23479         this.dragging = false;
23480     },
23481
23482     // private
23483     triggerCacheRefresh : function(){
23484         Roo.dd.DDM.refreshCache(this.groups);
23485     },
23486
23487     // private - override to prevent hiding
23488     b4EndDrag: function(e) {
23489     },
23490
23491     // private - override to prevent moving
23492     endDrag : function(e){
23493         this.onEndDrag(this.dragData, e);
23494     },
23495
23496     // private
23497     onEndDrag : function(data, e){
23498     },
23499     
23500     // private - pin to cursor
23501     autoOffset : function(x, y) {
23502         this.setDelta(-12, -20);
23503     }    
23504 });/*
23505  * Based on:
23506  * Ext JS Library 1.1.1
23507  * Copyright(c) 2006-2007, Ext JS, LLC.
23508  *
23509  * Originally Released Under LGPL - original licence link has changed is not relivant.
23510  *
23511  * Fork - LGPL
23512  * <script type="text/javascript">
23513  */
23514
23515
23516 /**
23517  * @class Roo.dd.DropTarget
23518  * @extends Roo.dd.DDTarget
23519  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23520  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23521  * @constructor
23522  * @param {String/HTMLElement/Element} el The container element
23523  * @param {Object} config
23524  */
23525 Roo.dd.DropTarget = function(el, config){
23526     this.el = Roo.get(el);
23527     
23528     var listeners = false; ;
23529     if (config && config.listeners) {
23530         listeners= config.listeners;
23531         delete config.listeners;
23532     }
23533     Roo.apply(this, config);
23534     
23535     if(this.containerScroll){
23536         Roo.dd.ScrollManager.register(this.el);
23537     }
23538     this.addEvents( {
23539          /**
23540          * @scope Roo.dd.DropTarget
23541          */
23542          
23543          /**
23544          * @event enter
23545          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23546          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23547          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23548          * 
23549          * IMPORTANT : it should set  this.valid to true|false
23550          * 
23551          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23552          * @param {Event} e The event
23553          * @param {Object} data An object containing arbitrary data supplied by the drag source
23554          */
23555         "enter" : true,
23556         
23557          /**
23558          * @event over
23559          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23560          * This method will be called on every mouse movement while the drag source is over the drop target.
23561          * This default implementation simply returns the dropAllowed config value.
23562          * 
23563          * IMPORTANT : it should set  this.valid to true|false
23564          * 
23565          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23566          * @param {Event} e The event
23567          * @param {Object} data An object containing arbitrary data supplied by the drag source
23568          
23569          */
23570         "over" : true,
23571         /**
23572          * @event out
23573          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23574          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23575          * overClass (if any) from the drop element.
23576          * 
23577          * 
23578          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23579          * @param {Event} e The event
23580          * @param {Object} data An object containing arbitrary data supplied by the drag source
23581          */
23582          "out" : true,
23583          
23584         /**
23585          * @event drop
23586          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23587          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23588          * implementation that does something to process the drop event and returns true so that the drag source's
23589          * repair action does not run.
23590          * 
23591          * IMPORTANT : it should set this.success
23592          * 
23593          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23594          * @param {Event} e The event
23595          * @param {Object} data An object containing arbitrary data supplied by the drag source
23596         */
23597          "drop" : true
23598     });
23599             
23600      
23601     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23602         this.el.dom, 
23603         this.ddGroup || this.group,
23604         {
23605             isTarget: true,
23606             listeners : listeners || {} 
23607            
23608         
23609         }
23610     );
23611
23612 };
23613
23614 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23615     /**
23616      * @cfg {String} overClass
23617      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23618      */
23619      /**
23620      * @cfg {String} ddGroup
23621      * The drag drop group to handle drop events for
23622      */
23623      
23624     /**
23625      * @cfg {String} dropAllowed
23626      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23627      */
23628     dropAllowed : "x-dd-drop-ok",
23629     /**
23630      * @cfg {String} dropNotAllowed
23631      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23632      */
23633     dropNotAllowed : "x-dd-drop-nodrop",
23634     /**
23635      * @cfg {boolean} success
23636      * set this after drop listener.. 
23637      */
23638     success : false,
23639     /**
23640      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23641      * if the drop point is valid for over/enter..
23642      */
23643     valid : false,
23644     // private
23645     isTarget : true,
23646
23647     // private
23648     isNotifyTarget : true,
23649     
23650     /**
23651      * @hide
23652      */
23653     notifyEnter : function(dd, e, data)
23654     {
23655         this.valid = true;
23656         this.fireEvent('enter', dd, e, data);
23657         if(this.overClass){
23658             this.el.addClass(this.overClass);
23659         }
23660         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23661             this.valid ? this.dropAllowed : this.dropNotAllowed
23662         );
23663     },
23664
23665     /**
23666      * @hide
23667      */
23668     notifyOver : function(dd, e, data)
23669     {
23670         this.valid = true;
23671         this.fireEvent('over', dd, e, data);
23672         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23673             this.valid ? this.dropAllowed : this.dropNotAllowed
23674         );
23675     },
23676
23677     /**
23678      * @hide
23679      */
23680     notifyOut : function(dd, e, data)
23681     {
23682         this.fireEvent('out', dd, e, data);
23683         if(this.overClass){
23684             this.el.removeClass(this.overClass);
23685         }
23686     },
23687
23688     /**
23689      * @hide
23690      */
23691     notifyDrop : function(dd, e, data)
23692     {
23693         this.success = false;
23694         this.fireEvent('drop', dd, e, data);
23695         return this.success;
23696     }
23697 });/*
23698  * Based on:
23699  * Ext JS Library 1.1.1
23700  * Copyright(c) 2006-2007, Ext JS, LLC.
23701  *
23702  * Originally Released Under LGPL - original licence link has changed is not relivant.
23703  *
23704  * Fork - LGPL
23705  * <script type="text/javascript">
23706  */
23707
23708
23709 /**
23710  * @class Roo.dd.DragZone
23711  * @extends Roo.dd.DragSource
23712  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23713  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23714  * @constructor
23715  * @param {String/HTMLElement/Element} el The container element
23716  * @param {Object} config
23717  */
23718 Roo.dd.DragZone = function(el, config){
23719     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23720     if(this.containerScroll){
23721         Roo.dd.ScrollManager.register(this.el);
23722     }
23723 };
23724
23725 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23726     /**
23727      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23728      * for auto scrolling during drag operations.
23729      */
23730     /**
23731      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23732      * method after a failed drop (defaults to "c3daf9" - light blue)
23733      */
23734
23735     /**
23736      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23737      * for a valid target to drag based on the mouse down. Override this method
23738      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23739      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23740      * @param {EventObject} e The mouse down event
23741      * @return {Object} The dragData
23742      */
23743     getDragData : function(e){
23744         return Roo.dd.Registry.getHandleFromEvent(e);
23745     },
23746     
23747     /**
23748      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23749      * this.dragData.ddel
23750      * @param {Number} x The x position of the click on the dragged object
23751      * @param {Number} y The y position of the click on the dragged object
23752      * @return {Boolean} true to continue the drag, false to cancel
23753      */
23754     onInitDrag : function(x, y){
23755         this.proxy.update(this.dragData.ddel.cloneNode(true));
23756         this.onStartDrag(x, y);
23757         return true;
23758     },
23759     
23760     /**
23761      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23762      */
23763     afterRepair : function(){
23764         if(Roo.enableFx){
23765             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23766         }
23767         this.dragging = false;
23768     },
23769
23770     /**
23771      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23772      * the XY of this.dragData.ddel
23773      * @param {EventObject} e The mouse up event
23774      * @return {Array} The xy location (e.g. [100, 200])
23775      */
23776     getRepairXY : function(e){
23777         return Roo.Element.fly(this.dragData.ddel).getXY();  
23778     }
23779 });/*
23780  * Based on:
23781  * Ext JS Library 1.1.1
23782  * Copyright(c) 2006-2007, Ext JS, LLC.
23783  *
23784  * Originally Released Under LGPL - original licence link has changed is not relivant.
23785  *
23786  * Fork - LGPL
23787  * <script type="text/javascript">
23788  */
23789 /**
23790  * @class Roo.dd.DropZone
23791  * @extends Roo.dd.DropTarget
23792  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23793  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23794  * @constructor
23795  * @param {String/HTMLElement/Element} el The container element
23796  * @param {Object} config
23797  */
23798 Roo.dd.DropZone = function(el, config){
23799     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23800 };
23801
23802 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23803     /**
23804      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23805      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23806      * provide your own custom lookup.
23807      * @param {Event} e The event
23808      * @return {Object} data The custom data
23809      */
23810     getTargetFromEvent : function(e){
23811         return Roo.dd.Registry.getTargetFromEvent(e);
23812     },
23813
23814     /**
23815      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23816      * that it has registered.  This method has no default implementation and should be overridden to provide
23817      * node-specific processing if necessary.
23818      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23819      * {@link #getTargetFromEvent} for this node)
23820      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23821      * @param {Event} e The event
23822      * @param {Object} data An object containing arbitrary data supplied by the drag source
23823      */
23824     onNodeEnter : function(n, dd, e, data){
23825         
23826     },
23827
23828     /**
23829      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23830      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23831      * overridden to provide the proper feedback.
23832      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23833      * {@link #getTargetFromEvent} for this node)
23834      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23835      * @param {Event} e The event
23836      * @param {Object} data An object containing arbitrary data supplied by the drag source
23837      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23838      * underlying {@link Roo.dd.StatusProxy} can be updated
23839      */
23840     onNodeOver : function(n, dd, e, data){
23841         return this.dropAllowed;
23842     },
23843
23844     /**
23845      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23846      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23847      * node-specific processing if necessary.
23848      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23849      * {@link #getTargetFromEvent} for this node)
23850      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23851      * @param {Event} e The event
23852      * @param {Object} data An object containing arbitrary data supplied by the drag source
23853      */
23854     onNodeOut : function(n, dd, e, data){
23855         
23856     },
23857
23858     /**
23859      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23860      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23861      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23862      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23863      * {@link #getTargetFromEvent} for this node)
23864      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23865      * @param {Event} e The event
23866      * @param {Object} data An object containing arbitrary data supplied by the drag source
23867      * @return {Boolean} True if the drop was valid, else false
23868      */
23869     onNodeDrop : function(n, dd, e, data){
23870         return false;
23871     },
23872
23873     /**
23874      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23875      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23876      * it should be overridden to provide the proper feedback if necessary.
23877      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23878      * @param {Event} e The event
23879      * @param {Object} data An object containing arbitrary data supplied by the drag source
23880      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23881      * underlying {@link Roo.dd.StatusProxy} can be updated
23882      */
23883     onContainerOver : function(dd, e, data){
23884         return this.dropNotAllowed;
23885     },
23886
23887     /**
23888      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23889      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23890      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23891      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23892      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23893      * @param {Event} e The event
23894      * @param {Object} data An object containing arbitrary data supplied by the drag source
23895      * @return {Boolean} True if the drop was valid, else false
23896      */
23897     onContainerDrop : function(dd, e, data){
23898         return false;
23899     },
23900
23901     /**
23902      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23903      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23904      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23905      * you should override this method and provide a custom implementation.
23906      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23907      * @param {Event} e The event
23908      * @param {Object} data An object containing arbitrary data supplied by the drag source
23909      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23910      * underlying {@link Roo.dd.StatusProxy} can be updated
23911      */
23912     notifyEnter : function(dd, e, data){
23913         return this.dropNotAllowed;
23914     },
23915
23916     /**
23917      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23918      * This method will be called on every mouse movement while the drag source is over the drop zone.
23919      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23920      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23921      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23922      * registered node, it will call {@link #onContainerOver}.
23923      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23924      * @param {Event} e The event
23925      * @param {Object} data An object containing arbitrary data supplied by the drag source
23926      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23927      * underlying {@link Roo.dd.StatusProxy} can be updated
23928      */
23929     notifyOver : function(dd, e, data){
23930         var n = this.getTargetFromEvent(e);
23931         if(!n){ // not over valid drop target
23932             if(this.lastOverNode){
23933                 this.onNodeOut(this.lastOverNode, dd, e, data);
23934                 this.lastOverNode = null;
23935             }
23936             return this.onContainerOver(dd, e, data);
23937         }
23938         if(this.lastOverNode != n){
23939             if(this.lastOverNode){
23940                 this.onNodeOut(this.lastOverNode, dd, e, data);
23941             }
23942             this.onNodeEnter(n, dd, e, data);
23943             this.lastOverNode = n;
23944         }
23945         return this.onNodeOver(n, dd, e, data);
23946     },
23947
23948     /**
23949      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23950      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23951      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23952      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23953      * @param {Event} e The event
23954      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23955      */
23956     notifyOut : function(dd, e, data){
23957         if(this.lastOverNode){
23958             this.onNodeOut(this.lastOverNode, dd, e, data);
23959             this.lastOverNode = null;
23960         }
23961     },
23962
23963     /**
23964      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23965      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23966      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23967      * otherwise it will call {@link #onContainerDrop}.
23968      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23969      * @param {Event} e The event
23970      * @param {Object} data An object containing arbitrary data supplied by the drag source
23971      * @return {Boolean} True if the drop was valid, else false
23972      */
23973     notifyDrop : function(dd, e, data){
23974         if(this.lastOverNode){
23975             this.onNodeOut(this.lastOverNode, dd, e, data);
23976             this.lastOverNode = null;
23977         }
23978         var n = this.getTargetFromEvent(e);
23979         return n ?
23980             this.onNodeDrop(n, dd, e, data) :
23981             this.onContainerDrop(dd, e, data);
23982     },
23983
23984     // private
23985     triggerCacheRefresh : function(){
23986         Roo.dd.DDM.refreshCache(this.groups);
23987     }  
23988 });/*
23989  * Based on:
23990  * Ext JS Library 1.1.1
23991  * Copyright(c) 2006-2007, Ext JS, LLC.
23992  *
23993  * Originally Released Under LGPL - original licence link has changed is not relivant.
23994  *
23995  * Fork - LGPL
23996  * <script type="text/javascript">
23997  */
23998
23999
24000 /**
24001  * @class Roo.data.SortTypes
24002  * @static
24003  * Defines the default sorting (casting?) comparison functions used when sorting data.
24004  */
24005 Roo.data.SortTypes = {
24006     /**
24007      * Default sort that does nothing
24008      * @param {Mixed} s The value being converted
24009      * @return {Mixed} The comparison value
24010      */
24011     none : function(s){
24012         return s;
24013     },
24014     
24015     /**
24016      * The regular expression used to strip tags
24017      * @type {RegExp}
24018      * @property
24019      */
24020     stripTagsRE : /<\/?[^>]+>/gi,
24021     
24022     /**
24023      * Strips all HTML tags to sort on text only
24024      * @param {Mixed} s The value being converted
24025      * @return {String} The comparison value
24026      */
24027     asText : function(s){
24028         return String(s).replace(this.stripTagsRE, "");
24029     },
24030     
24031     /**
24032      * Strips all HTML tags to sort on text only - Case insensitive
24033      * @param {Mixed} s The value being converted
24034      * @return {String} The comparison value
24035      */
24036     asUCText : function(s){
24037         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24038     },
24039     
24040     /**
24041      * Case insensitive string
24042      * @param {Mixed} s The value being converted
24043      * @return {String} The comparison value
24044      */
24045     asUCString : function(s) {
24046         return String(s).toUpperCase();
24047     },
24048     
24049     /**
24050      * Date sorting
24051      * @param {Mixed} s The value being converted
24052      * @return {Number} The comparison value
24053      */
24054     asDate : function(s) {
24055         if(!s){
24056             return 0;
24057         }
24058         if(s instanceof Date){
24059             return s.getTime();
24060         }
24061         return Date.parse(String(s));
24062     },
24063     
24064     /**
24065      * Float sorting
24066      * @param {Mixed} s The value being converted
24067      * @return {Float} The comparison value
24068      */
24069     asFloat : function(s) {
24070         var val = parseFloat(String(s).replace(/,/g, ""));
24071         if(isNaN(val)) {
24072             val = 0;
24073         }
24074         return val;
24075     },
24076     
24077     /**
24078      * Integer sorting
24079      * @param {Mixed} s The value being converted
24080      * @return {Number} The comparison value
24081      */
24082     asInt : function(s) {
24083         var val = parseInt(String(s).replace(/,/g, ""));
24084         if(isNaN(val)) {
24085             val = 0;
24086         }
24087         return val;
24088     }
24089 };/*
24090  * Based on:
24091  * Ext JS Library 1.1.1
24092  * Copyright(c) 2006-2007, Ext JS, LLC.
24093  *
24094  * Originally Released Under LGPL - original licence link has changed is not relivant.
24095  *
24096  * Fork - LGPL
24097  * <script type="text/javascript">
24098  */
24099
24100 /**
24101 * @class Roo.data.Record
24102  * Instances of this class encapsulate both record <em>definition</em> information, and record
24103  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24104  * to access Records cached in an {@link Roo.data.Store} object.<br>
24105  * <p>
24106  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24107  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24108  * objects.<br>
24109  * <p>
24110  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24111  * @constructor
24112  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24113  * {@link #create}. The parameters are the same.
24114  * @param {Array} data An associative Array of data values keyed by the field name.
24115  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24116  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24117  * not specified an integer id is generated.
24118  */
24119 Roo.data.Record = function(data, id){
24120     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24121     this.data = data;
24122 };
24123
24124 /**
24125  * Generate a constructor for a specific record layout.
24126  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24127  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24128  * Each field definition object may contain the following properties: <ul>
24129  * <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,
24130  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24131  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24132  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24133  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24134  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24135  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24136  * this may be omitted.</p></li>
24137  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24138  * <ul><li>auto (Default, implies no conversion)</li>
24139  * <li>string</li>
24140  * <li>int</li>
24141  * <li>float</li>
24142  * <li>boolean</li>
24143  * <li>date</li></ul></p></li>
24144  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24145  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24146  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24147  * by the Reader into an object that will be stored in the Record. It is passed the
24148  * following parameters:<ul>
24149  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24150  * </ul></p></li>
24151  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24152  * </ul>
24153  * <br>usage:<br><pre><code>
24154 var TopicRecord = Roo.data.Record.create(
24155     {name: 'title', mapping: 'topic_title'},
24156     {name: 'author', mapping: 'username'},
24157     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24158     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24159     {name: 'lastPoster', mapping: 'user2'},
24160     {name: 'excerpt', mapping: 'post_text'}
24161 );
24162
24163 var myNewRecord = new TopicRecord({
24164     title: 'Do my job please',
24165     author: 'noobie',
24166     totalPosts: 1,
24167     lastPost: new Date(),
24168     lastPoster: 'Animal',
24169     excerpt: 'No way dude!'
24170 });
24171 myStore.add(myNewRecord);
24172 </code></pre>
24173  * @method create
24174  * @static
24175  */
24176 Roo.data.Record.create = function(o){
24177     var f = function(){
24178         f.superclass.constructor.apply(this, arguments);
24179     };
24180     Roo.extend(f, Roo.data.Record);
24181     var p = f.prototype;
24182     p.fields = new Roo.util.MixedCollection(false, function(field){
24183         return field.name;
24184     });
24185     for(var i = 0, len = o.length; i < len; i++){
24186         p.fields.add(new Roo.data.Field(o[i]));
24187     }
24188     f.getField = function(name){
24189         return p.fields.get(name);  
24190     };
24191     return f;
24192 };
24193
24194 Roo.data.Record.AUTO_ID = 1000;
24195 Roo.data.Record.EDIT = 'edit';
24196 Roo.data.Record.REJECT = 'reject';
24197 Roo.data.Record.COMMIT = 'commit';
24198
24199 Roo.data.Record.prototype = {
24200     /**
24201      * Readonly flag - true if this record has been modified.
24202      * @type Boolean
24203      */
24204     dirty : false,
24205     editing : false,
24206     error: null,
24207     modified: null,
24208
24209     // private
24210     join : function(store){
24211         this.store = store;
24212     },
24213
24214     /**
24215      * Set the named field to the specified value.
24216      * @param {String} name The name of the field to set.
24217      * @param {Object} value The value to set the field to.
24218      */
24219     set : function(name, value){
24220         if(this.data[name] == value){
24221             return;
24222         }
24223         this.dirty = true;
24224         if(!this.modified){
24225             this.modified = {};
24226         }
24227         if(typeof this.modified[name] == 'undefined'){
24228             this.modified[name] = this.data[name];
24229         }
24230         this.data[name] = value;
24231         if(!this.editing && this.store){
24232             this.store.afterEdit(this);
24233         }       
24234     },
24235
24236     /**
24237      * Get the value of the named field.
24238      * @param {String} name The name of the field to get the value of.
24239      * @return {Object} The value of the field.
24240      */
24241     get : function(name){
24242         return this.data[name]; 
24243     },
24244
24245     // private
24246     beginEdit : function(){
24247         this.editing = true;
24248         this.modified = {}; 
24249     },
24250
24251     // private
24252     cancelEdit : function(){
24253         this.editing = false;
24254         delete this.modified;
24255     },
24256
24257     // private
24258     endEdit : function(){
24259         this.editing = false;
24260         if(this.dirty && this.store){
24261             this.store.afterEdit(this);
24262         }
24263     },
24264
24265     /**
24266      * Usually called by the {@link Roo.data.Store} which owns the Record.
24267      * Rejects all changes made to the Record since either creation, or the last commit operation.
24268      * Modified fields are reverted to their original values.
24269      * <p>
24270      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24271      * of reject operations.
24272      */
24273     reject : function(){
24274         var m = this.modified;
24275         for(var n in m){
24276             if(typeof m[n] != "function"){
24277                 this.data[n] = m[n];
24278             }
24279         }
24280         this.dirty = false;
24281         delete this.modified;
24282         this.editing = false;
24283         if(this.store){
24284             this.store.afterReject(this);
24285         }
24286     },
24287
24288     /**
24289      * Usually called by the {@link Roo.data.Store} which owns the Record.
24290      * Commits all changes made to the Record since either creation, or the last commit operation.
24291      * <p>
24292      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24293      * of commit operations.
24294      */
24295     commit : function(){
24296         this.dirty = false;
24297         delete this.modified;
24298         this.editing = false;
24299         if(this.store){
24300             this.store.afterCommit(this);
24301         }
24302     },
24303
24304     // private
24305     hasError : function(){
24306         return this.error != null;
24307     },
24308
24309     // private
24310     clearError : function(){
24311         this.error = null;
24312     },
24313
24314     /**
24315      * Creates a copy of this record.
24316      * @param {String} id (optional) A new record id if you don't want to use this record's id
24317      * @return {Record}
24318      */
24319     copy : function(newId) {
24320         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24321     }
24322 };/*
24323  * Based on:
24324  * Ext JS Library 1.1.1
24325  * Copyright(c) 2006-2007, Ext JS, LLC.
24326  *
24327  * Originally Released Under LGPL - original licence link has changed is not relivant.
24328  *
24329  * Fork - LGPL
24330  * <script type="text/javascript">
24331  */
24332
24333
24334
24335 /**
24336  * @class Roo.data.Store
24337  * @extends Roo.util.Observable
24338  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24339  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24340  * <p>
24341  * 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
24342  * has no knowledge of the format of the data returned by the Proxy.<br>
24343  * <p>
24344  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24345  * instances from the data object. These records are cached and made available through accessor functions.
24346  * @constructor
24347  * Creates a new Store.
24348  * @param {Object} config A config object containing the objects needed for the Store to access data,
24349  * and read the data into Records.
24350  */
24351 Roo.data.Store = function(config){
24352     this.data = new Roo.util.MixedCollection(false);
24353     this.data.getKey = function(o){
24354         return o.id;
24355     };
24356     this.baseParams = {};
24357     // private
24358     this.paramNames = {
24359         "start" : "start",
24360         "limit" : "limit",
24361         "sort" : "sort",
24362         "dir" : "dir",
24363         "multisort" : "_multisort"
24364     };
24365
24366     if(config && config.data){
24367         this.inlineData = config.data;
24368         delete config.data;
24369     }
24370
24371     Roo.apply(this, config);
24372     
24373     if(this.reader){ // reader passed
24374         this.reader = Roo.factory(this.reader, Roo.data);
24375         this.reader.xmodule = this.xmodule || false;
24376         if(!this.recordType){
24377             this.recordType = this.reader.recordType;
24378         }
24379         if(this.reader.onMetaChange){
24380             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24381         }
24382     }
24383
24384     if(this.recordType){
24385         this.fields = this.recordType.prototype.fields;
24386     }
24387     this.modified = [];
24388
24389     this.addEvents({
24390         /**
24391          * @event datachanged
24392          * Fires when the data cache has changed, and a widget which is using this Store
24393          * as a Record cache should refresh its view.
24394          * @param {Store} this
24395          */
24396         datachanged : true,
24397         /**
24398          * @event metachange
24399          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24400          * @param {Store} this
24401          * @param {Object} meta The JSON metadata
24402          */
24403         metachange : true,
24404         /**
24405          * @event add
24406          * Fires when Records have been added to the Store
24407          * @param {Store} this
24408          * @param {Roo.data.Record[]} records The array of Records added
24409          * @param {Number} index The index at which the record(s) were added
24410          */
24411         add : true,
24412         /**
24413          * @event remove
24414          * Fires when a Record has been removed from the Store
24415          * @param {Store} this
24416          * @param {Roo.data.Record} record The Record that was removed
24417          * @param {Number} index The index at which the record was removed
24418          */
24419         remove : true,
24420         /**
24421          * @event update
24422          * Fires when a Record has been updated
24423          * @param {Store} this
24424          * @param {Roo.data.Record} record The Record that was updated
24425          * @param {String} operation The update operation being performed.  Value may be one of:
24426          * <pre><code>
24427  Roo.data.Record.EDIT
24428  Roo.data.Record.REJECT
24429  Roo.data.Record.COMMIT
24430          * </code></pre>
24431          */
24432         update : true,
24433         /**
24434          * @event clear
24435          * Fires when the data cache has been cleared.
24436          * @param {Store} this
24437          */
24438         clear : true,
24439         /**
24440          * @event beforeload
24441          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24442          * the load action will be canceled.
24443          * @param {Store} this
24444          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24445          */
24446         beforeload : true,
24447         /**
24448          * @event beforeloadadd
24449          * Fires after a new set of Records has been loaded.
24450          * @param {Store} this
24451          * @param {Roo.data.Record[]} records The Records that were loaded
24452          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24453          */
24454         beforeloadadd : true,
24455         /**
24456          * @event load
24457          * Fires after a new set of Records has been loaded, before they are added to the store.
24458          * @param {Store} this
24459          * @param {Roo.data.Record[]} records The Records that were loaded
24460          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24461          * @params {Object} return from reader
24462          */
24463         load : true,
24464         /**
24465          * @event loadexception
24466          * Fires if an exception occurs in the Proxy during loading.
24467          * Called with the signature of the Proxy's "loadexception" event.
24468          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24469          * 
24470          * @param {Proxy} 
24471          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24472          * @param {Object} load options 
24473          * @param {Object} jsonData from your request (normally this contains the Exception)
24474          */
24475         loadexception : true
24476     });
24477     
24478     if(this.proxy){
24479         this.proxy = Roo.factory(this.proxy, Roo.data);
24480         this.proxy.xmodule = this.xmodule || false;
24481         this.relayEvents(this.proxy,  ["loadexception"]);
24482     }
24483     this.sortToggle = {};
24484     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24485
24486     Roo.data.Store.superclass.constructor.call(this);
24487
24488     if(this.inlineData){
24489         this.loadData(this.inlineData);
24490         delete this.inlineData;
24491     }
24492 };
24493
24494 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24495      /**
24496     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24497     * without a remote query - used by combo/forms at present.
24498     */
24499     
24500     /**
24501     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24502     */
24503     /**
24504     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24505     */
24506     /**
24507     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24508     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24509     */
24510     /**
24511     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24512     * on any HTTP request
24513     */
24514     /**
24515     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24516     */
24517     /**
24518     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24519     */
24520     multiSort: false,
24521     /**
24522     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24523     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24524     */
24525     remoteSort : false,
24526
24527     /**
24528     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24529      * loaded or when a record is removed. (defaults to false).
24530     */
24531     pruneModifiedRecords : false,
24532
24533     // private
24534     lastOptions : null,
24535
24536     /**
24537      * Add Records to the Store and fires the add event.
24538      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24539      */
24540     add : function(records){
24541         records = [].concat(records);
24542         for(var i = 0, len = records.length; i < len; i++){
24543             records[i].join(this);
24544         }
24545         var index = this.data.length;
24546         this.data.addAll(records);
24547         this.fireEvent("add", this, records, index);
24548     },
24549
24550     /**
24551      * Remove a Record from the Store and fires the remove event.
24552      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24553      */
24554     remove : function(record){
24555         var index = this.data.indexOf(record);
24556         this.data.removeAt(index);
24557  
24558         if(this.pruneModifiedRecords){
24559             this.modified.remove(record);
24560         }
24561         this.fireEvent("remove", this, record, index);
24562     },
24563
24564     /**
24565      * Remove all Records from the Store and fires the clear event.
24566      */
24567     removeAll : function(){
24568         this.data.clear();
24569         if(this.pruneModifiedRecords){
24570             this.modified = [];
24571         }
24572         this.fireEvent("clear", this);
24573     },
24574
24575     /**
24576      * Inserts Records to the Store at the given index and fires the add event.
24577      * @param {Number} index The start index at which to insert the passed Records.
24578      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24579      */
24580     insert : function(index, records){
24581         records = [].concat(records);
24582         for(var i = 0, len = records.length; i < len; i++){
24583             this.data.insert(index, records[i]);
24584             records[i].join(this);
24585         }
24586         this.fireEvent("add", this, records, index);
24587     },
24588
24589     /**
24590      * Get the index within the cache of the passed Record.
24591      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24592      * @return {Number} The index of the passed Record. Returns -1 if not found.
24593      */
24594     indexOf : function(record){
24595         return this.data.indexOf(record);
24596     },
24597
24598     /**
24599      * Get the index within the cache of the Record with the passed id.
24600      * @param {String} id The id of the Record to find.
24601      * @return {Number} The index of the Record. Returns -1 if not found.
24602      */
24603     indexOfId : function(id){
24604         return this.data.indexOfKey(id);
24605     },
24606
24607     /**
24608      * Get the Record with the specified id.
24609      * @param {String} id The id of the Record to find.
24610      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24611      */
24612     getById : function(id){
24613         return this.data.key(id);
24614     },
24615
24616     /**
24617      * Get the Record at the specified index.
24618      * @param {Number} index The index of the Record to find.
24619      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24620      */
24621     getAt : function(index){
24622         return this.data.itemAt(index);
24623     },
24624
24625     /**
24626      * Returns a range of Records between specified indices.
24627      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24628      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24629      * @return {Roo.data.Record[]} An array of Records
24630      */
24631     getRange : function(start, end){
24632         return this.data.getRange(start, end);
24633     },
24634
24635     // private
24636     storeOptions : function(o){
24637         o = Roo.apply({}, o);
24638         delete o.callback;
24639         delete o.scope;
24640         this.lastOptions = o;
24641     },
24642
24643     /**
24644      * Loads the Record cache from the configured Proxy using the configured Reader.
24645      * <p>
24646      * If using remote paging, then the first load call must specify the <em>start</em>
24647      * and <em>limit</em> properties in the options.params property to establish the initial
24648      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24649      * <p>
24650      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24651      * and this call will return before the new data has been loaded. Perform any post-processing
24652      * in a callback function, or in a "load" event handler.</strong>
24653      * <p>
24654      * @param {Object} options An object containing properties which control loading options:<ul>
24655      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24656      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24657      * passed the following arguments:<ul>
24658      * <li>r : Roo.data.Record[]</li>
24659      * <li>options: Options object from the load call</li>
24660      * <li>success: Boolean success indicator</li></ul></li>
24661      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24662      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24663      * </ul>
24664      */
24665     load : function(options){
24666         options = options || {};
24667         if(this.fireEvent("beforeload", this, options) !== false){
24668             this.storeOptions(options);
24669             var p = Roo.apply(options.params || {}, this.baseParams);
24670             // if meta was not loaded from remote source.. try requesting it.
24671             if (!this.reader.metaFromRemote) {
24672                 p._requestMeta = 1;
24673             }
24674             if(this.sortInfo && this.remoteSort){
24675                 var pn = this.paramNames;
24676                 p[pn["sort"]] = this.sortInfo.field;
24677                 p[pn["dir"]] = this.sortInfo.direction;
24678             }
24679             if (this.multiSort) {
24680                 var pn = this.paramNames;
24681                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24682             }
24683             
24684             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24685         }
24686     },
24687
24688     /**
24689      * Reloads the Record cache from the configured Proxy using the configured Reader and
24690      * the options from the last load operation performed.
24691      * @param {Object} options (optional) An object containing properties which may override the options
24692      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24693      * the most recently used options are reused).
24694      */
24695     reload : function(options){
24696         this.load(Roo.applyIf(options||{}, this.lastOptions));
24697     },
24698
24699     // private
24700     // Called as a callback by the Reader during a load operation.
24701     loadRecords : function(o, options, success){
24702          
24703         if(!o){
24704             if(success !== false){
24705                 this.fireEvent("load", this, [], options, o);
24706             }
24707             if(options.callback){
24708                 options.callback.call(options.scope || this, [], options, false);
24709             }
24710             return;
24711         }
24712         // if data returned failure - throw an exception.
24713         if (o.success === false) {
24714             // show a message if no listener is registered.
24715             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24716                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24717             }
24718             // loadmask wil be hooked into this..
24719             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24720             return;
24721         }
24722         var r = o.records, t = o.totalRecords || r.length;
24723         
24724         this.fireEvent("beforeloadadd", this, r, options, o);
24725         
24726         if(!options || options.add !== true){
24727             if(this.pruneModifiedRecords){
24728                 this.modified = [];
24729             }
24730             for(var i = 0, len = r.length; i < len; i++){
24731                 r[i].join(this);
24732             }
24733             if(this.snapshot){
24734                 this.data = this.snapshot;
24735                 delete this.snapshot;
24736             }
24737             this.data.clear();
24738             this.data.addAll(r);
24739             this.totalLength = t;
24740             this.applySort();
24741             this.fireEvent("datachanged", this);
24742         }else{
24743             this.totalLength = Math.max(t, this.data.length+r.length);
24744             this.add(r);
24745         }
24746         
24747         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24748                 
24749             var e = new Roo.data.Record({});
24750
24751             e.set(this.parent.displayField, this.parent.emptyTitle);
24752             e.set(this.parent.valueField, '');
24753
24754             this.insert(0, e);
24755         }
24756             
24757         this.fireEvent("load", this, r, options, o);
24758         if(options.callback){
24759             options.callback.call(options.scope || this, r, options, true);
24760         }
24761     },
24762
24763
24764     /**
24765      * Loads data from a passed data block. A Reader which understands the format of the data
24766      * must have been configured in the constructor.
24767      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24768      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24769      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24770      */
24771     loadData : function(o, append){
24772         var r = this.reader.readRecords(o);
24773         this.loadRecords(r, {add: append}, true);
24774     },
24775     
24776      /**
24777      * using 'cn' the nested child reader read the child array into it's child stores.
24778      * @param {Object} rec The record with a 'children array
24779      */
24780     loadDataFromChildren : function(rec)
24781     {
24782         this.loadData(this.reader.toLoadData(rec));
24783     },
24784     
24785
24786     /**
24787      * Gets the number of cached records.
24788      * <p>
24789      * <em>If using paging, this may not be the total size of the dataset. If the data object
24790      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24791      * the data set size</em>
24792      */
24793     getCount : function(){
24794         return this.data.length || 0;
24795     },
24796
24797     /**
24798      * Gets the total number of records in the dataset as returned by the server.
24799      * <p>
24800      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24801      * the dataset size</em>
24802      */
24803     getTotalCount : function(){
24804         return this.totalLength || 0;
24805     },
24806
24807     /**
24808      * Returns the sort state of the Store as an object with two properties:
24809      * <pre><code>
24810  field {String} The name of the field by which the Records are sorted
24811  direction {String} The sort order, "ASC" or "DESC"
24812      * </code></pre>
24813      */
24814     getSortState : function(){
24815         return this.sortInfo;
24816     },
24817
24818     // private
24819     applySort : function(){
24820         if(this.sortInfo && !this.remoteSort){
24821             var s = this.sortInfo, f = s.field;
24822             var st = this.fields.get(f).sortType;
24823             var fn = function(r1, r2){
24824                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24825                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24826             };
24827             this.data.sort(s.direction, fn);
24828             if(this.snapshot && this.snapshot != this.data){
24829                 this.snapshot.sort(s.direction, fn);
24830             }
24831         }
24832     },
24833
24834     /**
24835      * Sets the default sort column and order to be used by the next load operation.
24836      * @param {String} fieldName The name of the field to sort by.
24837      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24838      */
24839     setDefaultSort : function(field, dir){
24840         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24841     },
24842
24843     /**
24844      * Sort the Records.
24845      * If remote sorting is used, the sort is performed on the server, and the cache is
24846      * reloaded. If local sorting is used, the cache is sorted internally.
24847      * @param {String} fieldName The name of the field to sort by.
24848      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24849      */
24850     sort : function(fieldName, dir){
24851         var f = this.fields.get(fieldName);
24852         if(!dir){
24853             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24854             
24855             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24856                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24857             }else{
24858                 dir = f.sortDir;
24859             }
24860         }
24861         this.sortToggle[f.name] = dir;
24862         this.sortInfo = {field: f.name, direction: dir};
24863         if(!this.remoteSort){
24864             this.applySort();
24865             this.fireEvent("datachanged", this);
24866         }else{
24867             this.load(this.lastOptions);
24868         }
24869     },
24870
24871     /**
24872      * Calls the specified function for each of the Records in the cache.
24873      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24874      * Returning <em>false</em> aborts and exits the iteration.
24875      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24876      */
24877     each : function(fn, scope){
24878         this.data.each(fn, scope);
24879     },
24880
24881     /**
24882      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24883      * (e.g., during paging).
24884      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24885      */
24886     getModifiedRecords : function(){
24887         return this.modified;
24888     },
24889
24890     // private
24891     createFilterFn : function(property, value, anyMatch){
24892         if(!value.exec){ // not a regex
24893             value = String(value);
24894             if(value.length == 0){
24895                 return false;
24896             }
24897             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24898         }
24899         return function(r){
24900             return value.test(r.data[property]);
24901         };
24902     },
24903
24904     /**
24905      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24906      * @param {String} property A field on your records
24907      * @param {Number} start The record index to start at (defaults to 0)
24908      * @param {Number} end The last record index to include (defaults to length - 1)
24909      * @return {Number} The sum
24910      */
24911     sum : function(property, start, end){
24912         var rs = this.data.items, v = 0;
24913         start = start || 0;
24914         end = (end || end === 0) ? end : rs.length-1;
24915
24916         for(var i = start; i <= end; i++){
24917             v += (rs[i].data[property] || 0);
24918         }
24919         return v;
24920     },
24921
24922     /**
24923      * Filter the records by a specified property.
24924      * @param {String} field A field on your records
24925      * @param {String/RegExp} value Either a string that the field
24926      * should start with or a RegExp to test against the field
24927      * @param {Boolean} anyMatch True to match any part not just the beginning
24928      */
24929     filter : function(property, value, anyMatch){
24930         var fn = this.createFilterFn(property, value, anyMatch);
24931         return fn ? this.filterBy(fn) : this.clearFilter();
24932     },
24933
24934     /**
24935      * Filter by a function. The specified function will be called with each
24936      * record in this data source. If the function returns true the record is included,
24937      * otherwise it is filtered.
24938      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24939      * @param {Object} scope (optional) The scope of the function (defaults to this)
24940      */
24941     filterBy : function(fn, scope){
24942         this.snapshot = this.snapshot || this.data;
24943         this.data = this.queryBy(fn, scope||this);
24944         this.fireEvent("datachanged", this);
24945     },
24946
24947     /**
24948      * Query the records by a specified property.
24949      * @param {String} field A field on your records
24950      * @param {String/RegExp} value Either a string that the field
24951      * should start with or a RegExp to test against the field
24952      * @param {Boolean} anyMatch True to match any part not just the beginning
24953      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24954      */
24955     query : function(property, value, anyMatch){
24956         var fn = this.createFilterFn(property, value, anyMatch);
24957         return fn ? this.queryBy(fn) : this.data.clone();
24958     },
24959
24960     /**
24961      * Query by a function. The specified function will be called with each
24962      * record in this data source. If the function returns true the record is included
24963      * in the results.
24964      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24965      * @param {Object} scope (optional) The scope of the function (defaults to this)
24966       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24967      **/
24968     queryBy : function(fn, scope){
24969         var data = this.snapshot || this.data;
24970         return data.filterBy(fn, scope||this);
24971     },
24972
24973     /**
24974      * Collects unique values for a particular dataIndex from this store.
24975      * @param {String} dataIndex The property to collect
24976      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24977      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24978      * @return {Array} An array of the unique values
24979      **/
24980     collect : function(dataIndex, allowNull, bypassFilter){
24981         var d = (bypassFilter === true && this.snapshot) ?
24982                 this.snapshot.items : this.data.items;
24983         var v, sv, r = [], l = {};
24984         for(var i = 0, len = d.length; i < len; i++){
24985             v = d[i].data[dataIndex];
24986             sv = String(v);
24987             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24988                 l[sv] = true;
24989                 r[r.length] = v;
24990             }
24991         }
24992         return r;
24993     },
24994
24995     /**
24996      * Revert to a view of the Record cache with no filtering applied.
24997      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24998      */
24999     clearFilter : function(suppressEvent){
25000         if(this.snapshot && this.snapshot != this.data){
25001             this.data = this.snapshot;
25002             delete this.snapshot;
25003             if(suppressEvent !== true){
25004                 this.fireEvent("datachanged", this);
25005             }
25006         }
25007     },
25008
25009     // private
25010     afterEdit : function(record){
25011         if(this.modified.indexOf(record) == -1){
25012             this.modified.push(record);
25013         }
25014         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25015     },
25016     
25017     // private
25018     afterReject : function(record){
25019         this.modified.remove(record);
25020         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25021     },
25022
25023     // private
25024     afterCommit : function(record){
25025         this.modified.remove(record);
25026         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25027     },
25028
25029     /**
25030      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25031      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25032      */
25033     commitChanges : function(){
25034         var m = this.modified.slice(0);
25035         this.modified = [];
25036         for(var i = 0, len = m.length; i < len; i++){
25037             m[i].commit();
25038         }
25039     },
25040
25041     /**
25042      * Cancel outstanding changes on all changed records.
25043      */
25044     rejectChanges : function(){
25045         var m = this.modified.slice(0);
25046         this.modified = [];
25047         for(var i = 0, len = m.length; i < len; i++){
25048             m[i].reject();
25049         }
25050     },
25051
25052     onMetaChange : function(meta, rtype, o){
25053         this.recordType = rtype;
25054         this.fields = rtype.prototype.fields;
25055         delete this.snapshot;
25056         this.sortInfo = meta.sortInfo || this.sortInfo;
25057         this.modified = [];
25058         this.fireEvent('metachange', this, this.reader.meta);
25059     },
25060     
25061     moveIndex : function(data, type)
25062     {
25063         var index = this.indexOf(data);
25064         
25065         var newIndex = index + type;
25066         
25067         this.remove(data);
25068         
25069         this.insert(newIndex, data);
25070         
25071     }
25072 });/*
25073  * Based on:
25074  * Ext JS Library 1.1.1
25075  * Copyright(c) 2006-2007, Ext JS, LLC.
25076  *
25077  * Originally Released Under LGPL - original licence link has changed is not relivant.
25078  *
25079  * Fork - LGPL
25080  * <script type="text/javascript">
25081  */
25082
25083 /**
25084  * @class Roo.data.SimpleStore
25085  * @extends Roo.data.Store
25086  * Small helper class to make creating Stores from Array data easier.
25087  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25088  * @cfg {Array} fields An array of field definition objects, or field name strings.
25089  * @cfg {Object} an existing reader (eg. copied from another store)
25090  * @cfg {Array} data The multi-dimensional array of data
25091  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25092  * @cfg {Roo.data.Reader} reader  [not-required] 
25093  * @constructor
25094  * @param {Object} config
25095  */
25096 Roo.data.SimpleStore = function(config)
25097 {
25098     Roo.data.SimpleStore.superclass.constructor.call(this, {
25099         isLocal : true,
25100         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25101                 id: config.id
25102             },
25103             Roo.data.Record.create(config.fields)
25104         ),
25105         proxy : new Roo.data.MemoryProxy(config.data)
25106     });
25107     this.load();
25108 };
25109 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25110  * Based on:
25111  * Ext JS Library 1.1.1
25112  * Copyright(c) 2006-2007, Ext JS, LLC.
25113  *
25114  * Originally Released Under LGPL - original licence link has changed is not relivant.
25115  *
25116  * Fork - LGPL
25117  * <script type="text/javascript">
25118  */
25119
25120 /**
25121 /**
25122  * @extends Roo.data.Store
25123  * @class Roo.data.JsonStore
25124  * Small helper class to make creating Stores for JSON data easier. <br/>
25125 <pre><code>
25126 var store = new Roo.data.JsonStore({
25127     url: 'get-images.php',
25128     root: 'images',
25129     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25130 });
25131 </code></pre>
25132  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25133  * JsonReader and HttpProxy (unless inline data is provided).</b>
25134  * @cfg {Array} fields An array of field definition objects, or field name strings.
25135  * @constructor
25136  * @param {Object} config
25137  */
25138 Roo.data.JsonStore = function(c){
25139     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25140         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25141         reader: new Roo.data.JsonReader(c, c.fields)
25142     }));
25143 };
25144 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25145  * Based on:
25146  * Ext JS Library 1.1.1
25147  * Copyright(c) 2006-2007, Ext JS, LLC.
25148  *
25149  * Originally Released Under LGPL - original licence link has changed is not relivant.
25150  *
25151  * Fork - LGPL
25152  * <script type="text/javascript">
25153  */
25154
25155  
25156 Roo.data.Field = function(config){
25157     if(typeof config == "string"){
25158         config = {name: config};
25159     }
25160     Roo.apply(this, config);
25161     
25162     if(!this.type){
25163         this.type = "auto";
25164     }
25165     
25166     var st = Roo.data.SortTypes;
25167     // named sortTypes are supported, here we look them up
25168     if(typeof this.sortType == "string"){
25169         this.sortType = st[this.sortType];
25170     }
25171     
25172     // set default sortType for strings and dates
25173     if(!this.sortType){
25174         switch(this.type){
25175             case "string":
25176                 this.sortType = st.asUCString;
25177                 break;
25178             case "date":
25179                 this.sortType = st.asDate;
25180                 break;
25181             default:
25182                 this.sortType = st.none;
25183         }
25184     }
25185
25186     // define once
25187     var stripRe = /[\$,%]/g;
25188
25189     // prebuilt conversion function for this field, instead of
25190     // switching every time we're reading a value
25191     if(!this.convert){
25192         var cv, dateFormat = this.dateFormat;
25193         switch(this.type){
25194             case "":
25195             case "auto":
25196             case undefined:
25197                 cv = function(v){ return v; };
25198                 break;
25199             case "string":
25200                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25201                 break;
25202             case "int":
25203                 cv = function(v){
25204                     return v !== undefined && v !== null && v !== '' ?
25205                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25206                     };
25207                 break;
25208             case "float":
25209                 cv = function(v){
25210                     return v !== undefined && v !== null && v !== '' ?
25211                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25212                     };
25213                 break;
25214             case "bool":
25215             case "boolean":
25216                 cv = function(v){ return v === true || v === "true" || v == 1; };
25217                 break;
25218             case "date":
25219                 cv = function(v){
25220                     if(!v){
25221                         return '';
25222                     }
25223                     if(v instanceof Date){
25224                         return v;
25225                     }
25226                     if(dateFormat){
25227                         if(dateFormat == "timestamp"){
25228                             return new Date(v*1000);
25229                         }
25230                         return Date.parseDate(v, dateFormat);
25231                     }
25232                     var parsed = Date.parse(v);
25233                     return parsed ? new Date(parsed) : null;
25234                 };
25235              break;
25236             
25237         }
25238         this.convert = cv;
25239     }
25240 };
25241
25242 Roo.data.Field.prototype = {
25243     dateFormat: null,
25244     defaultValue: "",
25245     mapping: null,
25246     sortType : null,
25247     sortDir : "ASC"
25248 };/*
25249  * Based on:
25250  * Ext JS Library 1.1.1
25251  * Copyright(c) 2006-2007, Ext JS, LLC.
25252  *
25253  * Originally Released Under LGPL - original licence link has changed is not relivant.
25254  *
25255  * Fork - LGPL
25256  * <script type="text/javascript">
25257  */
25258  
25259 // Base class for reading structured data from a data source.  This class is intended to be
25260 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25261
25262 /**
25263  * @class Roo.data.DataReader
25264  * @abstract
25265  * Base class for reading structured data from a data source.  This class is intended to be
25266  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25267  */
25268
25269 Roo.data.DataReader = function(meta, recordType){
25270     
25271     this.meta = meta;
25272     
25273     this.recordType = recordType instanceof Array ? 
25274         Roo.data.Record.create(recordType) : recordType;
25275 };
25276
25277 Roo.data.DataReader.prototype = {
25278     
25279     
25280     readerType : 'Data',
25281      /**
25282      * Create an empty record
25283      * @param {Object} data (optional) - overlay some values
25284      * @return {Roo.data.Record} record created.
25285      */
25286     newRow :  function(d) {
25287         var da =  {};
25288         this.recordType.prototype.fields.each(function(c) {
25289             switch( c.type) {
25290                 case 'int' : da[c.name] = 0; break;
25291                 case 'date' : da[c.name] = new Date(); break;
25292                 case 'float' : da[c.name] = 0.0; break;
25293                 case 'boolean' : da[c.name] = false; break;
25294                 default : da[c.name] = ""; break;
25295             }
25296             
25297         });
25298         return new this.recordType(Roo.apply(da, d));
25299     }
25300     
25301     
25302 };/*
25303  * Based on:
25304  * Ext JS Library 1.1.1
25305  * Copyright(c) 2006-2007, Ext JS, LLC.
25306  *
25307  * Originally Released Under LGPL - original licence link has changed is not relivant.
25308  *
25309  * Fork - LGPL
25310  * <script type="text/javascript">
25311  */
25312
25313 /**
25314  * @class Roo.data.DataProxy
25315  * @extends Roo.util.Observable
25316  * @abstract
25317  * This class is an abstract base class for implementations which provide retrieval of
25318  * unformatted data objects.<br>
25319  * <p>
25320  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25321  * (of the appropriate type which knows how to parse the data object) to provide a block of
25322  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25323  * <p>
25324  * Custom implementations must implement the load method as described in
25325  * {@link Roo.data.HttpProxy#load}.
25326  */
25327 Roo.data.DataProxy = function(){
25328     this.addEvents({
25329         /**
25330          * @event beforeload
25331          * Fires before a network request is made to retrieve a data object.
25332          * @param {Object} This DataProxy object.
25333          * @param {Object} params The params parameter to the load function.
25334          */
25335         beforeload : true,
25336         /**
25337          * @event load
25338          * Fires before the load method's callback is called.
25339          * @param {Object} This DataProxy object.
25340          * @param {Object} o The data object.
25341          * @param {Object} arg The callback argument object passed to the load function.
25342          */
25343         load : true,
25344         /**
25345          * @event loadexception
25346          * Fires if an Exception occurs during data retrieval.
25347          * @param {Object} This DataProxy object.
25348          * @param {Object} o The data object.
25349          * @param {Object} arg The callback argument object passed to the load function.
25350          * @param {Object} e The Exception.
25351          */
25352         loadexception : true
25353     });
25354     Roo.data.DataProxy.superclass.constructor.call(this);
25355 };
25356
25357 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25358
25359     /**
25360      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25361      */
25362 /*
25363  * Based on:
25364  * Ext JS Library 1.1.1
25365  * Copyright(c) 2006-2007, Ext JS, LLC.
25366  *
25367  * Originally Released Under LGPL - original licence link has changed is not relivant.
25368  *
25369  * Fork - LGPL
25370  * <script type="text/javascript">
25371  */
25372 /**
25373  * @class Roo.data.MemoryProxy
25374  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25375  * to the Reader when its load method is called.
25376  * @constructor
25377  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25378  */
25379 Roo.data.MemoryProxy = function(data){
25380     if (data.data) {
25381         data = data.data;
25382     }
25383     Roo.data.MemoryProxy.superclass.constructor.call(this);
25384     this.data = data;
25385 };
25386
25387 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25388     
25389     /**
25390      * Load data from the requested source (in this case an in-memory
25391      * data object passed to the constructor), read the data object into
25392      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25393      * process that block using the passed callback.
25394      * @param {Object} params This parameter is not used by the MemoryProxy class.
25395      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25396      * object into a block of Roo.data.Records.
25397      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25398      * The function must be passed <ul>
25399      * <li>The Record block object</li>
25400      * <li>The "arg" argument from the load function</li>
25401      * <li>A boolean success indicator</li>
25402      * </ul>
25403      * @param {Object} scope The scope in which to call the callback
25404      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25405      */
25406     load : function(params, reader, callback, scope, arg){
25407         params = params || {};
25408         var result;
25409         try {
25410             result = reader.readRecords(params.data ? params.data :this.data);
25411         }catch(e){
25412             this.fireEvent("loadexception", this, arg, null, e);
25413             callback.call(scope, null, arg, false);
25414             return;
25415         }
25416         callback.call(scope, result, arg, true);
25417     },
25418     
25419     // private
25420     update : function(params, records){
25421         
25422     }
25423 });/*
25424  * Based on:
25425  * Ext JS Library 1.1.1
25426  * Copyright(c) 2006-2007, Ext JS, LLC.
25427  *
25428  * Originally Released Under LGPL - original licence link has changed is not relivant.
25429  *
25430  * Fork - LGPL
25431  * <script type="text/javascript">
25432  */
25433 /**
25434  * @class Roo.data.HttpProxy
25435  * @extends Roo.data.DataProxy
25436  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25437  * configured to reference a certain URL.<br><br>
25438  * <p>
25439  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25440  * from which the running page was served.<br><br>
25441  * <p>
25442  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25443  * <p>
25444  * Be aware that to enable the browser to parse an XML document, the server must set
25445  * the Content-Type header in the HTTP response to "text/xml".
25446  * @constructor
25447  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25448  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25449  * will be used to make the request.
25450  */
25451 Roo.data.HttpProxy = function(conn){
25452     Roo.data.HttpProxy.superclass.constructor.call(this);
25453     // is conn a conn config or a real conn?
25454     this.conn = conn;
25455     this.useAjax = !conn || !conn.events;
25456   
25457 };
25458
25459 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25460     // thse are take from connection...
25461     
25462     /**
25463      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25464      */
25465     /**
25466      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25467      * extra parameters to each request made by this object. (defaults to undefined)
25468      */
25469     /**
25470      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25471      *  to each request made by this object. (defaults to undefined)
25472      */
25473     /**
25474      * @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)
25475      */
25476     /**
25477      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25478      */
25479      /**
25480      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25481      * @type Boolean
25482      */
25483   
25484
25485     /**
25486      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25487      * @type Boolean
25488      */
25489     /**
25490      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25491      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25492      * a finer-grained basis than the DataProxy events.
25493      */
25494     getConnection : function(){
25495         return this.useAjax ? Roo.Ajax : this.conn;
25496     },
25497
25498     /**
25499      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25500      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25501      * process that block using the passed callback.
25502      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25503      * for the request to the remote server.
25504      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25505      * object into a block of Roo.data.Records.
25506      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25507      * The function must be passed <ul>
25508      * <li>The Record block object</li>
25509      * <li>The "arg" argument from the load function</li>
25510      * <li>A boolean success indicator</li>
25511      * </ul>
25512      * @param {Object} scope The scope in which to call the callback
25513      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25514      */
25515     load : function(params, reader, callback, scope, arg){
25516         if(this.fireEvent("beforeload", this, params) !== false){
25517             var  o = {
25518                 params : params || {},
25519                 request: {
25520                     callback : callback,
25521                     scope : scope,
25522                     arg : arg
25523                 },
25524                 reader: reader,
25525                 callback : this.loadResponse,
25526                 scope: this
25527             };
25528             if(this.useAjax){
25529                 Roo.applyIf(o, this.conn);
25530                 if(this.activeRequest){
25531                     Roo.Ajax.abort(this.activeRequest);
25532                 }
25533                 this.activeRequest = Roo.Ajax.request(o);
25534             }else{
25535                 this.conn.request(o);
25536             }
25537         }else{
25538             callback.call(scope||this, null, arg, false);
25539         }
25540     },
25541
25542     // private
25543     loadResponse : function(o, success, response){
25544         delete this.activeRequest;
25545         if(!success){
25546             this.fireEvent("loadexception", this, o, response);
25547             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25548             return;
25549         }
25550         var result;
25551         try {
25552             result = o.reader.read(response);
25553         }catch(e){
25554             o.success = false;
25555             o.raw = { errorMsg : response.responseText };
25556             this.fireEvent("loadexception", this, o, response, e);
25557             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25558             return;
25559         }
25560         
25561         this.fireEvent("load", this, o, o.request.arg);
25562         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25563     },
25564
25565     // private
25566     update : function(dataSet){
25567
25568     },
25569
25570     // private
25571     updateResponse : function(dataSet){
25572
25573     }
25574 });/*
25575  * Based on:
25576  * Ext JS Library 1.1.1
25577  * Copyright(c) 2006-2007, Ext JS, LLC.
25578  *
25579  * Originally Released Under LGPL - original licence link has changed is not relivant.
25580  *
25581  * Fork - LGPL
25582  * <script type="text/javascript">
25583  */
25584
25585 /**
25586  * @class Roo.data.ScriptTagProxy
25587  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25588  * other than the originating domain of the running page.<br><br>
25589  * <p>
25590  * <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
25591  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25592  * <p>
25593  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25594  * source code that is used as the source inside a &lt;script> tag.<br><br>
25595  * <p>
25596  * In order for the browser to process the returned data, the server must wrap the data object
25597  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25598  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25599  * depending on whether the callback name was passed:
25600  * <p>
25601  * <pre><code>
25602 boolean scriptTag = false;
25603 String cb = request.getParameter("callback");
25604 if (cb != null) {
25605     scriptTag = true;
25606     response.setContentType("text/javascript");
25607 } else {
25608     response.setContentType("application/x-json");
25609 }
25610 Writer out = response.getWriter();
25611 if (scriptTag) {
25612     out.write(cb + "(");
25613 }
25614 out.print(dataBlock.toJsonString());
25615 if (scriptTag) {
25616     out.write(");");
25617 }
25618 </pre></code>
25619  *
25620  * @constructor
25621  * @param {Object} config A configuration object.
25622  */
25623 Roo.data.ScriptTagProxy = function(config){
25624     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25625     Roo.apply(this, config);
25626     this.head = document.getElementsByTagName("head")[0];
25627 };
25628
25629 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25630
25631 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25632     /**
25633      * @cfg {String} url The URL from which to request the data object.
25634      */
25635     /**
25636      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25637      */
25638     timeout : 30000,
25639     /**
25640      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25641      * the server the name of the callback function set up by the load call to process the returned data object.
25642      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25643      * javascript output which calls this named function passing the data object as its only parameter.
25644      */
25645     callbackParam : "callback",
25646     /**
25647      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25648      * name to the request.
25649      */
25650     nocache : true,
25651
25652     /**
25653      * Load data from the configured URL, read the data object into
25654      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25655      * process that block using the passed callback.
25656      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25657      * for the request to the remote server.
25658      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25659      * object into a block of Roo.data.Records.
25660      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25661      * The function must be passed <ul>
25662      * <li>The Record block object</li>
25663      * <li>The "arg" argument from the load function</li>
25664      * <li>A boolean success indicator</li>
25665      * </ul>
25666      * @param {Object} scope The scope in which to call the callback
25667      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25668      */
25669     load : function(params, reader, callback, scope, arg){
25670         if(this.fireEvent("beforeload", this, params) !== false){
25671
25672             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25673
25674             var url = this.url;
25675             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25676             if(this.nocache){
25677                 url += "&_dc=" + (new Date().getTime());
25678             }
25679             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25680             var trans = {
25681                 id : transId,
25682                 cb : "stcCallback"+transId,
25683                 scriptId : "stcScript"+transId,
25684                 params : params,
25685                 arg : arg,
25686                 url : url,
25687                 callback : callback,
25688                 scope : scope,
25689                 reader : reader
25690             };
25691             var conn = this;
25692
25693             window[trans.cb] = function(o){
25694                 conn.handleResponse(o, trans);
25695             };
25696
25697             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25698
25699             if(this.autoAbort !== false){
25700                 this.abort();
25701             }
25702
25703             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25704
25705             var script = document.createElement("script");
25706             script.setAttribute("src", url);
25707             script.setAttribute("type", "text/javascript");
25708             script.setAttribute("id", trans.scriptId);
25709             this.head.appendChild(script);
25710
25711             this.trans = trans;
25712         }else{
25713             callback.call(scope||this, null, arg, false);
25714         }
25715     },
25716
25717     // private
25718     isLoading : function(){
25719         return this.trans ? true : false;
25720     },
25721
25722     /**
25723      * Abort the current server request.
25724      */
25725     abort : function(){
25726         if(this.isLoading()){
25727             this.destroyTrans(this.trans);
25728         }
25729     },
25730
25731     // private
25732     destroyTrans : function(trans, isLoaded){
25733         this.head.removeChild(document.getElementById(trans.scriptId));
25734         clearTimeout(trans.timeoutId);
25735         if(isLoaded){
25736             window[trans.cb] = undefined;
25737             try{
25738                 delete window[trans.cb];
25739             }catch(e){}
25740         }else{
25741             // if hasn't been loaded, wait for load to remove it to prevent script error
25742             window[trans.cb] = function(){
25743                 window[trans.cb] = undefined;
25744                 try{
25745                     delete window[trans.cb];
25746                 }catch(e){}
25747             };
25748         }
25749     },
25750
25751     // private
25752     handleResponse : function(o, trans){
25753         this.trans = false;
25754         this.destroyTrans(trans, true);
25755         var result;
25756         try {
25757             result = trans.reader.readRecords(o);
25758         }catch(e){
25759             this.fireEvent("loadexception", this, o, trans.arg, e);
25760             trans.callback.call(trans.scope||window, null, trans.arg, false);
25761             return;
25762         }
25763         this.fireEvent("load", this, o, trans.arg);
25764         trans.callback.call(trans.scope||window, result, trans.arg, true);
25765     },
25766
25767     // private
25768     handleFailure : function(trans){
25769         this.trans = false;
25770         this.destroyTrans(trans, false);
25771         this.fireEvent("loadexception", this, null, trans.arg);
25772         trans.callback.call(trans.scope||window, null, trans.arg, false);
25773     }
25774 });/*
25775  * Based on:
25776  * Ext JS Library 1.1.1
25777  * Copyright(c) 2006-2007, Ext JS, LLC.
25778  *
25779  * Originally Released Under LGPL - original licence link has changed is not relivant.
25780  *
25781  * Fork - LGPL
25782  * <script type="text/javascript">
25783  */
25784
25785 /**
25786  * @class Roo.data.JsonReader
25787  * @extends Roo.data.DataReader
25788  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25789  * based on mappings in a provided Roo.data.Record constructor.
25790  * 
25791  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25792  * in the reply previously. 
25793  * 
25794  * <p>
25795  * Example code:
25796  * <pre><code>
25797 var RecordDef = Roo.data.Record.create([
25798     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25799     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25800 ]);
25801 var myReader = new Roo.data.JsonReader({
25802     totalProperty: "results",    // The property which contains the total dataset size (optional)
25803     root: "rows",                // The property which contains an Array of row objects
25804     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25805 }, RecordDef);
25806 </code></pre>
25807  * <p>
25808  * This would consume a JSON file like this:
25809  * <pre><code>
25810 { 'results': 2, 'rows': [
25811     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25812     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25813 }
25814 </code></pre>
25815  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25816  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25817  * paged from the remote server.
25818  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25819  * @cfg {String} root name of the property which contains the Array of row objects.
25820  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25821  * @cfg {Array} fields Array of field definition objects
25822  * @constructor
25823  * Create a new JsonReader
25824  * @param {Object} meta Metadata configuration options
25825  * @param {Object} recordType Either an Array of field definition objects,
25826  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25827  */
25828 Roo.data.JsonReader = function(meta, recordType){
25829     
25830     meta = meta || {};
25831     // set some defaults:
25832     Roo.applyIf(meta, {
25833         totalProperty: 'total',
25834         successProperty : 'success',
25835         root : 'data',
25836         id : 'id'
25837     });
25838     
25839     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25840 };
25841 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25842     
25843     readerType : 'Json',
25844     
25845     /**
25846      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25847      * Used by Store query builder to append _requestMeta to params.
25848      * 
25849      */
25850     metaFromRemote : false,
25851     /**
25852      * This method is only used by a DataProxy which has retrieved data from a remote server.
25853      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25854      * @return {Object} data A data block which is used by an Roo.data.Store object as
25855      * a cache of Roo.data.Records.
25856      */
25857     read : function(response){
25858         var json = response.responseText;
25859        
25860         var o = /* eval:var:o */ eval("("+json+")");
25861         if(!o) {
25862             throw {message: "JsonReader.read: Json object not found"};
25863         }
25864         
25865         if(o.metaData){
25866             
25867             delete this.ef;
25868             this.metaFromRemote = true;
25869             this.meta = o.metaData;
25870             this.recordType = Roo.data.Record.create(o.metaData.fields);
25871             this.onMetaChange(this.meta, this.recordType, o);
25872         }
25873         return this.readRecords(o);
25874     },
25875
25876     // private function a store will implement
25877     onMetaChange : function(meta, recordType, o){
25878
25879     },
25880
25881     /**
25882          * @ignore
25883          */
25884     simpleAccess: function(obj, subsc) {
25885         return obj[subsc];
25886     },
25887
25888         /**
25889          * @ignore
25890          */
25891     getJsonAccessor: function(){
25892         var re = /[\[\.]/;
25893         return function(expr) {
25894             try {
25895                 return(re.test(expr))
25896                     ? new Function("obj", "return obj." + expr)
25897                     : function(obj){
25898                         return obj[expr];
25899                     };
25900             } catch(e){}
25901             return Roo.emptyFn;
25902         };
25903     }(),
25904
25905     /**
25906      * Create a data block containing Roo.data.Records from an XML document.
25907      * @param {Object} o An object which contains an Array of row objects in the property specified
25908      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25909      * which contains the total size of the dataset.
25910      * @return {Object} data A data block which is used by an Roo.data.Store object as
25911      * a cache of Roo.data.Records.
25912      */
25913     readRecords : function(o){
25914         /**
25915          * After any data loads, the raw JSON data is available for further custom processing.
25916          * @type Object
25917          */
25918         this.o = o;
25919         var s = this.meta, Record = this.recordType,
25920             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25921
25922 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25923         if (!this.ef) {
25924             if(s.totalProperty) {
25925                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25926                 }
25927                 if(s.successProperty) {
25928                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25929                 }
25930                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25931                 if (s.id) {
25932                         var g = this.getJsonAccessor(s.id);
25933                         this.getId = function(rec) {
25934                                 var r = g(rec);  
25935                                 return (r === undefined || r === "") ? null : r;
25936                         };
25937                 } else {
25938                         this.getId = function(){return null;};
25939                 }
25940             this.ef = [];
25941             for(var jj = 0; jj < fl; jj++){
25942                 f = fi[jj];
25943                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25944                 this.ef[jj] = this.getJsonAccessor(map);
25945             }
25946         }
25947
25948         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25949         if(s.totalProperty){
25950             var vt = parseInt(this.getTotal(o), 10);
25951             if(!isNaN(vt)){
25952                 totalRecords = vt;
25953             }
25954         }
25955         if(s.successProperty){
25956             var vs = this.getSuccess(o);
25957             if(vs === false || vs === 'false'){
25958                 success = false;
25959             }
25960         }
25961         var records = [];
25962         for(var i = 0; i < c; i++){
25963             var n = root[i];
25964             var values = {};
25965             var id = this.getId(n);
25966             for(var j = 0; j < fl; j++){
25967                 f = fi[j];
25968                                 var v = this.ef[j](n);
25969                                 if (!f.convert) {
25970                                         Roo.log('missing convert for ' + f.name);
25971                                         Roo.log(f);
25972                                         continue;
25973                                 }
25974                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25975             }
25976                         if (!Record) {
25977                                 return {
25978                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25979                                         success : false,
25980                                         records : [],
25981                                         totalRecords : 0
25982                                 };
25983                         }
25984             var record = new Record(values, id);
25985             record.json = n;
25986             records[i] = record;
25987         }
25988         return {
25989             raw : o,
25990             success : success,
25991             records : records,
25992             totalRecords : totalRecords
25993         };
25994     },
25995     // used when loading children.. @see loadDataFromChildren
25996     toLoadData: function(rec)
25997     {
25998         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25999         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26000         return { data : data, total : data.length };
26001         
26002     }
26003 });/*
26004  * Based on:
26005  * Ext JS Library 1.1.1
26006  * Copyright(c) 2006-2007, Ext JS, LLC.
26007  *
26008  * Originally Released Under LGPL - original licence link has changed is not relivant.
26009  *
26010  * Fork - LGPL
26011  * <script type="text/javascript">
26012  */
26013
26014 /**
26015  * @class Roo.data.XmlReader
26016  * @extends Roo.data.DataReader
26017  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26018  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26019  * <p>
26020  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26021  * header in the HTTP response must be set to "text/xml".</em>
26022  * <p>
26023  * Example code:
26024  * <pre><code>
26025 var RecordDef = Roo.data.Record.create([
26026    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26027    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26028 ]);
26029 var myReader = new Roo.data.XmlReader({
26030    totalRecords: "results", // The element which contains the total dataset size (optional)
26031    record: "row",           // The repeated element which contains row information
26032    id: "id"                 // The element within the row that provides an ID for the record (optional)
26033 }, RecordDef);
26034 </code></pre>
26035  * <p>
26036  * This would consume an XML file like this:
26037  * <pre><code>
26038 &lt;?xml?>
26039 &lt;dataset>
26040  &lt;results>2&lt;/results>
26041  &lt;row>
26042    &lt;id>1&lt;/id>
26043    &lt;name>Bill&lt;/name>
26044    &lt;occupation>Gardener&lt;/occupation>
26045  &lt;/row>
26046  &lt;row>
26047    &lt;id>2&lt;/id>
26048    &lt;name>Ben&lt;/name>
26049    &lt;occupation>Horticulturalist&lt;/occupation>
26050  &lt;/row>
26051 &lt;/dataset>
26052 </code></pre>
26053  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26054  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26055  * paged from the remote server.
26056  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26057  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26058  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26059  * a record identifier value.
26060  * @constructor
26061  * Create a new XmlReader
26062  * @param {Object} meta Metadata configuration options
26063  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26064  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26065  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26066  */
26067 Roo.data.XmlReader = function(meta, recordType){
26068     meta = meta || {};
26069     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26070 };
26071 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26072     
26073     readerType : 'Xml',
26074     
26075     /**
26076      * This method is only used by a DataProxy which has retrieved data from a remote server.
26077          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26078          * to contain a method called 'responseXML' that returns an XML document object.
26079      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26080      * a cache of Roo.data.Records.
26081      */
26082     read : function(response){
26083         var doc = response.responseXML;
26084         if(!doc) {
26085             throw {message: "XmlReader.read: XML Document not available"};
26086         }
26087         return this.readRecords(doc);
26088     },
26089
26090     /**
26091      * Create a data block containing Roo.data.Records from an XML document.
26092          * @param {Object} doc A parsed XML document.
26093      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26094      * a cache of Roo.data.Records.
26095      */
26096     readRecords : function(doc){
26097         /**
26098          * After any data loads/reads, the raw XML Document is available for further custom processing.
26099          * @type XMLDocument
26100          */
26101         this.xmlData = doc;
26102         var root = doc.documentElement || doc;
26103         var q = Roo.DomQuery;
26104         var recordType = this.recordType, fields = recordType.prototype.fields;
26105         var sid = this.meta.id;
26106         var totalRecords = 0, success = true;
26107         if(this.meta.totalRecords){
26108             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26109         }
26110         
26111         if(this.meta.success){
26112             var sv = q.selectValue(this.meta.success, root, true);
26113             success = sv !== false && sv !== 'false';
26114         }
26115         var records = [];
26116         var ns = q.select(this.meta.record, root);
26117         for(var i = 0, len = ns.length; i < len; i++) {
26118                 var n = ns[i];
26119                 var values = {};
26120                 var id = sid ? q.selectValue(sid, n) : undefined;
26121                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26122                     var f = fields.items[j];
26123                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26124                     v = f.convert(v);
26125                     values[f.name] = v;
26126                 }
26127                 var record = new recordType(values, id);
26128                 record.node = n;
26129                 records[records.length] = record;
26130             }
26131
26132             return {
26133                 success : success,
26134                 records : records,
26135                 totalRecords : totalRecords || records.length
26136             };
26137     }
26138 });/*
26139  * Based on:
26140  * Ext JS Library 1.1.1
26141  * Copyright(c) 2006-2007, Ext JS, LLC.
26142  *
26143  * Originally Released Under LGPL - original licence link has changed is not relivant.
26144  *
26145  * Fork - LGPL
26146  * <script type="text/javascript">
26147  */
26148
26149 /**
26150  * @class Roo.data.ArrayReader
26151  * @extends Roo.data.DataReader
26152  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26153  * Each element of that Array represents a row of data fields. The
26154  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26155  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26156  * <p>
26157  * Example code:.
26158  * <pre><code>
26159 var RecordDef = Roo.data.Record.create([
26160     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26161     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26162 ]);
26163 var myReader = new Roo.data.ArrayReader({
26164     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26165 }, RecordDef);
26166 </code></pre>
26167  * <p>
26168  * This would consume an Array like this:
26169  * <pre><code>
26170 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26171   </code></pre>
26172  
26173  * @constructor
26174  * Create a new JsonReader
26175  * @param {Object} meta Metadata configuration options.
26176  * @param {Object|Array} recordType Either an Array of field definition objects
26177  * 
26178  * @cfg {Array} fields Array of field definition objects
26179  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26180  * as specified to {@link Roo.data.Record#create},
26181  * or an {@link Roo.data.Record} object
26182  *
26183  * 
26184  * created using {@link Roo.data.Record#create}.
26185  */
26186 Roo.data.ArrayReader = function(meta, recordType)
26187 {    
26188     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26189 };
26190
26191 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26192     
26193       /**
26194      * Create a data block containing Roo.data.Records from an XML document.
26195      * @param {Object} o An Array of row objects which represents the dataset.
26196      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26197      * a cache of Roo.data.Records.
26198      */
26199     readRecords : function(o)
26200     {
26201         var sid = this.meta ? this.meta.id : null;
26202         var recordType = this.recordType, fields = recordType.prototype.fields;
26203         var records = [];
26204         var root = o;
26205         for(var i = 0; i < root.length; i++){
26206             var n = root[i];
26207             var values = {};
26208             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26209             for(var j = 0, jlen = fields.length; j < jlen; j++){
26210                 var f = fields.items[j];
26211                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26212                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26213                 v = f.convert(v);
26214                 values[f.name] = v;
26215             }
26216             var record = new recordType(values, id);
26217             record.json = n;
26218             records[records.length] = record;
26219         }
26220         return {
26221             records : records,
26222             totalRecords : records.length
26223         };
26224     },
26225     // used when loading children.. @see loadDataFromChildren
26226     toLoadData: function(rec)
26227     {
26228         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26229         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26230         
26231     }
26232     
26233     
26234 });/*
26235  * Based on:
26236  * Ext JS Library 1.1.1
26237  * Copyright(c) 2006-2007, Ext JS, LLC.
26238  *
26239  * Originally Released Under LGPL - original licence link has changed is not relivant.
26240  *
26241  * Fork - LGPL
26242  * <script type="text/javascript">
26243  */
26244
26245
26246 /**
26247  * @class Roo.data.Tree
26248  * @extends Roo.util.Observable
26249  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26250  * in the tree have most standard DOM functionality.
26251  * @constructor
26252  * @param {Node} root (optional) The root node
26253  */
26254 Roo.data.Tree = function(root){
26255    this.nodeHash = {};
26256    /**
26257     * The root node for this tree
26258     * @type Node
26259     */
26260    this.root = null;
26261    if(root){
26262        this.setRootNode(root);
26263    }
26264    this.addEvents({
26265        /**
26266         * @event append
26267         * Fires when a new child node is appended to a node in this tree.
26268         * @param {Tree} tree The owner tree
26269         * @param {Node} parent The parent node
26270         * @param {Node} node The newly appended node
26271         * @param {Number} index The index of the newly appended node
26272         */
26273        "append" : true,
26274        /**
26275         * @event remove
26276         * Fires when a child node is removed from a node in this tree.
26277         * @param {Tree} tree The owner tree
26278         * @param {Node} parent The parent node
26279         * @param {Node} node The child node removed
26280         */
26281        "remove" : true,
26282        /**
26283         * @event move
26284         * Fires when a node is moved to a new location in the tree
26285         * @param {Tree} tree The owner tree
26286         * @param {Node} node The node moved
26287         * @param {Node} oldParent The old parent of this node
26288         * @param {Node} newParent The new parent of this node
26289         * @param {Number} index The index it was moved to
26290         */
26291        "move" : true,
26292        /**
26293         * @event insert
26294         * Fires when a new child node is inserted in a node in this tree.
26295         * @param {Tree} tree The owner tree
26296         * @param {Node} parent The parent node
26297         * @param {Node} node The child node inserted
26298         * @param {Node} refNode The child node the node was inserted before
26299         */
26300        "insert" : true,
26301        /**
26302         * @event beforeappend
26303         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26304         * @param {Tree} tree The owner tree
26305         * @param {Node} parent The parent node
26306         * @param {Node} node The child node to be appended
26307         */
26308        "beforeappend" : true,
26309        /**
26310         * @event beforeremove
26311         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26312         * @param {Tree} tree The owner tree
26313         * @param {Node} parent The parent node
26314         * @param {Node} node The child node to be removed
26315         */
26316        "beforeremove" : true,
26317        /**
26318         * @event beforemove
26319         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26320         * @param {Tree} tree The owner tree
26321         * @param {Node} node The node being moved
26322         * @param {Node} oldParent The parent of the node
26323         * @param {Node} newParent The new parent the node is moving to
26324         * @param {Number} index The index it is being moved to
26325         */
26326        "beforemove" : true,
26327        /**
26328         * @event beforeinsert
26329         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26330         * @param {Tree} tree The owner tree
26331         * @param {Node} parent The parent node
26332         * @param {Node} node The child node to be inserted
26333         * @param {Node} refNode The child node the node is being inserted before
26334         */
26335        "beforeinsert" : true
26336    });
26337
26338     Roo.data.Tree.superclass.constructor.call(this);
26339 };
26340
26341 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26342     pathSeparator: "/",
26343
26344     proxyNodeEvent : function(){
26345         return this.fireEvent.apply(this, arguments);
26346     },
26347
26348     /**
26349      * Returns the root node for this tree.
26350      * @return {Node}
26351      */
26352     getRootNode : function(){
26353         return this.root;
26354     },
26355
26356     /**
26357      * Sets the root node for this tree.
26358      * @param {Node} node
26359      * @return {Node}
26360      */
26361     setRootNode : function(node){
26362         this.root = node;
26363         node.ownerTree = this;
26364         node.isRoot = true;
26365         this.registerNode(node);
26366         return node;
26367     },
26368
26369     /**
26370      * Gets a node in this tree by its id.
26371      * @param {String} id
26372      * @return {Node}
26373      */
26374     getNodeById : function(id){
26375         return this.nodeHash[id];
26376     },
26377
26378     registerNode : function(node){
26379         this.nodeHash[node.id] = node;
26380     },
26381
26382     unregisterNode : function(node){
26383         delete this.nodeHash[node.id];
26384     },
26385
26386     toString : function(){
26387         return "[Tree"+(this.id?" "+this.id:"")+"]";
26388     }
26389 });
26390
26391 /**
26392  * @class Roo.data.Node
26393  * @extends Roo.util.Observable
26394  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26395  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26396  * @constructor
26397  * @param {Object} attributes The attributes/config for the node
26398  */
26399 Roo.data.Node = function(attributes){
26400     /**
26401      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26402      * @type {Object}
26403      */
26404     this.attributes = attributes || {};
26405     this.leaf = this.attributes.leaf;
26406     /**
26407      * The node id. @type String
26408      */
26409     this.id = this.attributes.id;
26410     if(!this.id){
26411         this.id = Roo.id(null, "ynode-");
26412         this.attributes.id = this.id;
26413     }
26414      
26415     
26416     /**
26417      * All child nodes of this node. @type Array
26418      */
26419     this.childNodes = [];
26420     if(!this.childNodes.indexOf){ // indexOf is a must
26421         this.childNodes.indexOf = function(o){
26422             for(var i = 0, len = this.length; i < len; i++){
26423                 if(this[i] == o) {
26424                     return i;
26425                 }
26426             }
26427             return -1;
26428         };
26429     }
26430     /**
26431      * The parent node for this node. @type Node
26432      */
26433     this.parentNode = null;
26434     /**
26435      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26436      */
26437     this.firstChild = null;
26438     /**
26439      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26440      */
26441     this.lastChild = null;
26442     /**
26443      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26444      */
26445     this.previousSibling = null;
26446     /**
26447      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26448      */
26449     this.nextSibling = null;
26450
26451     this.addEvents({
26452        /**
26453         * @event append
26454         * Fires when a new child node is appended
26455         * @param {Tree} tree The owner tree
26456         * @param {Node} this This node
26457         * @param {Node} node The newly appended node
26458         * @param {Number} index The index of the newly appended node
26459         */
26460        "append" : true,
26461        /**
26462         * @event remove
26463         * Fires when a child node is removed
26464         * @param {Tree} tree The owner tree
26465         * @param {Node} this This node
26466         * @param {Node} node The removed node
26467         */
26468        "remove" : true,
26469        /**
26470         * @event move
26471         * Fires when this node is moved to a new location in the tree
26472         * @param {Tree} tree The owner tree
26473         * @param {Node} this This node
26474         * @param {Node} oldParent The old parent of this node
26475         * @param {Node} newParent The new parent of this node
26476         * @param {Number} index The index it was moved to
26477         */
26478        "move" : true,
26479        /**
26480         * @event insert
26481         * Fires when a new child node is inserted.
26482         * @param {Tree} tree The owner tree
26483         * @param {Node} this This node
26484         * @param {Node} node The child node inserted
26485         * @param {Node} refNode The child node the node was inserted before
26486         */
26487        "insert" : true,
26488        /**
26489         * @event beforeappend
26490         * Fires before a new child is appended, return false to cancel the append.
26491         * @param {Tree} tree The owner tree
26492         * @param {Node} this This node
26493         * @param {Node} node The child node to be appended
26494         */
26495        "beforeappend" : true,
26496        /**
26497         * @event beforeremove
26498         * Fires before a child is removed, return false to cancel the remove.
26499         * @param {Tree} tree The owner tree
26500         * @param {Node} this This node
26501         * @param {Node} node The child node to be removed
26502         */
26503        "beforeremove" : true,
26504        /**
26505         * @event beforemove
26506         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26507         * @param {Tree} tree The owner tree
26508         * @param {Node} this This node
26509         * @param {Node} oldParent The parent of this node
26510         * @param {Node} newParent The new parent this node is moving to
26511         * @param {Number} index The index it is being moved to
26512         */
26513        "beforemove" : true,
26514        /**
26515         * @event beforeinsert
26516         * Fires before a new child is inserted, return false to cancel the insert.
26517         * @param {Tree} tree The owner tree
26518         * @param {Node} this This node
26519         * @param {Node} node The child node to be inserted
26520         * @param {Node} refNode The child node the node is being inserted before
26521         */
26522        "beforeinsert" : true
26523    });
26524     this.listeners = this.attributes.listeners;
26525     Roo.data.Node.superclass.constructor.call(this);
26526 };
26527
26528 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26529     fireEvent : function(evtName){
26530         // first do standard event for this node
26531         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26532             return false;
26533         }
26534         // then bubble it up to the tree if the event wasn't cancelled
26535         var ot = this.getOwnerTree();
26536         if(ot){
26537             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26538                 return false;
26539             }
26540         }
26541         return true;
26542     },
26543
26544     /**
26545      * Returns true if this node is a leaf
26546      * @return {Boolean}
26547      */
26548     isLeaf : function(){
26549         return this.leaf === true;
26550     },
26551
26552     // private
26553     setFirstChild : function(node){
26554         this.firstChild = node;
26555     },
26556
26557     //private
26558     setLastChild : function(node){
26559         this.lastChild = node;
26560     },
26561
26562
26563     /**
26564      * Returns true if this node is the last child of its parent
26565      * @return {Boolean}
26566      */
26567     isLast : function(){
26568        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26569     },
26570
26571     /**
26572      * Returns true if this node is the first child of its parent
26573      * @return {Boolean}
26574      */
26575     isFirst : function(){
26576        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26577     },
26578
26579     hasChildNodes : function(){
26580         return !this.isLeaf() && this.childNodes.length > 0;
26581     },
26582
26583     /**
26584      * Insert node(s) as the last child node of this node.
26585      * @param {Node/Array} node The node or Array of nodes to append
26586      * @return {Node} The appended node if single append, or null if an array was passed
26587      */
26588     appendChild : function(node){
26589         var multi = false;
26590         if(node instanceof Array){
26591             multi = node;
26592         }else if(arguments.length > 1){
26593             multi = arguments;
26594         }
26595         
26596         // if passed an array or multiple args do them one by one
26597         if(multi){
26598             for(var i = 0, len = multi.length; i < len; i++) {
26599                 this.appendChild(multi[i]);
26600             }
26601         }else{
26602             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26603                 return false;
26604             }
26605             var index = this.childNodes.length;
26606             var oldParent = node.parentNode;
26607             // it's a move, make sure we move it cleanly
26608             if(oldParent){
26609                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26610                     return false;
26611                 }
26612                 oldParent.removeChild(node);
26613             }
26614             
26615             index = this.childNodes.length;
26616             if(index == 0){
26617                 this.setFirstChild(node);
26618             }
26619             this.childNodes.push(node);
26620             node.parentNode = this;
26621             var ps = this.childNodes[index-1];
26622             if(ps){
26623                 node.previousSibling = ps;
26624                 ps.nextSibling = node;
26625             }else{
26626                 node.previousSibling = null;
26627             }
26628             node.nextSibling = null;
26629             this.setLastChild(node);
26630             node.setOwnerTree(this.getOwnerTree());
26631             this.fireEvent("append", this.ownerTree, this, node, index);
26632             if(this.ownerTree) {
26633                 this.ownerTree.fireEvent("appendnode", this, node, index);
26634             }
26635             if(oldParent){
26636                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26637             }
26638             return node;
26639         }
26640     },
26641
26642     /**
26643      * Removes a child node from this node.
26644      * @param {Node} node The node to remove
26645      * @return {Node} The removed node
26646      */
26647     removeChild : function(node){
26648         var index = this.childNodes.indexOf(node);
26649         if(index == -1){
26650             return false;
26651         }
26652         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26653             return false;
26654         }
26655
26656         // remove it from childNodes collection
26657         this.childNodes.splice(index, 1);
26658
26659         // update siblings
26660         if(node.previousSibling){
26661             node.previousSibling.nextSibling = node.nextSibling;
26662         }
26663         if(node.nextSibling){
26664             node.nextSibling.previousSibling = node.previousSibling;
26665         }
26666
26667         // update child refs
26668         if(this.firstChild == node){
26669             this.setFirstChild(node.nextSibling);
26670         }
26671         if(this.lastChild == node){
26672             this.setLastChild(node.previousSibling);
26673         }
26674
26675         node.setOwnerTree(null);
26676         // clear any references from the node
26677         node.parentNode = null;
26678         node.previousSibling = null;
26679         node.nextSibling = null;
26680         this.fireEvent("remove", this.ownerTree, this, node);
26681         return node;
26682     },
26683
26684     /**
26685      * Inserts the first node before the second node in this nodes childNodes collection.
26686      * @param {Node} node The node to insert
26687      * @param {Node} refNode The node to insert before (if null the node is appended)
26688      * @return {Node} The inserted node
26689      */
26690     insertBefore : function(node, refNode){
26691         if(!refNode){ // like standard Dom, refNode can be null for append
26692             return this.appendChild(node);
26693         }
26694         // nothing to do
26695         if(node == refNode){
26696             return false;
26697         }
26698
26699         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26700             return false;
26701         }
26702         var index = this.childNodes.indexOf(refNode);
26703         var oldParent = node.parentNode;
26704         var refIndex = index;
26705
26706         // when moving internally, indexes will change after remove
26707         if(oldParent == this && this.childNodes.indexOf(node) < index){
26708             refIndex--;
26709         }
26710
26711         // it's a move, make sure we move it cleanly
26712         if(oldParent){
26713             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26714                 return false;
26715             }
26716             oldParent.removeChild(node);
26717         }
26718         if(refIndex == 0){
26719             this.setFirstChild(node);
26720         }
26721         this.childNodes.splice(refIndex, 0, node);
26722         node.parentNode = this;
26723         var ps = this.childNodes[refIndex-1];
26724         if(ps){
26725             node.previousSibling = ps;
26726             ps.nextSibling = node;
26727         }else{
26728             node.previousSibling = null;
26729         }
26730         node.nextSibling = refNode;
26731         refNode.previousSibling = node;
26732         node.setOwnerTree(this.getOwnerTree());
26733         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26734         if(oldParent){
26735             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26736         }
26737         return node;
26738     },
26739
26740     /**
26741      * Returns the child node at the specified index.
26742      * @param {Number} index
26743      * @return {Node}
26744      */
26745     item : function(index){
26746         return this.childNodes[index];
26747     },
26748
26749     /**
26750      * Replaces one child node in this node with another.
26751      * @param {Node} newChild The replacement node
26752      * @param {Node} oldChild The node to replace
26753      * @return {Node} The replaced node
26754      */
26755     replaceChild : function(newChild, oldChild){
26756         this.insertBefore(newChild, oldChild);
26757         this.removeChild(oldChild);
26758         return oldChild;
26759     },
26760
26761     /**
26762      * Returns the index of a child node
26763      * @param {Node} node
26764      * @return {Number} The index of the node or -1 if it was not found
26765      */
26766     indexOf : function(child){
26767         return this.childNodes.indexOf(child);
26768     },
26769
26770     /**
26771      * Returns the tree this node is in.
26772      * @return {Tree}
26773      */
26774     getOwnerTree : function(){
26775         // if it doesn't have one, look for one
26776         if(!this.ownerTree){
26777             var p = this;
26778             while(p){
26779                 if(p.ownerTree){
26780                     this.ownerTree = p.ownerTree;
26781                     break;
26782                 }
26783                 p = p.parentNode;
26784             }
26785         }
26786         return this.ownerTree;
26787     },
26788
26789     /**
26790      * Returns depth of this node (the root node has a depth of 0)
26791      * @return {Number}
26792      */
26793     getDepth : function(){
26794         var depth = 0;
26795         var p = this;
26796         while(p.parentNode){
26797             ++depth;
26798             p = p.parentNode;
26799         }
26800         return depth;
26801     },
26802
26803     // private
26804     setOwnerTree : function(tree){
26805         // if it's move, we need to update everyone
26806         if(tree != this.ownerTree){
26807             if(this.ownerTree){
26808                 this.ownerTree.unregisterNode(this);
26809             }
26810             this.ownerTree = tree;
26811             var cs = this.childNodes;
26812             for(var i = 0, len = cs.length; i < len; i++) {
26813                 cs[i].setOwnerTree(tree);
26814             }
26815             if(tree){
26816                 tree.registerNode(this);
26817             }
26818         }
26819     },
26820
26821     /**
26822      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26823      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26824      * @return {String} The path
26825      */
26826     getPath : function(attr){
26827         attr = attr || "id";
26828         var p = this.parentNode;
26829         var b = [this.attributes[attr]];
26830         while(p){
26831             b.unshift(p.attributes[attr]);
26832             p = p.parentNode;
26833         }
26834         var sep = this.getOwnerTree().pathSeparator;
26835         return sep + b.join(sep);
26836     },
26837
26838     /**
26839      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26840      * function call will be the scope provided or the current node. The arguments to the function
26841      * will be the args provided or the current node. If the function returns false at any point,
26842      * the bubble is stopped.
26843      * @param {Function} fn The function to call
26844      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26845      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26846      */
26847     bubble : function(fn, scope, args){
26848         var p = this;
26849         while(p){
26850             if(fn.call(scope || p, args || p) === false){
26851                 break;
26852             }
26853             p = p.parentNode;
26854         }
26855     },
26856
26857     /**
26858      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26859      * function call will be the scope provided or the current node. The arguments to the function
26860      * will be the args provided or the current node. If the function returns false at any point,
26861      * the cascade is stopped on that branch.
26862      * @param {Function} fn The function to call
26863      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26864      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26865      */
26866     cascade : function(fn, scope, args){
26867         if(fn.call(scope || this, args || this) !== false){
26868             var cs = this.childNodes;
26869             for(var i = 0, len = cs.length; i < len; i++) {
26870                 cs[i].cascade(fn, scope, args);
26871             }
26872         }
26873     },
26874
26875     /**
26876      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26877      * function call will be the scope provided or the current node. The arguments to the function
26878      * will be the args provided or the current node. If the function returns false at any point,
26879      * the iteration stops.
26880      * @param {Function} fn The function to call
26881      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26882      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26883      */
26884     eachChild : function(fn, scope, args){
26885         var cs = this.childNodes;
26886         for(var i = 0, len = cs.length; i < len; i++) {
26887                 if(fn.call(scope || this, args || cs[i]) === false){
26888                     break;
26889                 }
26890         }
26891     },
26892
26893     /**
26894      * Finds the first child that has the attribute with the specified value.
26895      * @param {String} attribute The attribute name
26896      * @param {Mixed} value The value to search for
26897      * @return {Node} The found child or null if none was found
26898      */
26899     findChild : function(attribute, value){
26900         var cs = this.childNodes;
26901         for(var i = 0, len = cs.length; i < len; i++) {
26902                 if(cs[i].attributes[attribute] == value){
26903                     return cs[i];
26904                 }
26905         }
26906         return null;
26907     },
26908
26909     /**
26910      * Finds the first child by a custom function. The child matches if the function passed
26911      * returns true.
26912      * @param {Function} fn
26913      * @param {Object} scope (optional)
26914      * @return {Node} The found child or null if none was found
26915      */
26916     findChildBy : function(fn, scope){
26917         var cs = this.childNodes;
26918         for(var i = 0, len = cs.length; i < len; i++) {
26919                 if(fn.call(scope||cs[i], cs[i]) === true){
26920                     return cs[i];
26921                 }
26922         }
26923         return null;
26924     },
26925
26926     /**
26927      * Sorts this nodes children using the supplied sort function
26928      * @param {Function} fn
26929      * @param {Object} scope (optional)
26930      */
26931     sort : function(fn, scope){
26932         var cs = this.childNodes;
26933         var len = cs.length;
26934         if(len > 0){
26935             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26936             cs.sort(sortFn);
26937             for(var i = 0; i < len; i++){
26938                 var n = cs[i];
26939                 n.previousSibling = cs[i-1];
26940                 n.nextSibling = cs[i+1];
26941                 if(i == 0){
26942                     this.setFirstChild(n);
26943                 }
26944                 if(i == len-1){
26945                     this.setLastChild(n);
26946                 }
26947             }
26948         }
26949     },
26950
26951     /**
26952      * Returns true if this node is an ancestor (at any point) of the passed node.
26953      * @param {Node} node
26954      * @return {Boolean}
26955      */
26956     contains : function(node){
26957         return node.isAncestor(this);
26958     },
26959
26960     /**
26961      * Returns true if the passed node is an ancestor (at any point) of this node.
26962      * @param {Node} node
26963      * @return {Boolean}
26964      */
26965     isAncestor : function(node){
26966         var p = this.parentNode;
26967         while(p){
26968             if(p == node){
26969                 return true;
26970             }
26971             p = p.parentNode;
26972         }
26973         return false;
26974     },
26975
26976     toString : function(){
26977         return "[Node"+(this.id?" "+this.id:"")+"]";
26978     }
26979 });/*
26980  * Based on:
26981  * Ext JS Library 1.1.1
26982  * Copyright(c) 2006-2007, Ext JS, LLC.
26983  *
26984  * Originally Released Under LGPL - original licence link has changed is not relivant.
26985  *
26986  * Fork - LGPL
26987  * <script type="text/javascript">
26988  */
26989
26990
26991 /**
26992  * @class Roo.Shadow
26993  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26994  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26995  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26996  * @constructor
26997  * Create a new Shadow
26998  * @param {Object} config The config object
26999  */
27000 Roo.Shadow = function(config){
27001     Roo.apply(this, config);
27002     if(typeof this.mode != "string"){
27003         this.mode = this.defaultMode;
27004     }
27005     var o = this.offset, a = {h: 0};
27006     var rad = Math.floor(this.offset/2);
27007     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27008         case "drop":
27009             a.w = 0;
27010             a.l = a.t = o;
27011             a.t -= 1;
27012             if(Roo.isIE){
27013                 a.l -= this.offset + rad;
27014                 a.t -= this.offset + rad;
27015                 a.w -= rad;
27016                 a.h -= rad;
27017                 a.t += 1;
27018             }
27019         break;
27020         case "sides":
27021             a.w = (o*2);
27022             a.l = -o;
27023             a.t = o-1;
27024             if(Roo.isIE){
27025                 a.l -= (this.offset - rad);
27026                 a.t -= this.offset + rad;
27027                 a.l += 1;
27028                 a.w -= (this.offset - rad)*2;
27029                 a.w -= rad + 1;
27030                 a.h -= 1;
27031             }
27032         break;
27033         case "frame":
27034             a.w = a.h = (o*2);
27035             a.l = a.t = -o;
27036             a.t += 1;
27037             a.h -= 2;
27038             if(Roo.isIE){
27039                 a.l -= (this.offset - rad);
27040                 a.t -= (this.offset - rad);
27041                 a.l += 1;
27042                 a.w -= (this.offset + rad + 1);
27043                 a.h -= (this.offset + rad);
27044                 a.h += 1;
27045             }
27046         break;
27047     };
27048
27049     this.adjusts = a;
27050 };
27051
27052 Roo.Shadow.prototype = {
27053     /**
27054      * @cfg {String} mode
27055      * The shadow display mode.  Supports the following options:<br />
27056      * sides: Shadow displays on both sides and bottom only<br />
27057      * frame: Shadow displays equally on all four sides<br />
27058      * drop: Traditional bottom-right drop shadow (default)
27059      */
27060     mode: false,
27061     /**
27062      * @cfg {String} offset
27063      * The number of pixels to offset the shadow from the element (defaults to 4)
27064      */
27065     offset: 4,
27066
27067     // private
27068     defaultMode: "drop",
27069
27070     /**
27071      * Displays the shadow under the target element
27072      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27073      */
27074     show : function(target){
27075         target = Roo.get(target);
27076         if(!this.el){
27077             this.el = Roo.Shadow.Pool.pull();
27078             if(this.el.dom.nextSibling != target.dom){
27079                 this.el.insertBefore(target);
27080             }
27081         }
27082         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27083         if(Roo.isIE){
27084             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27085         }
27086         this.realign(
27087             target.getLeft(true),
27088             target.getTop(true),
27089             target.getWidth(),
27090             target.getHeight()
27091         );
27092         this.el.dom.style.display = "block";
27093     },
27094
27095     /**
27096      * Returns true if the shadow is visible, else false
27097      */
27098     isVisible : function(){
27099         return this.el ? true : false;  
27100     },
27101
27102     /**
27103      * Direct alignment when values are already available. Show must be called at least once before
27104      * calling this method to ensure it is initialized.
27105      * @param {Number} left The target element left position
27106      * @param {Number} top The target element top position
27107      * @param {Number} width The target element width
27108      * @param {Number} height The target element height
27109      */
27110     realign : function(l, t, w, h){
27111         if(!this.el){
27112             return;
27113         }
27114         var a = this.adjusts, d = this.el.dom, s = d.style;
27115         var iea = 0;
27116         s.left = (l+a.l)+"px";
27117         s.top = (t+a.t)+"px";
27118         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27119  
27120         if(s.width != sws || s.height != shs){
27121             s.width = sws;
27122             s.height = shs;
27123             if(!Roo.isIE){
27124                 var cn = d.childNodes;
27125                 var sww = Math.max(0, (sw-12))+"px";
27126                 cn[0].childNodes[1].style.width = sww;
27127                 cn[1].childNodes[1].style.width = sww;
27128                 cn[2].childNodes[1].style.width = sww;
27129                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27130             }
27131         }
27132     },
27133
27134     /**
27135      * Hides this shadow
27136      */
27137     hide : function(){
27138         if(this.el){
27139             this.el.dom.style.display = "none";
27140             Roo.Shadow.Pool.push(this.el);
27141             delete this.el;
27142         }
27143     },
27144
27145     /**
27146      * Adjust the z-index of this shadow
27147      * @param {Number} zindex The new z-index
27148      */
27149     setZIndex : function(z){
27150         this.zIndex = z;
27151         if(this.el){
27152             this.el.setStyle("z-index", z);
27153         }
27154     }
27155 };
27156
27157 // Private utility class that manages the internal Shadow cache
27158 Roo.Shadow.Pool = function(){
27159     var p = [];
27160     var markup = Roo.isIE ?
27161                  '<div class="x-ie-shadow"></div>' :
27162                  '<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>';
27163     return {
27164         pull : function(){
27165             var sh = p.shift();
27166             if(!sh){
27167                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27168                 sh.autoBoxAdjust = false;
27169             }
27170             return sh;
27171         },
27172
27173         push : function(sh){
27174             p.push(sh);
27175         }
27176     };
27177 }();/*
27178  * Based on:
27179  * Ext JS Library 1.1.1
27180  * Copyright(c) 2006-2007, Ext JS, LLC.
27181  *
27182  * Originally Released Under LGPL - original licence link has changed is not relivant.
27183  *
27184  * Fork - LGPL
27185  * <script type="text/javascript">
27186  */
27187
27188
27189 /**
27190  * @class Roo.SplitBar
27191  * @extends Roo.util.Observable
27192  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27193  * <br><br>
27194  * Usage:
27195  * <pre><code>
27196 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27197                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27198 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27199 split.minSize = 100;
27200 split.maxSize = 600;
27201 split.animate = true;
27202 split.on('moved', splitterMoved);
27203 </code></pre>
27204  * @constructor
27205  * Create a new SplitBar
27206  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27207  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27208  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27209  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27210                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27211                         position of the SplitBar).
27212  */
27213 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27214     
27215     /** @private */
27216     this.el = Roo.get(dragElement, true);
27217     this.el.dom.unselectable = "on";
27218     /** @private */
27219     this.resizingEl = Roo.get(resizingElement, true);
27220
27221     /**
27222      * @private
27223      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27224      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27225      * @type Number
27226      */
27227     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27228     
27229     /**
27230      * The minimum size of the resizing element. (Defaults to 0)
27231      * @type Number
27232      */
27233     this.minSize = 0;
27234     
27235     /**
27236      * The maximum size of the resizing element. (Defaults to 2000)
27237      * @type Number
27238      */
27239     this.maxSize = 2000;
27240     
27241     /**
27242      * Whether to animate the transition to the new size
27243      * @type Boolean
27244      */
27245     this.animate = false;
27246     
27247     /**
27248      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27249      * @type Boolean
27250      */
27251     this.useShim = false;
27252     
27253     /** @private */
27254     this.shim = null;
27255     
27256     if(!existingProxy){
27257         /** @private */
27258         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27259     }else{
27260         this.proxy = Roo.get(existingProxy).dom;
27261     }
27262     /** @private */
27263     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27264     
27265     /** @private */
27266     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27267     
27268     /** @private */
27269     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27270     
27271     /** @private */
27272     this.dragSpecs = {};
27273     
27274     /**
27275      * @private The adapter to use to positon and resize elements
27276      */
27277     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27278     this.adapter.init(this);
27279     
27280     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27281         /** @private */
27282         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27283         this.el.addClass("x-splitbar-h");
27284     }else{
27285         /** @private */
27286         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27287         this.el.addClass("x-splitbar-v");
27288     }
27289     
27290     this.addEvents({
27291         /**
27292          * @event resize
27293          * Fires when the splitter is moved (alias for {@link #event-moved})
27294          * @param {Roo.SplitBar} this
27295          * @param {Number} newSize the new width or height
27296          */
27297         "resize" : true,
27298         /**
27299          * @event moved
27300          * Fires when the splitter is moved
27301          * @param {Roo.SplitBar} this
27302          * @param {Number} newSize the new width or height
27303          */
27304         "moved" : true,
27305         /**
27306          * @event beforeresize
27307          * Fires before the splitter is dragged
27308          * @param {Roo.SplitBar} this
27309          */
27310         "beforeresize" : true,
27311
27312         "beforeapply" : true
27313     });
27314
27315     Roo.util.Observable.call(this);
27316 };
27317
27318 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27319     onStartProxyDrag : function(x, y){
27320         this.fireEvent("beforeresize", this);
27321         if(!this.overlay){
27322             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27323             o.unselectable();
27324             o.enableDisplayMode("block");
27325             // all splitbars share the same overlay
27326             Roo.SplitBar.prototype.overlay = o;
27327         }
27328         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27329         this.overlay.show();
27330         Roo.get(this.proxy).setDisplayed("block");
27331         var size = this.adapter.getElementSize(this);
27332         this.activeMinSize = this.getMinimumSize();;
27333         this.activeMaxSize = this.getMaximumSize();;
27334         var c1 = size - this.activeMinSize;
27335         var c2 = Math.max(this.activeMaxSize - size, 0);
27336         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27337             this.dd.resetConstraints();
27338             this.dd.setXConstraint(
27339                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27340                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27341             );
27342             this.dd.setYConstraint(0, 0);
27343         }else{
27344             this.dd.resetConstraints();
27345             this.dd.setXConstraint(0, 0);
27346             this.dd.setYConstraint(
27347                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27348                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27349             );
27350          }
27351         this.dragSpecs.startSize = size;
27352         this.dragSpecs.startPoint = [x, y];
27353         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27354     },
27355     
27356     /** 
27357      * @private Called after the drag operation by the DDProxy
27358      */
27359     onEndProxyDrag : function(e){
27360         Roo.get(this.proxy).setDisplayed(false);
27361         var endPoint = Roo.lib.Event.getXY(e);
27362         if(this.overlay){
27363             this.overlay.hide();
27364         }
27365         var newSize;
27366         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27367             newSize = this.dragSpecs.startSize + 
27368                 (this.placement == Roo.SplitBar.LEFT ?
27369                     endPoint[0] - this.dragSpecs.startPoint[0] :
27370                     this.dragSpecs.startPoint[0] - endPoint[0]
27371                 );
27372         }else{
27373             newSize = this.dragSpecs.startSize + 
27374                 (this.placement == Roo.SplitBar.TOP ?
27375                     endPoint[1] - this.dragSpecs.startPoint[1] :
27376                     this.dragSpecs.startPoint[1] - endPoint[1]
27377                 );
27378         }
27379         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27380         if(newSize != this.dragSpecs.startSize){
27381             if(this.fireEvent('beforeapply', this, newSize) !== false){
27382                 this.adapter.setElementSize(this, newSize);
27383                 this.fireEvent("moved", this, newSize);
27384                 this.fireEvent("resize", this, newSize);
27385             }
27386         }
27387     },
27388     
27389     /**
27390      * Get the adapter this SplitBar uses
27391      * @return The adapter object
27392      */
27393     getAdapter : function(){
27394         return this.adapter;
27395     },
27396     
27397     /**
27398      * Set the adapter this SplitBar uses
27399      * @param {Object} adapter A SplitBar adapter object
27400      */
27401     setAdapter : function(adapter){
27402         this.adapter = adapter;
27403         this.adapter.init(this);
27404     },
27405     
27406     /**
27407      * Gets the minimum size for the resizing element
27408      * @return {Number} The minimum size
27409      */
27410     getMinimumSize : function(){
27411         return this.minSize;
27412     },
27413     
27414     /**
27415      * Sets the minimum size for the resizing element
27416      * @param {Number} minSize The minimum size
27417      */
27418     setMinimumSize : function(minSize){
27419         this.minSize = minSize;
27420     },
27421     
27422     /**
27423      * Gets the maximum size for the resizing element
27424      * @return {Number} The maximum size
27425      */
27426     getMaximumSize : function(){
27427         return this.maxSize;
27428     },
27429     
27430     /**
27431      * Sets the maximum size for the resizing element
27432      * @param {Number} maxSize The maximum size
27433      */
27434     setMaximumSize : function(maxSize){
27435         this.maxSize = maxSize;
27436     },
27437     
27438     /**
27439      * Sets the initialize size for the resizing element
27440      * @param {Number} size The initial size
27441      */
27442     setCurrentSize : function(size){
27443         var oldAnimate = this.animate;
27444         this.animate = false;
27445         this.adapter.setElementSize(this, size);
27446         this.animate = oldAnimate;
27447     },
27448     
27449     /**
27450      * Destroy this splitbar. 
27451      * @param {Boolean} removeEl True to remove the element
27452      */
27453     destroy : function(removeEl){
27454         if(this.shim){
27455             this.shim.remove();
27456         }
27457         this.dd.unreg();
27458         this.proxy.parentNode.removeChild(this.proxy);
27459         if(removeEl){
27460             this.el.remove();
27461         }
27462     }
27463 });
27464
27465 /**
27466  * @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.
27467  */
27468 Roo.SplitBar.createProxy = function(dir){
27469     var proxy = new Roo.Element(document.createElement("div"));
27470     proxy.unselectable();
27471     var cls = 'x-splitbar-proxy';
27472     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27473     document.body.appendChild(proxy.dom);
27474     return proxy.dom;
27475 };
27476
27477 /** 
27478  * @class Roo.SplitBar.BasicLayoutAdapter
27479  * Default Adapter. It assumes the splitter and resizing element are not positioned
27480  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27481  */
27482 Roo.SplitBar.BasicLayoutAdapter = function(){
27483 };
27484
27485 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27486     // do nothing for now
27487     init : function(s){
27488     
27489     },
27490     /**
27491      * Called before drag operations to get the current size of the resizing element. 
27492      * @param {Roo.SplitBar} s The SplitBar using this adapter
27493      */
27494      getElementSize : function(s){
27495         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27496             return s.resizingEl.getWidth();
27497         }else{
27498             return s.resizingEl.getHeight();
27499         }
27500     },
27501     
27502     /**
27503      * Called after drag operations to set the size of the resizing element.
27504      * @param {Roo.SplitBar} s The SplitBar using this adapter
27505      * @param {Number} newSize The new size to set
27506      * @param {Function} onComplete A function to be invoked when resizing is complete
27507      */
27508     setElementSize : function(s, newSize, onComplete){
27509         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27510             if(!s.animate){
27511                 s.resizingEl.setWidth(newSize);
27512                 if(onComplete){
27513                     onComplete(s, newSize);
27514                 }
27515             }else{
27516                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27517             }
27518         }else{
27519             
27520             if(!s.animate){
27521                 s.resizingEl.setHeight(newSize);
27522                 if(onComplete){
27523                     onComplete(s, newSize);
27524                 }
27525             }else{
27526                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27527             }
27528         }
27529     }
27530 };
27531
27532 /** 
27533  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27534  * @extends Roo.SplitBar.BasicLayoutAdapter
27535  * Adapter that  moves the splitter element to align with the resized sizing element. 
27536  * Used with an absolute positioned SplitBar.
27537  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27538  * document.body, make sure you assign an id to the body element.
27539  */
27540 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27541     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27542     this.container = Roo.get(container);
27543 };
27544
27545 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27546     init : function(s){
27547         this.basic.init(s);
27548     },
27549     
27550     getElementSize : function(s){
27551         return this.basic.getElementSize(s);
27552     },
27553     
27554     setElementSize : function(s, newSize, onComplete){
27555         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27556     },
27557     
27558     moveSplitter : function(s){
27559         var yes = Roo.SplitBar;
27560         switch(s.placement){
27561             case yes.LEFT:
27562                 s.el.setX(s.resizingEl.getRight());
27563                 break;
27564             case yes.RIGHT:
27565                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27566                 break;
27567             case yes.TOP:
27568                 s.el.setY(s.resizingEl.getBottom());
27569                 break;
27570             case yes.BOTTOM:
27571                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27572                 break;
27573         }
27574     }
27575 };
27576
27577 /**
27578  * Orientation constant - Create a vertical SplitBar
27579  * @static
27580  * @type Number
27581  */
27582 Roo.SplitBar.VERTICAL = 1;
27583
27584 /**
27585  * Orientation constant - Create a horizontal SplitBar
27586  * @static
27587  * @type Number
27588  */
27589 Roo.SplitBar.HORIZONTAL = 2;
27590
27591 /**
27592  * Placement constant - The resizing element is to the left of the splitter element
27593  * @static
27594  * @type Number
27595  */
27596 Roo.SplitBar.LEFT = 1;
27597
27598 /**
27599  * Placement constant - The resizing element is to the right of the splitter element
27600  * @static
27601  * @type Number
27602  */
27603 Roo.SplitBar.RIGHT = 2;
27604
27605 /**
27606  * Placement constant - The resizing element is positioned above the splitter element
27607  * @static
27608  * @type Number
27609  */
27610 Roo.SplitBar.TOP = 3;
27611
27612 /**
27613  * Placement constant - The resizing element is positioned under splitter element
27614  * @static
27615  * @type Number
27616  */
27617 Roo.SplitBar.BOTTOM = 4;
27618 /*
27619  * Based on:
27620  * Ext JS Library 1.1.1
27621  * Copyright(c) 2006-2007, Ext JS, LLC.
27622  *
27623  * Originally Released Under LGPL - original licence link has changed is not relivant.
27624  *
27625  * Fork - LGPL
27626  * <script type="text/javascript">
27627  */
27628
27629 /**
27630  * @class Roo.View
27631  * @extends Roo.util.Observable
27632  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27633  * This class also supports single and multi selection modes. <br>
27634  * Create a data model bound view:
27635  <pre><code>
27636  var store = new Roo.data.Store(...);
27637
27638  var view = new Roo.View({
27639     el : "my-element",
27640     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27641  
27642     singleSelect: true,
27643     selectedClass: "ydataview-selected",
27644     store: store
27645  });
27646
27647  // listen for node click?
27648  view.on("click", function(vw, index, node, e){
27649  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27650  });
27651
27652  // load XML data
27653  dataModel.load("foobar.xml");
27654  </code></pre>
27655  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27656  * <br><br>
27657  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27658  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27659  * 
27660  * Note: old style constructor is still suported (container, template, config)
27661  * 
27662  * @constructor
27663  * Create a new View
27664  * @param {Object} config The config object
27665  * 
27666  */
27667 Roo.View = function(config, depreciated_tpl, depreciated_config){
27668     
27669     this.parent = false;
27670     
27671     if (typeof(depreciated_tpl) == 'undefined') {
27672         // new way.. - universal constructor.
27673         Roo.apply(this, config);
27674         this.el  = Roo.get(this.el);
27675     } else {
27676         // old format..
27677         this.el  = Roo.get(config);
27678         this.tpl = depreciated_tpl;
27679         Roo.apply(this, depreciated_config);
27680     }
27681     this.wrapEl  = this.el.wrap().wrap();
27682     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27683     
27684     
27685     if(typeof(this.tpl) == "string"){
27686         this.tpl = new Roo.Template(this.tpl);
27687     } else {
27688         // support xtype ctors..
27689         this.tpl = new Roo.factory(this.tpl, Roo);
27690     }
27691     
27692     
27693     this.tpl.compile();
27694     
27695     /** @private */
27696     this.addEvents({
27697         /**
27698          * @event beforeclick
27699          * Fires before a click is processed. Returns false to cancel the default action.
27700          * @param {Roo.View} this
27701          * @param {Number} index The index of the target node
27702          * @param {HTMLElement} node The target node
27703          * @param {Roo.EventObject} e The raw event object
27704          */
27705             "beforeclick" : true,
27706         /**
27707          * @event click
27708          * Fires when a template node is clicked.
27709          * @param {Roo.View} this
27710          * @param {Number} index The index of the target node
27711          * @param {HTMLElement} node The target node
27712          * @param {Roo.EventObject} e The raw event object
27713          */
27714             "click" : true,
27715         /**
27716          * @event dblclick
27717          * Fires when a template node is double clicked.
27718          * @param {Roo.View} this
27719          * @param {Number} index The index of the target node
27720          * @param {HTMLElement} node The target node
27721          * @param {Roo.EventObject} e The raw event object
27722          */
27723             "dblclick" : true,
27724         /**
27725          * @event contextmenu
27726          * Fires when a template node is right clicked.
27727          * @param {Roo.View} this
27728          * @param {Number} index The index of the target node
27729          * @param {HTMLElement} node The target node
27730          * @param {Roo.EventObject} e The raw event object
27731          */
27732             "contextmenu" : true,
27733         /**
27734          * @event selectionchange
27735          * Fires when the selected nodes change.
27736          * @param {Roo.View} this
27737          * @param {Array} selections Array of the selected nodes
27738          */
27739             "selectionchange" : true,
27740     
27741         /**
27742          * @event beforeselect
27743          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27744          * @param {Roo.View} this
27745          * @param {HTMLElement} node The node to be selected
27746          * @param {Array} selections Array of currently selected nodes
27747          */
27748             "beforeselect" : true,
27749         /**
27750          * @event preparedata
27751          * Fires on every row to render, to allow you to change the data.
27752          * @param {Roo.View} this
27753          * @param {Object} data to be rendered (change this)
27754          */
27755           "preparedata" : true
27756           
27757           
27758         });
27759
27760
27761
27762     this.el.on({
27763         "click": this.onClick,
27764         "dblclick": this.onDblClick,
27765         "contextmenu": this.onContextMenu,
27766         scope:this
27767     });
27768
27769     this.selections = [];
27770     this.nodes = [];
27771     this.cmp = new Roo.CompositeElementLite([]);
27772     if(this.store){
27773         this.store = Roo.factory(this.store, Roo.data);
27774         this.setStore(this.store, true);
27775     }
27776     
27777     if ( this.footer && this.footer.xtype) {
27778            
27779          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27780         
27781         this.footer.dataSource = this.store;
27782         this.footer.container = fctr;
27783         this.footer = Roo.factory(this.footer, Roo);
27784         fctr.insertFirst(this.el);
27785         
27786         // this is a bit insane - as the paging toolbar seems to detach the el..
27787 //        dom.parentNode.parentNode.parentNode
27788          // they get detached?
27789     }
27790     
27791     
27792     Roo.View.superclass.constructor.call(this);
27793     
27794     
27795 };
27796
27797 Roo.extend(Roo.View, Roo.util.Observable, {
27798     
27799      /**
27800      * @cfg {Roo.data.Store} store Data store to load data from.
27801      */
27802     store : false,
27803     
27804     /**
27805      * @cfg {String|Roo.Element} el The container element.
27806      */
27807     el : '',
27808     
27809     /**
27810      * @cfg {String|Roo.Template} tpl The template used by this View 
27811      */
27812     tpl : false,
27813     /**
27814      * @cfg {String} dataName the named area of the template to use as the data area
27815      *                          Works with domtemplates roo-name="name"
27816      */
27817     dataName: false,
27818     /**
27819      * @cfg {String} selectedClass The css class to add to selected nodes
27820      */
27821     selectedClass : "x-view-selected",
27822      /**
27823      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27824      */
27825     emptyText : "",
27826     
27827     /**
27828      * @cfg {String} text to display on mask (default Loading)
27829      */
27830     mask : false,
27831     /**
27832      * @cfg {Boolean} multiSelect Allow multiple selection
27833      */
27834     multiSelect : false,
27835     /**
27836      * @cfg {Boolean} singleSelect Allow single selection
27837      */
27838     singleSelect:  false,
27839     
27840     /**
27841      * @cfg {Boolean} toggleSelect - selecting 
27842      */
27843     toggleSelect : false,
27844     
27845     /**
27846      * @cfg {Boolean} tickable - selecting 
27847      */
27848     tickable : false,
27849     
27850     /**
27851      * Returns the element this view is bound to.
27852      * @return {Roo.Element}
27853      */
27854     getEl : function(){
27855         return this.wrapEl;
27856     },
27857     
27858     
27859
27860     /**
27861      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27862      */
27863     refresh : function(){
27864         //Roo.log('refresh');
27865         var t = this.tpl;
27866         
27867         // if we are using something like 'domtemplate', then
27868         // the what gets used is:
27869         // t.applySubtemplate(NAME, data, wrapping data..)
27870         // the outer template then get' applied with
27871         //     the store 'extra data'
27872         // and the body get's added to the
27873         //      roo-name="data" node?
27874         //      <span class='roo-tpl-{name}'></span> ?????
27875         
27876         
27877         
27878         this.clearSelections();
27879         this.el.update("");
27880         var html = [];
27881         var records = this.store.getRange();
27882         if(records.length < 1) {
27883             
27884             // is this valid??  = should it render a template??
27885             
27886             this.el.update(this.emptyText);
27887             return;
27888         }
27889         var el = this.el;
27890         if (this.dataName) {
27891             this.el.update(t.apply(this.store.meta)); //????
27892             el = this.el.child('.roo-tpl-' + this.dataName);
27893         }
27894         
27895         for(var i = 0, len = records.length; i < len; i++){
27896             var data = this.prepareData(records[i].data, i, records[i]);
27897             this.fireEvent("preparedata", this, data, i, records[i]);
27898             
27899             var d = Roo.apply({}, data);
27900             
27901             if(this.tickable){
27902                 Roo.apply(d, {'roo-id' : Roo.id()});
27903                 
27904                 var _this = this;
27905             
27906                 Roo.each(this.parent.item, function(item){
27907                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27908                         return;
27909                     }
27910                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27911                 });
27912             }
27913             
27914             html[html.length] = Roo.util.Format.trim(
27915                 this.dataName ?
27916                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27917                     t.apply(d)
27918             );
27919         }
27920         
27921         
27922         
27923         el.update(html.join(""));
27924         this.nodes = el.dom.childNodes;
27925         this.updateIndexes(0);
27926     },
27927     
27928
27929     /**
27930      * Function to override to reformat the data that is sent to
27931      * the template for each node.
27932      * DEPRICATED - use the preparedata event handler.
27933      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27934      * a JSON object for an UpdateManager bound view).
27935      */
27936     prepareData : function(data, index, record)
27937     {
27938         this.fireEvent("preparedata", this, data, index, record);
27939         return data;
27940     },
27941
27942     onUpdate : function(ds, record){
27943         // Roo.log('on update');   
27944         this.clearSelections();
27945         var index = this.store.indexOf(record);
27946         var n = this.nodes[index];
27947         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27948         n.parentNode.removeChild(n);
27949         this.updateIndexes(index, index);
27950     },
27951
27952     
27953     
27954 // --------- FIXME     
27955     onAdd : function(ds, records, index)
27956     {
27957         //Roo.log(['on Add', ds, records, index] );        
27958         this.clearSelections();
27959         if(this.nodes.length == 0){
27960             this.refresh();
27961             return;
27962         }
27963         var n = this.nodes[index];
27964         for(var i = 0, len = records.length; i < len; i++){
27965             var d = this.prepareData(records[i].data, i, records[i]);
27966             if(n){
27967                 this.tpl.insertBefore(n, d);
27968             }else{
27969                 
27970                 this.tpl.append(this.el, d);
27971             }
27972         }
27973         this.updateIndexes(index);
27974     },
27975
27976     onRemove : function(ds, record, index){
27977        // Roo.log('onRemove');
27978         this.clearSelections();
27979         var el = this.dataName  ?
27980             this.el.child('.roo-tpl-' + this.dataName) :
27981             this.el; 
27982         
27983         el.dom.removeChild(this.nodes[index]);
27984         this.updateIndexes(index);
27985     },
27986
27987     /**
27988      * Refresh an individual node.
27989      * @param {Number} index
27990      */
27991     refreshNode : function(index){
27992         this.onUpdate(this.store, this.store.getAt(index));
27993     },
27994
27995     updateIndexes : function(startIndex, endIndex){
27996         var ns = this.nodes;
27997         startIndex = startIndex || 0;
27998         endIndex = endIndex || ns.length - 1;
27999         for(var i = startIndex; i <= endIndex; i++){
28000             ns[i].nodeIndex = i;
28001         }
28002     },
28003
28004     /**
28005      * Changes the data store this view uses and refresh the view.
28006      * @param {Store} store
28007      */
28008     setStore : function(store, initial){
28009         if(!initial && this.store){
28010             this.store.un("datachanged", this.refresh);
28011             this.store.un("add", this.onAdd);
28012             this.store.un("remove", this.onRemove);
28013             this.store.un("update", this.onUpdate);
28014             this.store.un("clear", this.refresh);
28015             this.store.un("beforeload", this.onBeforeLoad);
28016             this.store.un("load", this.onLoad);
28017             this.store.un("loadexception", this.onLoad);
28018         }
28019         if(store){
28020           
28021             store.on("datachanged", this.refresh, this);
28022             store.on("add", this.onAdd, this);
28023             store.on("remove", this.onRemove, this);
28024             store.on("update", this.onUpdate, this);
28025             store.on("clear", this.refresh, this);
28026             store.on("beforeload", this.onBeforeLoad, this);
28027             store.on("load", this.onLoad, this);
28028             store.on("loadexception", this.onLoad, this);
28029         }
28030         
28031         if(store){
28032             this.refresh();
28033         }
28034     },
28035     /**
28036      * onbeforeLoad - masks the loading area.
28037      *
28038      */
28039     onBeforeLoad : function(store,opts)
28040     {
28041          //Roo.log('onBeforeLoad');   
28042         if (!opts.add) {
28043             this.el.update("");
28044         }
28045         this.el.mask(this.mask ? this.mask : "Loading" ); 
28046     },
28047     onLoad : function ()
28048     {
28049         this.el.unmask();
28050     },
28051     
28052
28053     /**
28054      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28055      * @param {HTMLElement} node
28056      * @return {HTMLElement} The template node
28057      */
28058     findItemFromChild : function(node){
28059         var el = this.dataName  ?
28060             this.el.child('.roo-tpl-' + this.dataName,true) :
28061             this.el.dom; 
28062         
28063         if(!node || node.parentNode == el){
28064                     return node;
28065             }
28066             var p = node.parentNode;
28067             while(p && p != el){
28068             if(p.parentNode == el){
28069                 return p;
28070             }
28071             p = p.parentNode;
28072         }
28073             return null;
28074     },
28075
28076     /** @ignore */
28077     onClick : function(e){
28078         var item = this.findItemFromChild(e.getTarget());
28079         if(item){
28080             var index = this.indexOf(item);
28081             if(this.onItemClick(item, index, e) !== false){
28082                 this.fireEvent("click", this, index, item, e);
28083             }
28084         }else{
28085             this.clearSelections();
28086         }
28087     },
28088
28089     /** @ignore */
28090     onContextMenu : function(e){
28091         var item = this.findItemFromChild(e.getTarget());
28092         if(item){
28093             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28094         }
28095     },
28096
28097     /** @ignore */
28098     onDblClick : function(e){
28099         var item = this.findItemFromChild(e.getTarget());
28100         if(item){
28101             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28102         }
28103     },
28104
28105     onItemClick : function(item, index, e)
28106     {
28107         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28108             return false;
28109         }
28110         if (this.toggleSelect) {
28111             var m = this.isSelected(item) ? 'unselect' : 'select';
28112             //Roo.log(m);
28113             var _t = this;
28114             _t[m](item, true, false);
28115             return true;
28116         }
28117         if(this.multiSelect || this.singleSelect){
28118             if(this.multiSelect && e.shiftKey && this.lastSelection){
28119                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28120             }else{
28121                 this.select(item, this.multiSelect && e.ctrlKey);
28122                 this.lastSelection = item;
28123             }
28124             
28125             if(!this.tickable){
28126                 e.preventDefault();
28127             }
28128             
28129         }
28130         return true;
28131     },
28132
28133     /**
28134      * Get the number of selected nodes.
28135      * @return {Number}
28136      */
28137     getSelectionCount : function(){
28138         return this.selections.length;
28139     },
28140
28141     /**
28142      * Get the currently selected nodes.
28143      * @return {Array} An array of HTMLElements
28144      */
28145     getSelectedNodes : function(){
28146         return this.selections;
28147     },
28148
28149     /**
28150      * Get the indexes of the selected nodes.
28151      * @return {Array}
28152      */
28153     getSelectedIndexes : function(){
28154         var indexes = [], s = this.selections;
28155         for(var i = 0, len = s.length; i < len; i++){
28156             indexes.push(s[i].nodeIndex);
28157         }
28158         return indexes;
28159     },
28160
28161     /**
28162      * Clear all selections
28163      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28164      */
28165     clearSelections : function(suppressEvent){
28166         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28167             this.cmp.elements = this.selections;
28168             this.cmp.removeClass(this.selectedClass);
28169             this.selections = [];
28170             if(!suppressEvent){
28171                 this.fireEvent("selectionchange", this, this.selections);
28172             }
28173         }
28174     },
28175
28176     /**
28177      * Returns true if the passed node is selected
28178      * @param {HTMLElement/Number} node The node or node index
28179      * @return {Boolean}
28180      */
28181     isSelected : function(node){
28182         var s = this.selections;
28183         if(s.length < 1){
28184             return false;
28185         }
28186         node = this.getNode(node);
28187         return s.indexOf(node) !== -1;
28188     },
28189
28190     /**
28191      * Selects nodes.
28192      * @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
28193      * @param {Boolean} keepExisting (optional) true to keep existing selections
28194      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28195      */
28196     select : function(nodeInfo, keepExisting, suppressEvent){
28197         if(nodeInfo instanceof Array){
28198             if(!keepExisting){
28199                 this.clearSelections(true);
28200             }
28201             for(var i = 0, len = nodeInfo.length; i < len; i++){
28202                 this.select(nodeInfo[i], true, true);
28203             }
28204             return;
28205         } 
28206         var node = this.getNode(nodeInfo);
28207         if(!node || this.isSelected(node)){
28208             return; // already selected.
28209         }
28210         if(!keepExisting){
28211             this.clearSelections(true);
28212         }
28213         
28214         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28215             Roo.fly(node).addClass(this.selectedClass);
28216             this.selections.push(node);
28217             if(!suppressEvent){
28218                 this.fireEvent("selectionchange", this, this.selections);
28219             }
28220         }
28221         
28222         
28223     },
28224       /**
28225      * Unselects nodes.
28226      * @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
28227      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28228      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28229      */
28230     unselect : function(nodeInfo, keepExisting, suppressEvent)
28231     {
28232         if(nodeInfo instanceof Array){
28233             Roo.each(this.selections, function(s) {
28234                 this.unselect(s, nodeInfo);
28235             }, this);
28236             return;
28237         }
28238         var node = this.getNode(nodeInfo);
28239         if(!node || !this.isSelected(node)){
28240             //Roo.log("not selected");
28241             return; // not selected.
28242         }
28243         // fireevent???
28244         var ns = [];
28245         Roo.each(this.selections, function(s) {
28246             if (s == node ) {
28247                 Roo.fly(node).removeClass(this.selectedClass);
28248
28249                 return;
28250             }
28251             ns.push(s);
28252         },this);
28253         
28254         this.selections= ns;
28255         this.fireEvent("selectionchange", this, this.selections);
28256     },
28257
28258     /**
28259      * Gets a template node.
28260      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28261      * @return {HTMLElement} The node or null if it wasn't found
28262      */
28263     getNode : function(nodeInfo){
28264         if(typeof nodeInfo == "string"){
28265             return document.getElementById(nodeInfo);
28266         }else if(typeof nodeInfo == "number"){
28267             return this.nodes[nodeInfo];
28268         }
28269         return nodeInfo;
28270     },
28271
28272     /**
28273      * Gets a range template nodes.
28274      * @param {Number} startIndex
28275      * @param {Number} endIndex
28276      * @return {Array} An array of nodes
28277      */
28278     getNodes : function(start, end){
28279         var ns = this.nodes;
28280         start = start || 0;
28281         end = typeof end == "undefined" ? ns.length - 1 : end;
28282         var nodes = [];
28283         if(start <= end){
28284             for(var i = start; i <= end; i++){
28285                 nodes.push(ns[i]);
28286             }
28287         } else{
28288             for(var i = start; i >= end; i--){
28289                 nodes.push(ns[i]);
28290             }
28291         }
28292         return nodes;
28293     },
28294
28295     /**
28296      * Finds the index of the passed node
28297      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28298      * @return {Number} The index of the node or -1
28299      */
28300     indexOf : function(node){
28301         node = this.getNode(node);
28302         if(typeof node.nodeIndex == "number"){
28303             return node.nodeIndex;
28304         }
28305         var ns = this.nodes;
28306         for(var i = 0, len = ns.length; i < len; i++){
28307             if(ns[i] == node){
28308                 return i;
28309             }
28310         }
28311         return -1;
28312     }
28313 });
28314 /*
28315  * Based on:
28316  * Ext JS Library 1.1.1
28317  * Copyright(c) 2006-2007, Ext JS, LLC.
28318  *
28319  * Originally Released Under LGPL - original licence link has changed is not relivant.
28320  *
28321  * Fork - LGPL
28322  * <script type="text/javascript">
28323  */
28324
28325 /**
28326  * @class Roo.JsonView
28327  * @extends Roo.View
28328  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28329 <pre><code>
28330 var view = new Roo.JsonView({
28331     container: "my-element",
28332     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28333     multiSelect: true, 
28334     jsonRoot: "data" 
28335 });
28336
28337 // listen for node click?
28338 view.on("click", function(vw, index, node, e){
28339     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28340 });
28341
28342 // direct load of JSON data
28343 view.load("foobar.php");
28344
28345 // Example from my blog list
28346 var tpl = new Roo.Template(
28347     '&lt;div class="entry"&gt;' +
28348     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28349     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28350     "&lt;/div&gt;&lt;hr /&gt;"
28351 );
28352
28353 var moreView = new Roo.JsonView({
28354     container :  "entry-list", 
28355     template : tpl,
28356     jsonRoot: "posts"
28357 });
28358 moreView.on("beforerender", this.sortEntries, this);
28359 moreView.load({
28360     url: "/blog/get-posts.php",
28361     params: "allposts=true",
28362     text: "Loading Blog Entries..."
28363 });
28364 </code></pre>
28365
28366 * Note: old code is supported with arguments : (container, template, config)
28367
28368
28369  * @constructor
28370  * Create a new JsonView
28371  * 
28372  * @param {Object} config The config object
28373  * 
28374  */
28375 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28376     
28377     
28378     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28379
28380     var um = this.el.getUpdateManager();
28381     um.setRenderer(this);
28382     um.on("update", this.onLoad, this);
28383     um.on("failure", this.onLoadException, this);
28384
28385     /**
28386      * @event beforerender
28387      * Fires before rendering of the downloaded JSON data.
28388      * @param {Roo.JsonView} this
28389      * @param {Object} data The JSON data loaded
28390      */
28391     /**
28392      * @event load
28393      * Fires when data is loaded.
28394      * @param {Roo.JsonView} this
28395      * @param {Object} data The JSON data loaded
28396      * @param {Object} response The raw Connect response object
28397      */
28398     /**
28399      * @event loadexception
28400      * Fires when loading fails.
28401      * @param {Roo.JsonView} this
28402      * @param {Object} response The raw Connect response object
28403      */
28404     this.addEvents({
28405         'beforerender' : true,
28406         'load' : true,
28407         'loadexception' : true
28408     });
28409 };
28410 Roo.extend(Roo.JsonView, Roo.View, {
28411     /**
28412      * @type {String} The root property in the loaded JSON object that contains the data
28413      */
28414     jsonRoot : "",
28415
28416     /**
28417      * Refreshes the view.
28418      */
28419     refresh : function(){
28420         this.clearSelections();
28421         this.el.update("");
28422         var html = [];
28423         var o = this.jsonData;
28424         if(o && o.length > 0){
28425             for(var i = 0, len = o.length; i < len; i++){
28426                 var data = this.prepareData(o[i], i, o);
28427                 html[html.length] = this.tpl.apply(data);
28428             }
28429         }else{
28430             html.push(this.emptyText);
28431         }
28432         this.el.update(html.join(""));
28433         this.nodes = this.el.dom.childNodes;
28434         this.updateIndexes(0);
28435     },
28436
28437     /**
28438      * 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.
28439      * @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:
28440      <pre><code>
28441      view.load({
28442          url: "your-url.php",
28443          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28444          callback: yourFunction,
28445          scope: yourObject, //(optional scope)
28446          discardUrl: false,
28447          nocache: false,
28448          text: "Loading...",
28449          timeout: 30,
28450          scripts: false
28451      });
28452      </code></pre>
28453      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28454      * 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.
28455      * @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}
28456      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28457      * @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.
28458      */
28459     load : function(){
28460         var um = this.el.getUpdateManager();
28461         um.update.apply(um, arguments);
28462     },
28463
28464     // note - render is a standard framework call...
28465     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28466     render : function(el, response){
28467         
28468         this.clearSelections();
28469         this.el.update("");
28470         var o;
28471         try{
28472             if (response != '') {
28473                 o = Roo.util.JSON.decode(response.responseText);
28474                 if(this.jsonRoot){
28475                     
28476                     o = o[this.jsonRoot];
28477                 }
28478             }
28479         } catch(e){
28480         }
28481         /**
28482          * The current JSON data or null
28483          */
28484         this.jsonData = o;
28485         this.beforeRender();
28486         this.refresh();
28487     },
28488
28489 /**
28490  * Get the number of records in the current JSON dataset
28491  * @return {Number}
28492  */
28493     getCount : function(){
28494         return this.jsonData ? this.jsonData.length : 0;
28495     },
28496
28497 /**
28498  * Returns the JSON object for the specified node(s)
28499  * @param {HTMLElement/Array} node The node or an array of nodes
28500  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28501  * you get the JSON object for the node
28502  */
28503     getNodeData : function(node){
28504         if(node instanceof Array){
28505             var data = [];
28506             for(var i = 0, len = node.length; i < len; i++){
28507                 data.push(this.getNodeData(node[i]));
28508             }
28509             return data;
28510         }
28511         return this.jsonData[this.indexOf(node)] || null;
28512     },
28513
28514     beforeRender : function(){
28515         this.snapshot = this.jsonData;
28516         if(this.sortInfo){
28517             this.sort.apply(this, this.sortInfo);
28518         }
28519         this.fireEvent("beforerender", this, this.jsonData);
28520     },
28521
28522     onLoad : function(el, o){
28523         this.fireEvent("load", this, this.jsonData, o);
28524     },
28525
28526     onLoadException : function(el, o){
28527         this.fireEvent("loadexception", this, o);
28528     },
28529
28530 /**
28531  * Filter the data by a specific property.
28532  * @param {String} property A property on your JSON objects
28533  * @param {String/RegExp} value Either string that the property values
28534  * should start with, or a RegExp to test against the property
28535  */
28536     filter : function(property, value){
28537         if(this.jsonData){
28538             var data = [];
28539             var ss = this.snapshot;
28540             if(typeof value == "string"){
28541                 var vlen = value.length;
28542                 if(vlen == 0){
28543                     this.clearFilter();
28544                     return;
28545                 }
28546                 value = value.toLowerCase();
28547                 for(var i = 0, len = ss.length; i < len; i++){
28548                     var o = ss[i];
28549                     if(o[property].substr(0, vlen).toLowerCase() == value){
28550                         data.push(o);
28551                     }
28552                 }
28553             } else if(value.exec){ // regex?
28554                 for(var i = 0, len = ss.length; i < len; i++){
28555                     var o = ss[i];
28556                     if(value.test(o[property])){
28557                         data.push(o);
28558                     }
28559                 }
28560             } else{
28561                 return;
28562             }
28563             this.jsonData = data;
28564             this.refresh();
28565         }
28566     },
28567
28568 /**
28569  * Filter by a function. The passed function will be called with each
28570  * object in the current dataset. If the function returns true the value is kept,
28571  * otherwise it is filtered.
28572  * @param {Function} fn
28573  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28574  */
28575     filterBy : function(fn, scope){
28576         if(this.jsonData){
28577             var data = [];
28578             var ss = this.snapshot;
28579             for(var i = 0, len = ss.length; i < len; i++){
28580                 var o = ss[i];
28581                 if(fn.call(scope || this, o)){
28582                     data.push(o);
28583                 }
28584             }
28585             this.jsonData = data;
28586             this.refresh();
28587         }
28588     },
28589
28590 /**
28591  * Clears the current filter.
28592  */
28593     clearFilter : function(){
28594         if(this.snapshot && this.jsonData != this.snapshot){
28595             this.jsonData = this.snapshot;
28596             this.refresh();
28597         }
28598     },
28599
28600
28601 /**
28602  * Sorts the data for this view and refreshes it.
28603  * @param {String} property A property on your JSON objects to sort on
28604  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28605  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28606  */
28607     sort : function(property, dir, sortType){
28608         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28609         if(this.jsonData){
28610             var p = property;
28611             var dsc = dir && dir.toLowerCase() == "desc";
28612             var f = function(o1, o2){
28613                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28614                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28615                 ;
28616                 if(v1 < v2){
28617                     return dsc ? +1 : -1;
28618                 } else if(v1 > v2){
28619                     return dsc ? -1 : +1;
28620                 } else{
28621                     return 0;
28622                 }
28623             };
28624             this.jsonData.sort(f);
28625             this.refresh();
28626             if(this.jsonData != this.snapshot){
28627                 this.snapshot.sort(f);
28628             }
28629         }
28630     }
28631 });/*
28632  * Based on:
28633  * Ext JS Library 1.1.1
28634  * Copyright(c) 2006-2007, Ext JS, LLC.
28635  *
28636  * Originally Released Under LGPL - original licence link has changed is not relivant.
28637  *
28638  * Fork - LGPL
28639  * <script type="text/javascript">
28640  */
28641  
28642
28643 /**
28644  * @class Roo.ColorPalette
28645  * @extends Roo.Component
28646  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28647  * Here's an example of typical usage:
28648  * <pre><code>
28649 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28650 cp.render('my-div');
28651
28652 cp.on('select', function(palette, selColor){
28653     // do something with selColor
28654 });
28655 </code></pre>
28656  * @constructor
28657  * Create a new ColorPalette
28658  * @param {Object} config The config object
28659  */
28660 Roo.ColorPalette = function(config){
28661     Roo.ColorPalette.superclass.constructor.call(this, config);
28662     this.addEvents({
28663         /**
28664              * @event select
28665              * Fires when a color is selected
28666              * @param {ColorPalette} this
28667              * @param {String} color The 6-digit color hex code (without the # symbol)
28668              */
28669         select: true
28670     });
28671
28672     if(this.handler){
28673         this.on("select", this.handler, this.scope, true);
28674     }
28675 };
28676 Roo.extend(Roo.ColorPalette, Roo.Component, {
28677     /**
28678      * @cfg {String} itemCls
28679      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28680      */
28681     itemCls : "x-color-palette",
28682     /**
28683      * @cfg {String} value
28684      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28685      * the hex codes are case-sensitive.
28686      */
28687     value : null,
28688     clickEvent:'click',
28689     // private
28690     ctype: "Roo.ColorPalette",
28691
28692     /**
28693      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28694      */
28695     allowReselect : false,
28696
28697     /**
28698      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28699      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28700      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28701      * of colors with the width setting until the box is symmetrical.</p>
28702      * <p>You can override individual colors if needed:</p>
28703      * <pre><code>
28704 var cp = new Roo.ColorPalette();
28705 cp.colors[0] = "FF0000";  // change the first box to red
28706 </code></pre>
28707
28708 Or you can provide a custom array of your own for complete control:
28709 <pre><code>
28710 var cp = new Roo.ColorPalette();
28711 cp.colors = ["000000", "993300", "333300"];
28712 </code></pre>
28713      * @type Array
28714      */
28715     colors : [
28716         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28717         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28718         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28719         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28720         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28721     ],
28722
28723     // private
28724     onRender : function(container, position){
28725         var t = new Roo.MasterTemplate(
28726             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28727         );
28728         var c = this.colors;
28729         for(var i = 0, len = c.length; i < len; i++){
28730             t.add([c[i]]);
28731         }
28732         var el = document.createElement("div");
28733         el.className = this.itemCls;
28734         t.overwrite(el);
28735         container.dom.insertBefore(el, position);
28736         this.el = Roo.get(el);
28737         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28738         if(this.clickEvent != 'click'){
28739             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28740         }
28741     },
28742
28743     // private
28744     afterRender : function(){
28745         Roo.ColorPalette.superclass.afterRender.call(this);
28746         if(this.value){
28747             var s = this.value;
28748             this.value = null;
28749             this.select(s);
28750         }
28751     },
28752
28753     // private
28754     handleClick : function(e, t){
28755         e.preventDefault();
28756         if(!this.disabled){
28757             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28758             this.select(c.toUpperCase());
28759         }
28760     },
28761
28762     /**
28763      * Selects the specified color in the palette (fires the select event)
28764      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28765      */
28766     select : function(color){
28767         color = color.replace("#", "");
28768         if(color != this.value || this.allowReselect){
28769             var el = this.el;
28770             if(this.value){
28771                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28772             }
28773             el.child("a.color-"+color).addClass("x-color-palette-sel");
28774             this.value = color;
28775             this.fireEvent("select", this, color);
28776         }
28777     }
28778 });/*
28779  * Based on:
28780  * Ext JS Library 1.1.1
28781  * Copyright(c) 2006-2007, Ext JS, LLC.
28782  *
28783  * Originally Released Under LGPL - original licence link has changed is not relivant.
28784  *
28785  * Fork - LGPL
28786  * <script type="text/javascript">
28787  */
28788  
28789 /**
28790  * @class Roo.DatePicker
28791  * @extends Roo.Component
28792  * Simple date picker class.
28793  * @constructor
28794  * Create a new DatePicker
28795  * @param {Object} config The config object
28796  */
28797 Roo.DatePicker = function(config){
28798     Roo.DatePicker.superclass.constructor.call(this, config);
28799
28800     this.value = config && config.value ?
28801                  config.value.clearTime() : new Date().clearTime();
28802
28803     this.addEvents({
28804         /**
28805              * @event select
28806              * Fires when a date is selected
28807              * @param {DatePicker} this
28808              * @param {Date} date The selected date
28809              */
28810         'select': true,
28811         /**
28812              * @event monthchange
28813              * Fires when the displayed month changes 
28814              * @param {DatePicker} this
28815              * @param {Date} date The selected month
28816              */
28817         'monthchange': true
28818     });
28819
28820     if(this.handler){
28821         this.on("select", this.handler,  this.scope || this);
28822     }
28823     // build the disabledDatesRE
28824     if(!this.disabledDatesRE && this.disabledDates){
28825         var dd = this.disabledDates;
28826         var re = "(?:";
28827         for(var i = 0; i < dd.length; i++){
28828             re += dd[i];
28829             if(i != dd.length-1) {
28830                 re += "|";
28831             }
28832         }
28833         this.disabledDatesRE = new RegExp(re + ")");
28834     }
28835 };
28836
28837 Roo.extend(Roo.DatePicker, Roo.Component, {
28838     /**
28839      * @cfg {String} todayText
28840      * The text to display on the button that selects the current date (defaults to "Today")
28841      */
28842     todayText : "Today",
28843     /**
28844      * @cfg {String} okText
28845      * The text to display on the ok button
28846      */
28847     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28848     /**
28849      * @cfg {String} cancelText
28850      * The text to display on the cancel button
28851      */
28852     cancelText : "Cancel",
28853     /**
28854      * @cfg {String} todayTip
28855      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28856      */
28857     todayTip : "{0} (Spacebar)",
28858     /**
28859      * @cfg {Date} minDate
28860      * Minimum allowable date (JavaScript date object, defaults to null)
28861      */
28862     minDate : null,
28863     /**
28864      * @cfg {Date} maxDate
28865      * Maximum allowable date (JavaScript date object, defaults to null)
28866      */
28867     maxDate : null,
28868     /**
28869      * @cfg {String} minText
28870      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28871      */
28872     minText : "This date is before the minimum date",
28873     /**
28874      * @cfg {String} maxText
28875      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28876      */
28877     maxText : "This date is after the maximum date",
28878     /**
28879      * @cfg {String} format
28880      * The default date format string which can be overriden for localization support.  The format must be
28881      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28882      */
28883     format : "m/d/y",
28884     /**
28885      * @cfg {Array} disabledDays
28886      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28887      */
28888     disabledDays : null,
28889     /**
28890      * @cfg {String} disabledDaysText
28891      * The tooltip to display when the date falls on a disabled day (defaults to "")
28892      */
28893     disabledDaysText : "",
28894     /**
28895      * @cfg {RegExp} disabledDatesRE
28896      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28897      */
28898     disabledDatesRE : null,
28899     /**
28900      * @cfg {String} disabledDatesText
28901      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28902      */
28903     disabledDatesText : "",
28904     /**
28905      * @cfg {Boolean} constrainToViewport
28906      * True to constrain the date picker to the viewport (defaults to true)
28907      */
28908     constrainToViewport : true,
28909     /**
28910      * @cfg {Array} monthNames
28911      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28912      */
28913     monthNames : Date.monthNames,
28914     /**
28915      * @cfg {Array} dayNames
28916      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28917      */
28918     dayNames : Date.dayNames,
28919     /**
28920      * @cfg {String} nextText
28921      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28922      */
28923     nextText: 'Next Month (Control+Right)',
28924     /**
28925      * @cfg {String} prevText
28926      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28927      */
28928     prevText: 'Previous Month (Control+Left)',
28929     /**
28930      * @cfg {String} monthYearText
28931      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28932      */
28933     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28934     /**
28935      * @cfg {Number} startDay
28936      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28937      */
28938     startDay : 0,
28939     /**
28940      * @cfg {Bool} showClear
28941      * Show a clear button (usefull for date form elements that can be blank.)
28942      */
28943     
28944     showClear: false,
28945     
28946     /**
28947      * Sets the value of the date field
28948      * @param {Date} value The date to set
28949      */
28950     setValue : function(value){
28951         var old = this.value;
28952         
28953         if (typeof(value) == 'string') {
28954          
28955             value = Date.parseDate(value, this.format);
28956         }
28957         if (!value) {
28958             value = new Date();
28959         }
28960         
28961         this.value = value.clearTime(true);
28962         if(this.el){
28963             this.update(this.value);
28964         }
28965     },
28966
28967     /**
28968      * Gets the current selected value of the date field
28969      * @return {Date} The selected date
28970      */
28971     getValue : function(){
28972         return this.value;
28973     },
28974
28975     // private
28976     focus : function(){
28977         if(this.el){
28978             this.update(this.activeDate);
28979         }
28980     },
28981
28982     // privateval
28983     onRender : function(container, position){
28984         
28985         var m = [
28986              '<table cellspacing="0">',
28987                 '<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>',
28988                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28989         var dn = this.dayNames;
28990         for(var i = 0; i < 7; i++){
28991             var d = this.startDay+i;
28992             if(d > 6){
28993                 d = d-7;
28994             }
28995             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28996         }
28997         m[m.length] = "</tr></thead><tbody><tr>";
28998         for(var i = 0; i < 42; i++) {
28999             if(i % 7 == 0 && i != 0){
29000                 m[m.length] = "</tr><tr>";
29001             }
29002             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29003         }
29004         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29005             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29006
29007         var el = document.createElement("div");
29008         el.className = "x-date-picker";
29009         el.innerHTML = m.join("");
29010
29011         container.dom.insertBefore(el, position);
29012
29013         this.el = Roo.get(el);
29014         this.eventEl = Roo.get(el.firstChild);
29015
29016         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29017             handler: this.showPrevMonth,
29018             scope: this,
29019             preventDefault:true,
29020             stopDefault:true
29021         });
29022
29023         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29024             handler: this.showNextMonth,
29025             scope: this,
29026             preventDefault:true,
29027             stopDefault:true
29028         });
29029
29030         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29031
29032         this.monthPicker = this.el.down('div.x-date-mp');
29033         this.monthPicker.enableDisplayMode('block');
29034         
29035         var kn = new Roo.KeyNav(this.eventEl, {
29036             "left" : function(e){
29037                 e.ctrlKey ?
29038                     this.showPrevMonth() :
29039                     this.update(this.activeDate.add("d", -1));
29040             },
29041
29042             "right" : function(e){
29043                 e.ctrlKey ?
29044                     this.showNextMonth() :
29045                     this.update(this.activeDate.add("d", 1));
29046             },
29047
29048             "up" : function(e){
29049                 e.ctrlKey ?
29050                     this.showNextYear() :
29051                     this.update(this.activeDate.add("d", -7));
29052             },
29053
29054             "down" : function(e){
29055                 e.ctrlKey ?
29056                     this.showPrevYear() :
29057                     this.update(this.activeDate.add("d", 7));
29058             },
29059
29060             "pageUp" : function(e){
29061                 this.showNextMonth();
29062             },
29063
29064             "pageDown" : function(e){
29065                 this.showPrevMonth();
29066             },
29067
29068             "enter" : function(e){
29069                 e.stopPropagation();
29070                 return true;
29071             },
29072
29073             scope : this
29074         });
29075
29076         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29077
29078         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29079
29080         this.el.unselectable();
29081         
29082         this.cells = this.el.select("table.x-date-inner tbody td");
29083         this.textNodes = this.el.query("table.x-date-inner tbody span");
29084
29085         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29086             text: "&#160;",
29087             tooltip: this.monthYearText
29088         });
29089
29090         this.mbtn.on('click', this.showMonthPicker, this);
29091         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29092
29093
29094         var today = (new Date()).dateFormat(this.format);
29095         
29096         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29097         if (this.showClear) {
29098             baseTb.add( new Roo.Toolbar.Fill());
29099         }
29100         baseTb.add({
29101             text: String.format(this.todayText, today),
29102             tooltip: String.format(this.todayTip, today),
29103             handler: this.selectToday,
29104             scope: this
29105         });
29106         
29107         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29108             
29109         //});
29110         if (this.showClear) {
29111             
29112             baseTb.add( new Roo.Toolbar.Fill());
29113             baseTb.add({
29114                 text: '&#160;',
29115                 cls: 'x-btn-icon x-btn-clear',
29116                 handler: function() {
29117                     //this.value = '';
29118                     this.fireEvent("select", this, '');
29119                 },
29120                 scope: this
29121             });
29122         }
29123         
29124         
29125         if(Roo.isIE){
29126             this.el.repaint();
29127         }
29128         this.update(this.value);
29129     },
29130
29131     createMonthPicker : function(){
29132         if(!this.monthPicker.dom.firstChild){
29133             var buf = ['<table border="0" cellspacing="0">'];
29134             for(var i = 0; i < 6; i++){
29135                 buf.push(
29136                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29137                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29138                     i == 0 ?
29139                     '<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>' :
29140                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29141                 );
29142             }
29143             buf.push(
29144                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29145                     this.okText,
29146                     '</button><button type="button" class="x-date-mp-cancel">',
29147                     this.cancelText,
29148                     '</button></td></tr>',
29149                 '</table>'
29150             );
29151             this.monthPicker.update(buf.join(''));
29152             this.monthPicker.on('click', this.onMonthClick, this);
29153             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29154
29155             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29156             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29157
29158             this.mpMonths.each(function(m, a, i){
29159                 i += 1;
29160                 if((i%2) == 0){
29161                     m.dom.xmonth = 5 + Math.round(i * .5);
29162                 }else{
29163                     m.dom.xmonth = Math.round((i-1) * .5);
29164                 }
29165             });
29166         }
29167     },
29168
29169     showMonthPicker : function(){
29170         this.createMonthPicker();
29171         var size = this.el.getSize();
29172         this.monthPicker.setSize(size);
29173         this.monthPicker.child('table').setSize(size);
29174
29175         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29176         this.updateMPMonth(this.mpSelMonth);
29177         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29178         this.updateMPYear(this.mpSelYear);
29179
29180         this.monthPicker.slideIn('t', {duration:.2});
29181     },
29182
29183     updateMPYear : function(y){
29184         this.mpyear = y;
29185         var ys = this.mpYears.elements;
29186         for(var i = 1; i <= 10; i++){
29187             var td = ys[i-1], y2;
29188             if((i%2) == 0){
29189                 y2 = y + Math.round(i * .5);
29190                 td.firstChild.innerHTML = y2;
29191                 td.xyear = y2;
29192             }else{
29193                 y2 = y - (5-Math.round(i * .5));
29194                 td.firstChild.innerHTML = y2;
29195                 td.xyear = y2;
29196             }
29197             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29198         }
29199     },
29200
29201     updateMPMonth : function(sm){
29202         this.mpMonths.each(function(m, a, i){
29203             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29204         });
29205     },
29206
29207     selectMPMonth: function(m){
29208         
29209     },
29210
29211     onMonthClick : function(e, t){
29212         e.stopEvent();
29213         var el = new Roo.Element(t), pn;
29214         if(el.is('button.x-date-mp-cancel')){
29215             this.hideMonthPicker();
29216         }
29217         else if(el.is('button.x-date-mp-ok')){
29218             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29219             this.hideMonthPicker();
29220         }
29221         else if(pn = el.up('td.x-date-mp-month', 2)){
29222             this.mpMonths.removeClass('x-date-mp-sel');
29223             pn.addClass('x-date-mp-sel');
29224             this.mpSelMonth = pn.dom.xmonth;
29225         }
29226         else if(pn = el.up('td.x-date-mp-year', 2)){
29227             this.mpYears.removeClass('x-date-mp-sel');
29228             pn.addClass('x-date-mp-sel');
29229             this.mpSelYear = pn.dom.xyear;
29230         }
29231         else if(el.is('a.x-date-mp-prev')){
29232             this.updateMPYear(this.mpyear-10);
29233         }
29234         else if(el.is('a.x-date-mp-next')){
29235             this.updateMPYear(this.mpyear+10);
29236         }
29237     },
29238
29239     onMonthDblClick : function(e, t){
29240         e.stopEvent();
29241         var el = new Roo.Element(t), pn;
29242         if(pn = el.up('td.x-date-mp-month', 2)){
29243             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29244             this.hideMonthPicker();
29245         }
29246         else if(pn = el.up('td.x-date-mp-year', 2)){
29247             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29248             this.hideMonthPicker();
29249         }
29250     },
29251
29252     hideMonthPicker : function(disableAnim){
29253         if(this.monthPicker){
29254             if(disableAnim === true){
29255                 this.monthPicker.hide();
29256             }else{
29257                 this.monthPicker.slideOut('t', {duration:.2});
29258             }
29259         }
29260     },
29261
29262     // private
29263     showPrevMonth : function(e){
29264         this.update(this.activeDate.add("mo", -1));
29265     },
29266
29267     // private
29268     showNextMonth : function(e){
29269         this.update(this.activeDate.add("mo", 1));
29270     },
29271
29272     // private
29273     showPrevYear : function(){
29274         this.update(this.activeDate.add("y", -1));
29275     },
29276
29277     // private
29278     showNextYear : function(){
29279         this.update(this.activeDate.add("y", 1));
29280     },
29281
29282     // private
29283     handleMouseWheel : function(e){
29284         var delta = e.getWheelDelta();
29285         if(delta > 0){
29286             this.showPrevMonth();
29287             e.stopEvent();
29288         } else if(delta < 0){
29289             this.showNextMonth();
29290             e.stopEvent();
29291         }
29292     },
29293
29294     // private
29295     handleDateClick : function(e, t){
29296         e.stopEvent();
29297         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29298             this.setValue(new Date(t.dateValue));
29299             this.fireEvent("select", this, this.value);
29300         }
29301     },
29302
29303     // private
29304     selectToday : function(){
29305         this.setValue(new Date().clearTime());
29306         this.fireEvent("select", this, this.value);
29307     },
29308
29309     // private
29310     update : function(date)
29311     {
29312         var vd = this.activeDate;
29313         this.activeDate = date;
29314         if(vd && this.el){
29315             var t = date.getTime();
29316             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29317                 this.cells.removeClass("x-date-selected");
29318                 this.cells.each(function(c){
29319                    if(c.dom.firstChild.dateValue == t){
29320                        c.addClass("x-date-selected");
29321                        setTimeout(function(){
29322                             try{c.dom.firstChild.focus();}catch(e){}
29323                        }, 50);
29324                        return false;
29325                    }
29326                 });
29327                 return;
29328             }
29329         }
29330         
29331         var days = date.getDaysInMonth();
29332         var firstOfMonth = date.getFirstDateOfMonth();
29333         var startingPos = firstOfMonth.getDay()-this.startDay;
29334
29335         if(startingPos <= this.startDay){
29336             startingPos += 7;
29337         }
29338
29339         var pm = date.add("mo", -1);
29340         var prevStart = pm.getDaysInMonth()-startingPos;
29341
29342         var cells = this.cells.elements;
29343         var textEls = this.textNodes;
29344         days += startingPos;
29345
29346         // convert everything to numbers so it's fast
29347         var day = 86400000;
29348         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29349         var today = new Date().clearTime().getTime();
29350         var sel = date.clearTime().getTime();
29351         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29352         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29353         var ddMatch = this.disabledDatesRE;
29354         var ddText = this.disabledDatesText;
29355         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29356         var ddaysText = this.disabledDaysText;
29357         var format = this.format;
29358
29359         var setCellClass = function(cal, cell){
29360             cell.title = "";
29361             var t = d.getTime();
29362             cell.firstChild.dateValue = t;
29363             if(t == today){
29364                 cell.className += " x-date-today";
29365                 cell.title = cal.todayText;
29366             }
29367             if(t == sel){
29368                 cell.className += " x-date-selected";
29369                 setTimeout(function(){
29370                     try{cell.firstChild.focus();}catch(e){}
29371                 }, 50);
29372             }
29373             // disabling
29374             if(t < min) {
29375                 cell.className = " x-date-disabled";
29376                 cell.title = cal.minText;
29377                 return;
29378             }
29379             if(t > max) {
29380                 cell.className = " x-date-disabled";
29381                 cell.title = cal.maxText;
29382                 return;
29383             }
29384             if(ddays){
29385                 if(ddays.indexOf(d.getDay()) != -1){
29386                     cell.title = ddaysText;
29387                     cell.className = " x-date-disabled";
29388                 }
29389             }
29390             if(ddMatch && format){
29391                 var fvalue = d.dateFormat(format);
29392                 if(ddMatch.test(fvalue)){
29393                     cell.title = ddText.replace("%0", fvalue);
29394                     cell.className = " x-date-disabled";
29395                 }
29396             }
29397         };
29398
29399         var i = 0;
29400         for(; i < startingPos; i++) {
29401             textEls[i].innerHTML = (++prevStart);
29402             d.setDate(d.getDate()+1);
29403             cells[i].className = "x-date-prevday";
29404             setCellClass(this, cells[i]);
29405         }
29406         for(; i < days; i++){
29407             intDay = i - startingPos + 1;
29408             textEls[i].innerHTML = (intDay);
29409             d.setDate(d.getDate()+1);
29410             cells[i].className = "x-date-active";
29411             setCellClass(this, cells[i]);
29412         }
29413         var extraDays = 0;
29414         for(; i < 42; i++) {
29415              textEls[i].innerHTML = (++extraDays);
29416              d.setDate(d.getDate()+1);
29417              cells[i].className = "x-date-nextday";
29418              setCellClass(this, cells[i]);
29419         }
29420
29421         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29422         this.fireEvent('monthchange', this, date);
29423         
29424         if(!this.internalRender){
29425             var main = this.el.dom.firstChild;
29426             var w = main.offsetWidth;
29427             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29428             Roo.fly(main).setWidth(w);
29429             this.internalRender = true;
29430             // opera does not respect the auto grow header center column
29431             // then, after it gets a width opera refuses to recalculate
29432             // without a second pass
29433             if(Roo.isOpera && !this.secondPass){
29434                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29435                 this.secondPass = true;
29436                 this.update.defer(10, this, [date]);
29437             }
29438         }
29439         
29440         
29441     }
29442 });        /*
29443  * Based on:
29444  * Ext JS Library 1.1.1
29445  * Copyright(c) 2006-2007, Ext JS, LLC.
29446  *
29447  * Originally Released Under LGPL - original licence link has changed is not relivant.
29448  *
29449  * Fork - LGPL
29450  * <script type="text/javascript">
29451  */
29452 /**
29453  * @class Roo.TabPanel
29454  * @extends Roo.util.Observable
29455  * A lightweight tab container.
29456  * <br><br>
29457  * Usage:
29458  * <pre><code>
29459 // basic tabs 1, built from existing content
29460 var tabs = new Roo.TabPanel("tabs1");
29461 tabs.addTab("script", "View Script");
29462 tabs.addTab("markup", "View Markup");
29463 tabs.activate("script");
29464
29465 // more advanced tabs, built from javascript
29466 var jtabs = new Roo.TabPanel("jtabs");
29467 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29468
29469 // set up the UpdateManager
29470 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29471 var updater = tab2.getUpdateManager();
29472 updater.setDefaultUrl("ajax1.htm");
29473 tab2.on('activate', updater.refresh, updater, true);
29474
29475 // Use setUrl for Ajax loading
29476 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29477 tab3.setUrl("ajax2.htm", null, true);
29478
29479 // Disabled tab
29480 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29481 tab4.disable();
29482
29483 jtabs.activate("jtabs-1");
29484  * </code></pre>
29485  * @constructor
29486  * Create a new TabPanel.
29487  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29488  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29489  */
29490 Roo.TabPanel = function(container, config){
29491     /**
29492     * The container element for this TabPanel.
29493     * @type Roo.Element
29494     */
29495     this.el = Roo.get(container, true);
29496     if(config){
29497         if(typeof config == "boolean"){
29498             this.tabPosition = config ? "bottom" : "top";
29499         }else{
29500             Roo.apply(this, config);
29501         }
29502     }
29503     if(this.tabPosition == "bottom"){
29504         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29505         this.el.addClass("x-tabs-bottom");
29506     }
29507     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29508     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29509     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29510     if(Roo.isIE){
29511         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29512     }
29513     if(this.tabPosition != "bottom"){
29514         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29515          * @type Roo.Element
29516          */
29517         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29518         this.el.addClass("x-tabs-top");
29519     }
29520     this.items = [];
29521
29522     this.bodyEl.setStyle("position", "relative");
29523
29524     this.active = null;
29525     this.activateDelegate = this.activate.createDelegate(this);
29526
29527     this.addEvents({
29528         /**
29529          * @event tabchange
29530          * Fires when the active tab changes
29531          * @param {Roo.TabPanel} this
29532          * @param {Roo.TabPanelItem} activePanel The new active tab
29533          */
29534         "tabchange": true,
29535         /**
29536          * @event beforetabchange
29537          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29538          * @param {Roo.TabPanel} this
29539          * @param {Object} e Set cancel to true on this object to cancel the tab change
29540          * @param {Roo.TabPanelItem} tab The tab being changed to
29541          */
29542         "beforetabchange" : true
29543     });
29544
29545     Roo.EventManager.onWindowResize(this.onResize, this);
29546     this.cpad = this.el.getPadding("lr");
29547     this.hiddenCount = 0;
29548
29549
29550     // toolbar on the tabbar support...
29551     if (this.toolbar) {
29552         var tcfg = this.toolbar;
29553         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29554         this.toolbar = new Roo.Toolbar(tcfg);
29555         if (Roo.isSafari) {
29556             var tbl = tcfg.container.child('table', true);
29557             tbl.setAttribute('width', '100%');
29558         }
29559         
29560     }
29561    
29562
29563
29564     Roo.TabPanel.superclass.constructor.call(this);
29565 };
29566
29567 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29568     /*
29569      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29570      */
29571     tabPosition : "top",
29572     /*
29573      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29574      */
29575     currentTabWidth : 0,
29576     /*
29577      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29578      */
29579     minTabWidth : 40,
29580     /*
29581      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29582      */
29583     maxTabWidth : 250,
29584     /*
29585      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29586      */
29587     preferredTabWidth : 175,
29588     /*
29589      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29590      */
29591     resizeTabs : false,
29592     /*
29593      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29594      */
29595     monitorResize : true,
29596     /*
29597      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29598      */
29599     toolbar : false,
29600
29601     /**
29602      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29603      * @param {String} id The id of the div to use <b>or create</b>
29604      * @param {String} text The text for the tab
29605      * @param {String} content (optional) Content to put in the TabPanelItem body
29606      * @param {Boolean} closable (optional) True to create a close icon on the tab
29607      * @return {Roo.TabPanelItem} The created TabPanelItem
29608      */
29609     addTab : function(id, text, content, closable){
29610         var item = new Roo.TabPanelItem(this, id, text, closable);
29611         this.addTabItem(item);
29612         if(content){
29613             item.setContent(content);
29614         }
29615         return item;
29616     },
29617
29618     /**
29619      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29620      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29621      * @return {Roo.TabPanelItem}
29622      */
29623     getTab : function(id){
29624         return this.items[id];
29625     },
29626
29627     /**
29628      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29629      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29630      */
29631     hideTab : function(id){
29632         var t = this.items[id];
29633         if(!t.isHidden()){
29634            t.setHidden(true);
29635            this.hiddenCount++;
29636            this.autoSizeTabs();
29637         }
29638     },
29639
29640     /**
29641      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29642      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29643      */
29644     unhideTab : function(id){
29645         var t = this.items[id];
29646         if(t.isHidden()){
29647            t.setHidden(false);
29648            this.hiddenCount--;
29649            this.autoSizeTabs();
29650         }
29651     },
29652
29653     /**
29654      * Adds an existing {@link Roo.TabPanelItem}.
29655      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29656      */
29657     addTabItem : function(item){
29658         this.items[item.id] = item;
29659         this.items.push(item);
29660         if(this.resizeTabs){
29661            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29662            this.autoSizeTabs();
29663         }else{
29664             item.autoSize();
29665         }
29666     },
29667
29668     /**
29669      * Removes a {@link Roo.TabPanelItem}.
29670      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29671      */
29672     removeTab : function(id){
29673         var items = this.items;
29674         var tab = items[id];
29675         if(!tab) { return; }
29676         var index = items.indexOf(tab);
29677         if(this.active == tab && items.length > 1){
29678             var newTab = this.getNextAvailable(index);
29679             if(newTab) {
29680                 newTab.activate();
29681             }
29682         }
29683         this.stripEl.dom.removeChild(tab.pnode.dom);
29684         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29685             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29686         }
29687         items.splice(index, 1);
29688         delete this.items[tab.id];
29689         tab.fireEvent("close", tab);
29690         tab.purgeListeners();
29691         this.autoSizeTabs();
29692     },
29693
29694     getNextAvailable : function(start){
29695         var items = this.items;
29696         var index = start;
29697         // look for a next tab that will slide over to
29698         // replace the one being removed
29699         while(index < items.length){
29700             var item = items[++index];
29701             if(item && !item.isHidden()){
29702                 return item;
29703             }
29704         }
29705         // if one isn't found select the previous tab (on the left)
29706         index = start;
29707         while(index >= 0){
29708             var item = items[--index];
29709             if(item && !item.isHidden()){
29710                 return item;
29711             }
29712         }
29713         return null;
29714     },
29715
29716     /**
29717      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29718      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29719      */
29720     disableTab : function(id){
29721         var tab = this.items[id];
29722         if(tab && this.active != tab){
29723             tab.disable();
29724         }
29725     },
29726
29727     /**
29728      * Enables a {@link Roo.TabPanelItem} that is disabled.
29729      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29730      */
29731     enableTab : function(id){
29732         var tab = this.items[id];
29733         tab.enable();
29734     },
29735
29736     /**
29737      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29738      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29739      * @return {Roo.TabPanelItem} The TabPanelItem.
29740      */
29741     activate : function(id){
29742         var tab = this.items[id];
29743         if(!tab){
29744             return null;
29745         }
29746         if(tab == this.active || tab.disabled){
29747             return tab;
29748         }
29749         var e = {};
29750         this.fireEvent("beforetabchange", this, e, tab);
29751         if(e.cancel !== true && !tab.disabled){
29752             if(this.active){
29753                 this.active.hide();
29754             }
29755             this.active = this.items[id];
29756             this.active.show();
29757             this.fireEvent("tabchange", this, this.active);
29758         }
29759         return tab;
29760     },
29761
29762     /**
29763      * Gets the active {@link Roo.TabPanelItem}.
29764      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29765      */
29766     getActiveTab : function(){
29767         return this.active;
29768     },
29769
29770     /**
29771      * Updates the tab body element to fit the height of the container element
29772      * for overflow scrolling
29773      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29774      */
29775     syncHeight : function(targetHeight){
29776         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29777         var bm = this.bodyEl.getMargins();
29778         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29779         this.bodyEl.setHeight(newHeight);
29780         return newHeight;
29781     },
29782
29783     onResize : function(){
29784         if(this.monitorResize){
29785             this.autoSizeTabs();
29786         }
29787     },
29788
29789     /**
29790      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29791      */
29792     beginUpdate : function(){
29793         this.updating = true;
29794     },
29795
29796     /**
29797      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29798      */
29799     endUpdate : function(){
29800         this.updating = false;
29801         this.autoSizeTabs();
29802     },
29803
29804     /**
29805      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29806      */
29807     autoSizeTabs : function(){
29808         var count = this.items.length;
29809         var vcount = count - this.hiddenCount;
29810         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29811             return;
29812         }
29813         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29814         var availWidth = Math.floor(w / vcount);
29815         var b = this.stripBody;
29816         if(b.getWidth() > w){
29817             var tabs = this.items;
29818             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29819             if(availWidth < this.minTabWidth){
29820                 /*if(!this.sleft){    // incomplete scrolling code
29821                     this.createScrollButtons();
29822                 }
29823                 this.showScroll();
29824                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29825             }
29826         }else{
29827             if(this.currentTabWidth < this.preferredTabWidth){
29828                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29829             }
29830         }
29831     },
29832
29833     /**
29834      * Returns the number of tabs in this TabPanel.
29835      * @return {Number}
29836      */
29837      getCount : function(){
29838          return this.items.length;
29839      },
29840
29841     /**
29842      * Resizes all the tabs to the passed width
29843      * @param {Number} The new width
29844      */
29845     setTabWidth : function(width){
29846         this.currentTabWidth = width;
29847         for(var i = 0, len = this.items.length; i < len; i++) {
29848                 if(!this.items[i].isHidden()) {
29849                 this.items[i].setWidth(width);
29850             }
29851         }
29852     },
29853
29854     /**
29855      * Destroys this TabPanel
29856      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29857      */
29858     destroy : function(removeEl){
29859         Roo.EventManager.removeResizeListener(this.onResize, this);
29860         for(var i = 0, len = this.items.length; i < len; i++){
29861             this.items[i].purgeListeners();
29862         }
29863         if(removeEl === true){
29864             this.el.update("");
29865             this.el.remove();
29866         }
29867     }
29868 });
29869
29870 /**
29871  * @class Roo.TabPanelItem
29872  * @extends Roo.util.Observable
29873  * Represents an individual item (tab plus body) in a TabPanel.
29874  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29875  * @param {String} id The id of this TabPanelItem
29876  * @param {String} text The text for the tab of this TabPanelItem
29877  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29878  */
29879 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29880     /**
29881      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29882      * @type Roo.TabPanel
29883      */
29884     this.tabPanel = tabPanel;
29885     /**
29886      * The id for this TabPanelItem
29887      * @type String
29888      */
29889     this.id = id;
29890     /** @private */
29891     this.disabled = false;
29892     /** @private */
29893     this.text = text;
29894     /** @private */
29895     this.loaded = false;
29896     this.closable = closable;
29897
29898     /**
29899      * The body element for this TabPanelItem.
29900      * @type Roo.Element
29901      */
29902     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29903     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29904     this.bodyEl.setStyle("display", "block");
29905     this.bodyEl.setStyle("zoom", "1");
29906     this.hideAction();
29907
29908     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29909     /** @private */
29910     this.el = Roo.get(els.el, true);
29911     this.inner = Roo.get(els.inner, true);
29912     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29913     this.pnode = Roo.get(els.el.parentNode, true);
29914     this.el.on("mousedown", this.onTabMouseDown, this);
29915     this.el.on("click", this.onTabClick, this);
29916     /** @private */
29917     if(closable){
29918         var c = Roo.get(els.close, true);
29919         c.dom.title = this.closeText;
29920         c.addClassOnOver("close-over");
29921         c.on("click", this.closeClick, this);
29922      }
29923
29924     this.addEvents({
29925          /**
29926          * @event activate
29927          * Fires when this tab becomes the active tab.
29928          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29929          * @param {Roo.TabPanelItem} this
29930          */
29931         "activate": true,
29932         /**
29933          * @event beforeclose
29934          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29935          * @param {Roo.TabPanelItem} this
29936          * @param {Object} e Set cancel to true on this object to cancel the close.
29937          */
29938         "beforeclose": true,
29939         /**
29940          * @event close
29941          * Fires when this tab is closed.
29942          * @param {Roo.TabPanelItem} this
29943          */
29944          "close": true,
29945         /**
29946          * @event deactivate
29947          * Fires when this tab is no longer the active tab.
29948          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29949          * @param {Roo.TabPanelItem} this
29950          */
29951          "deactivate" : true
29952     });
29953     this.hidden = false;
29954
29955     Roo.TabPanelItem.superclass.constructor.call(this);
29956 };
29957
29958 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29959     purgeListeners : function(){
29960        Roo.util.Observable.prototype.purgeListeners.call(this);
29961        this.el.removeAllListeners();
29962     },
29963     /**
29964      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29965      */
29966     show : function(){
29967         this.pnode.addClass("on");
29968         this.showAction();
29969         if(Roo.isOpera){
29970             this.tabPanel.stripWrap.repaint();
29971         }
29972         this.fireEvent("activate", this.tabPanel, this);
29973     },
29974
29975     /**
29976      * Returns true if this tab is the active tab.
29977      * @return {Boolean}
29978      */
29979     isActive : function(){
29980         return this.tabPanel.getActiveTab() == this;
29981     },
29982
29983     /**
29984      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29985      */
29986     hide : function(){
29987         this.pnode.removeClass("on");
29988         this.hideAction();
29989         this.fireEvent("deactivate", this.tabPanel, this);
29990     },
29991
29992     hideAction : function(){
29993         this.bodyEl.hide();
29994         this.bodyEl.setStyle("position", "absolute");
29995         this.bodyEl.setLeft("-20000px");
29996         this.bodyEl.setTop("-20000px");
29997     },
29998
29999     showAction : function(){
30000         this.bodyEl.setStyle("position", "relative");
30001         this.bodyEl.setTop("");
30002         this.bodyEl.setLeft("");
30003         this.bodyEl.show();
30004     },
30005
30006     /**
30007      * Set the tooltip for the tab.
30008      * @param {String} tooltip The tab's tooltip
30009      */
30010     setTooltip : function(text){
30011         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30012             this.textEl.dom.qtip = text;
30013             this.textEl.dom.removeAttribute('title');
30014         }else{
30015             this.textEl.dom.title = text;
30016         }
30017     },
30018
30019     onTabClick : function(e){
30020         e.preventDefault();
30021         this.tabPanel.activate(this.id);
30022     },
30023
30024     onTabMouseDown : function(e){
30025         e.preventDefault();
30026         this.tabPanel.activate(this.id);
30027     },
30028
30029     getWidth : function(){
30030         return this.inner.getWidth();
30031     },
30032
30033     setWidth : function(width){
30034         var iwidth = width - this.pnode.getPadding("lr");
30035         this.inner.setWidth(iwidth);
30036         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30037         this.pnode.setWidth(width);
30038     },
30039
30040     /**
30041      * Show or hide the tab
30042      * @param {Boolean} hidden True to hide or false to show.
30043      */
30044     setHidden : function(hidden){
30045         this.hidden = hidden;
30046         this.pnode.setStyle("display", hidden ? "none" : "");
30047     },
30048
30049     /**
30050      * Returns true if this tab is "hidden"
30051      * @return {Boolean}
30052      */
30053     isHidden : function(){
30054         return this.hidden;
30055     },
30056
30057     /**
30058      * Returns the text for this tab
30059      * @return {String}
30060      */
30061     getText : function(){
30062         return this.text;
30063     },
30064
30065     autoSize : function(){
30066         //this.el.beginMeasure();
30067         this.textEl.setWidth(1);
30068         /*
30069          *  #2804 [new] Tabs in Roojs
30070          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30071          */
30072         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30073         //this.el.endMeasure();
30074     },
30075
30076     /**
30077      * Sets the text for the tab (Note: this also sets the tooltip text)
30078      * @param {String} text The tab's text and tooltip
30079      */
30080     setText : function(text){
30081         this.text = text;
30082         this.textEl.update(text);
30083         this.setTooltip(text);
30084         if(!this.tabPanel.resizeTabs){
30085             this.autoSize();
30086         }
30087     },
30088     /**
30089      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30090      */
30091     activate : function(){
30092         this.tabPanel.activate(this.id);
30093     },
30094
30095     /**
30096      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30097      */
30098     disable : function(){
30099         if(this.tabPanel.active != this){
30100             this.disabled = true;
30101             this.pnode.addClass("disabled");
30102         }
30103     },
30104
30105     /**
30106      * Enables this TabPanelItem if it was previously disabled.
30107      */
30108     enable : function(){
30109         this.disabled = false;
30110         this.pnode.removeClass("disabled");
30111     },
30112
30113     /**
30114      * Sets the content for this TabPanelItem.
30115      * @param {String} content The content
30116      * @param {Boolean} loadScripts true to look for and load scripts
30117      */
30118     setContent : function(content, loadScripts){
30119         this.bodyEl.update(content, loadScripts);
30120     },
30121
30122     /**
30123      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30124      * @return {Roo.UpdateManager} The UpdateManager
30125      */
30126     getUpdateManager : function(){
30127         return this.bodyEl.getUpdateManager();
30128     },
30129
30130     /**
30131      * Set a URL to be used to load the content for this TabPanelItem.
30132      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30133      * @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)
30134      * @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)
30135      * @return {Roo.UpdateManager} The UpdateManager
30136      */
30137     setUrl : function(url, params, loadOnce){
30138         if(this.refreshDelegate){
30139             this.un('activate', this.refreshDelegate);
30140         }
30141         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30142         this.on("activate", this.refreshDelegate);
30143         return this.bodyEl.getUpdateManager();
30144     },
30145
30146     /** @private */
30147     _handleRefresh : function(url, params, loadOnce){
30148         if(!loadOnce || !this.loaded){
30149             var updater = this.bodyEl.getUpdateManager();
30150             updater.update(url, params, this._setLoaded.createDelegate(this));
30151         }
30152     },
30153
30154     /**
30155      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30156      *   Will fail silently if the setUrl method has not been called.
30157      *   This does not activate the panel, just updates its content.
30158      */
30159     refresh : function(){
30160         if(this.refreshDelegate){
30161            this.loaded = false;
30162            this.refreshDelegate();
30163         }
30164     },
30165
30166     /** @private */
30167     _setLoaded : function(){
30168         this.loaded = true;
30169     },
30170
30171     /** @private */
30172     closeClick : function(e){
30173         var o = {};
30174         e.stopEvent();
30175         this.fireEvent("beforeclose", this, o);
30176         if(o.cancel !== true){
30177             this.tabPanel.removeTab(this.id);
30178         }
30179     },
30180     /**
30181      * The text displayed in the tooltip for the close icon.
30182      * @type String
30183      */
30184     closeText : "Close this tab"
30185 });
30186
30187 /** @private */
30188 Roo.TabPanel.prototype.createStrip = function(container){
30189     var strip = document.createElement("div");
30190     strip.className = "x-tabs-wrap";
30191     container.appendChild(strip);
30192     return strip;
30193 };
30194 /** @private */
30195 Roo.TabPanel.prototype.createStripList = function(strip){
30196     // div wrapper for retard IE
30197     // returns the "tr" element.
30198     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30199         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30200         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30201     return strip.firstChild.firstChild.firstChild.firstChild;
30202 };
30203 /** @private */
30204 Roo.TabPanel.prototype.createBody = function(container){
30205     var body = document.createElement("div");
30206     Roo.id(body, "tab-body");
30207     Roo.fly(body).addClass("x-tabs-body");
30208     container.appendChild(body);
30209     return body;
30210 };
30211 /** @private */
30212 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30213     var body = Roo.getDom(id);
30214     if(!body){
30215         body = document.createElement("div");
30216         body.id = id;
30217     }
30218     Roo.fly(body).addClass("x-tabs-item-body");
30219     bodyEl.insertBefore(body, bodyEl.firstChild);
30220     return body;
30221 };
30222 /** @private */
30223 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30224     var td = document.createElement("td");
30225     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30226     //stripEl.appendChild(td);
30227     if(closable){
30228         td.className = "x-tabs-closable";
30229         if(!this.closeTpl){
30230             this.closeTpl = new Roo.Template(
30231                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30232                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30233                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30234             );
30235         }
30236         var el = this.closeTpl.overwrite(td, {"text": text});
30237         var close = el.getElementsByTagName("div")[0];
30238         var inner = el.getElementsByTagName("em")[0];
30239         return {"el": el, "close": close, "inner": inner};
30240     } else {
30241         if(!this.tabTpl){
30242             this.tabTpl = new Roo.Template(
30243                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30244                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30245             );
30246         }
30247         var el = this.tabTpl.overwrite(td, {"text": text});
30248         var inner = el.getElementsByTagName("em")[0];
30249         return {"el": el, "inner": inner};
30250     }
30251 };/*
30252  * Based on:
30253  * Ext JS Library 1.1.1
30254  * Copyright(c) 2006-2007, Ext JS, LLC.
30255  *
30256  * Originally Released Under LGPL - original licence link has changed is not relivant.
30257  *
30258  * Fork - LGPL
30259  * <script type="text/javascript">
30260  */
30261
30262 /**
30263  * @class Roo.Button
30264  * @extends Roo.util.Observable
30265  * Simple Button class
30266  * @cfg {String} text The button text
30267  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30268  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30269  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30270  * @cfg {Object} scope The scope of the handler
30271  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30272  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30273  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30274  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30275  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30276  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30277    applies if enableToggle = true)
30278  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30279  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30280   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30281  * @constructor
30282  * Create a new button
30283  * @param {Object} config The config object
30284  */
30285 Roo.Button = function(renderTo, config)
30286 {
30287     if (!config) {
30288         config = renderTo;
30289         renderTo = config.renderTo || false;
30290     }
30291     
30292     Roo.apply(this, config);
30293     this.addEvents({
30294         /**
30295              * @event click
30296              * Fires when this button is clicked
30297              * @param {Button} this
30298              * @param {EventObject} e The click event
30299              */
30300             "click" : true,
30301         /**
30302              * @event toggle
30303              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30304              * @param {Button} this
30305              * @param {Boolean} pressed
30306              */
30307             "toggle" : true,
30308         /**
30309              * @event mouseover
30310              * Fires when the mouse hovers over the button
30311              * @param {Button} this
30312              * @param {Event} e The event object
30313              */
30314         'mouseover' : true,
30315         /**
30316              * @event mouseout
30317              * Fires when the mouse exits the button
30318              * @param {Button} this
30319              * @param {Event} e The event object
30320              */
30321         'mouseout': true,
30322          /**
30323              * @event render
30324              * Fires when the button is rendered
30325              * @param {Button} this
30326              */
30327         'render': true
30328     });
30329     if(this.menu){
30330         this.menu = Roo.menu.MenuMgr.get(this.menu);
30331     }
30332     // register listeners first!!  - so render can be captured..
30333     Roo.util.Observable.call(this);
30334     if(renderTo){
30335         this.render(renderTo);
30336     }
30337     
30338   
30339 };
30340
30341 Roo.extend(Roo.Button, Roo.util.Observable, {
30342     /**
30343      * 
30344      */
30345     
30346     /**
30347      * Read-only. True if this button is hidden
30348      * @type Boolean
30349      */
30350     hidden : false,
30351     /**
30352      * Read-only. True if this button is disabled
30353      * @type Boolean
30354      */
30355     disabled : false,
30356     /**
30357      * Read-only. True if this button is pressed (only if enableToggle = true)
30358      * @type Boolean
30359      */
30360     pressed : false,
30361
30362     /**
30363      * @cfg {Number} tabIndex 
30364      * The DOM tabIndex for this button (defaults to undefined)
30365      */
30366     tabIndex : undefined,
30367
30368     /**
30369      * @cfg {Boolean} enableToggle
30370      * True to enable pressed/not pressed toggling (defaults to false)
30371      */
30372     enableToggle: false,
30373     /**
30374      * @cfg {Roo.menu.Menu} menu
30375      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30376      */
30377     menu : undefined,
30378     /**
30379      * @cfg {String} menuAlign
30380      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30381      */
30382     menuAlign : "tl-bl?",
30383
30384     /**
30385      * @cfg {String} iconCls
30386      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30387      */
30388     iconCls : undefined,
30389     /**
30390      * @cfg {String} type
30391      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30392      */
30393     type : 'button',
30394
30395     // private
30396     menuClassTarget: 'tr',
30397
30398     /**
30399      * @cfg {String} clickEvent
30400      * The type of event to map to the button's event handler (defaults to 'click')
30401      */
30402     clickEvent : 'click',
30403
30404     /**
30405      * @cfg {Boolean} handleMouseEvents
30406      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30407      */
30408     handleMouseEvents : true,
30409
30410     /**
30411      * @cfg {String} tooltipType
30412      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30413      */
30414     tooltipType : 'qtip',
30415
30416     /**
30417      * @cfg {String} cls
30418      * A CSS class to apply to the button's main element.
30419      */
30420     
30421     /**
30422      * @cfg {Roo.Template} template (Optional)
30423      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30424      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30425      * require code modifications if required elements (e.g. a button) aren't present.
30426      */
30427
30428     // private
30429     render : function(renderTo){
30430         var btn;
30431         if(this.hideParent){
30432             this.parentEl = Roo.get(renderTo);
30433         }
30434         if(!this.dhconfig){
30435             if(!this.template){
30436                 if(!Roo.Button.buttonTemplate){
30437                     // hideous table template
30438                     Roo.Button.buttonTemplate = new Roo.Template(
30439                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30440                         '<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>',
30441                         "</tr></tbody></table>");
30442                 }
30443                 this.template = Roo.Button.buttonTemplate;
30444             }
30445             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30446             var btnEl = btn.child("button:first");
30447             btnEl.on('focus', this.onFocus, this);
30448             btnEl.on('blur', this.onBlur, this);
30449             if(this.cls){
30450                 btn.addClass(this.cls);
30451             }
30452             if(this.icon){
30453                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30454             }
30455             if(this.iconCls){
30456                 btnEl.addClass(this.iconCls);
30457                 if(!this.cls){
30458                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30459                 }
30460             }
30461             if(this.tabIndex !== undefined){
30462                 btnEl.dom.tabIndex = this.tabIndex;
30463             }
30464             if(this.tooltip){
30465                 if(typeof this.tooltip == 'object'){
30466                     Roo.QuickTips.tips(Roo.apply({
30467                           target: btnEl.id
30468                     }, this.tooltip));
30469                 } else {
30470                     btnEl.dom[this.tooltipType] = this.tooltip;
30471                 }
30472             }
30473         }else{
30474             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30475         }
30476         this.el = btn;
30477         if(this.id){
30478             this.el.dom.id = this.el.id = this.id;
30479         }
30480         if(this.menu){
30481             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30482             this.menu.on("show", this.onMenuShow, this);
30483             this.menu.on("hide", this.onMenuHide, this);
30484         }
30485         btn.addClass("x-btn");
30486         if(Roo.isIE && !Roo.isIE7){
30487             this.autoWidth.defer(1, this);
30488         }else{
30489             this.autoWidth();
30490         }
30491         if(this.handleMouseEvents){
30492             btn.on("mouseover", this.onMouseOver, this);
30493             btn.on("mouseout", this.onMouseOut, this);
30494             btn.on("mousedown", this.onMouseDown, this);
30495         }
30496         btn.on(this.clickEvent, this.onClick, this);
30497         //btn.on("mouseup", this.onMouseUp, this);
30498         if(this.hidden){
30499             this.hide();
30500         }
30501         if(this.disabled){
30502             this.disable();
30503         }
30504         Roo.ButtonToggleMgr.register(this);
30505         if(this.pressed){
30506             this.el.addClass("x-btn-pressed");
30507         }
30508         if(this.repeat){
30509             var repeater = new Roo.util.ClickRepeater(btn,
30510                 typeof this.repeat == "object" ? this.repeat : {}
30511             );
30512             repeater.on("click", this.onClick,  this);
30513         }
30514         
30515         this.fireEvent('render', this);
30516         
30517     },
30518     /**
30519      * Returns the button's underlying element
30520      * @return {Roo.Element} The element
30521      */
30522     getEl : function(){
30523         return this.el;  
30524     },
30525     
30526     /**
30527      * Destroys this Button and removes any listeners.
30528      */
30529     destroy : function(){
30530         Roo.ButtonToggleMgr.unregister(this);
30531         this.el.removeAllListeners();
30532         this.purgeListeners();
30533         this.el.remove();
30534     },
30535
30536     // private
30537     autoWidth : function(){
30538         if(this.el){
30539             this.el.setWidth("auto");
30540             if(Roo.isIE7 && Roo.isStrict){
30541                 var ib = this.el.child('button');
30542                 if(ib && ib.getWidth() > 20){
30543                     ib.clip();
30544                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30545                 }
30546             }
30547             if(this.minWidth){
30548                 if(this.hidden){
30549                     this.el.beginMeasure();
30550                 }
30551                 if(this.el.getWidth() < this.minWidth){
30552                     this.el.setWidth(this.minWidth);
30553                 }
30554                 if(this.hidden){
30555                     this.el.endMeasure();
30556                 }
30557             }
30558         }
30559     },
30560
30561     /**
30562      * Assigns this button's click handler
30563      * @param {Function} handler The function to call when the button is clicked
30564      * @param {Object} scope (optional) Scope for the function passed in
30565      */
30566     setHandler : function(handler, scope){
30567         this.handler = handler;
30568         this.scope = scope;  
30569     },
30570     
30571     /**
30572      * Sets this button's text
30573      * @param {String} text The button text
30574      */
30575     setText : function(text){
30576         this.text = text;
30577         if(this.el){
30578             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30579         }
30580         this.autoWidth();
30581     },
30582     
30583     /**
30584      * Gets the text for this button
30585      * @return {String} The button text
30586      */
30587     getText : function(){
30588         return this.text;  
30589     },
30590     
30591     /**
30592      * Show this button
30593      */
30594     show: function(){
30595         this.hidden = false;
30596         if(this.el){
30597             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30598         }
30599     },
30600     
30601     /**
30602      * Hide this button
30603      */
30604     hide: function(){
30605         this.hidden = true;
30606         if(this.el){
30607             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30608         }
30609     },
30610     
30611     /**
30612      * Convenience function for boolean show/hide
30613      * @param {Boolean} visible True to show, false to hide
30614      */
30615     setVisible: function(visible){
30616         if(visible) {
30617             this.show();
30618         }else{
30619             this.hide();
30620         }
30621     },
30622     
30623     /**
30624      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30625      * @param {Boolean} state (optional) Force a particular state
30626      */
30627     toggle : function(state){
30628         state = state === undefined ? !this.pressed : state;
30629         if(state != this.pressed){
30630             if(state){
30631                 this.el.addClass("x-btn-pressed");
30632                 this.pressed = true;
30633                 this.fireEvent("toggle", this, true);
30634             }else{
30635                 this.el.removeClass("x-btn-pressed");
30636                 this.pressed = false;
30637                 this.fireEvent("toggle", this, false);
30638             }
30639             if(this.toggleHandler){
30640                 this.toggleHandler.call(this.scope || this, this, state);
30641             }
30642         }
30643     },
30644     
30645     /**
30646      * Focus the button
30647      */
30648     focus : function(){
30649         this.el.child('button:first').focus();
30650     },
30651     
30652     /**
30653      * Disable this button
30654      */
30655     disable : function(){
30656         if(this.el){
30657             this.el.addClass("x-btn-disabled");
30658         }
30659         this.disabled = true;
30660     },
30661     
30662     /**
30663      * Enable this button
30664      */
30665     enable : function(){
30666         if(this.el){
30667             this.el.removeClass("x-btn-disabled");
30668         }
30669         this.disabled = false;
30670     },
30671
30672     /**
30673      * Convenience function for boolean enable/disable
30674      * @param {Boolean} enabled True to enable, false to disable
30675      */
30676     setDisabled : function(v){
30677         this[v !== true ? "enable" : "disable"]();
30678     },
30679
30680     // private
30681     onClick : function(e)
30682     {
30683         if(e){
30684             e.preventDefault();
30685         }
30686         if(e.button != 0){
30687             return;
30688         }
30689         if(!this.disabled){
30690             if(this.enableToggle){
30691                 this.toggle();
30692             }
30693             if(this.menu && !this.menu.isVisible()){
30694                 this.menu.show(this.el, this.menuAlign);
30695             }
30696             this.fireEvent("click", this, e);
30697             if(this.handler){
30698                 this.el.removeClass("x-btn-over");
30699                 this.handler.call(this.scope || this, this, e);
30700             }
30701         }
30702     },
30703     // private
30704     onMouseOver : function(e){
30705         if(!this.disabled){
30706             this.el.addClass("x-btn-over");
30707             this.fireEvent('mouseover', this, e);
30708         }
30709     },
30710     // private
30711     onMouseOut : function(e){
30712         if(!e.within(this.el,  true)){
30713             this.el.removeClass("x-btn-over");
30714             this.fireEvent('mouseout', this, e);
30715         }
30716     },
30717     // private
30718     onFocus : function(e){
30719         if(!this.disabled){
30720             this.el.addClass("x-btn-focus");
30721         }
30722     },
30723     // private
30724     onBlur : function(e){
30725         this.el.removeClass("x-btn-focus");
30726     },
30727     // private
30728     onMouseDown : function(e){
30729         if(!this.disabled && e.button == 0){
30730             this.el.addClass("x-btn-click");
30731             Roo.get(document).on('mouseup', this.onMouseUp, this);
30732         }
30733     },
30734     // private
30735     onMouseUp : function(e){
30736         if(e.button == 0){
30737             this.el.removeClass("x-btn-click");
30738             Roo.get(document).un('mouseup', this.onMouseUp, this);
30739         }
30740     },
30741     // private
30742     onMenuShow : function(e){
30743         this.el.addClass("x-btn-menu-active");
30744     },
30745     // private
30746     onMenuHide : function(e){
30747         this.el.removeClass("x-btn-menu-active");
30748     }   
30749 });
30750
30751 // Private utility class used by Button
30752 Roo.ButtonToggleMgr = function(){
30753    var groups = {};
30754    
30755    function toggleGroup(btn, state){
30756        if(state){
30757            var g = groups[btn.toggleGroup];
30758            for(var i = 0, l = g.length; i < l; i++){
30759                if(g[i] != btn){
30760                    g[i].toggle(false);
30761                }
30762            }
30763        }
30764    }
30765    
30766    return {
30767        register : function(btn){
30768            if(!btn.toggleGroup){
30769                return;
30770            }
30771            var g = groups[btn.toggleGroup];
30772            if(!g){
30773                g = groups[btn.toggleGroup] = [];
30774            }
30775            g.push(btn);
30776            btn.on("toggle", toggleGroup);
30777        },
30778        
30779        unregister : function(btn){
30780            if(!btn.toggleGroup){
30781                return;
30782            }
30783            var g = groups[btn.toggleGroup];
30784            if(g){
30785                g.remove(btn);
30786                btn.un("toggle", toggleGroup);
30787            }
30788        }
30789    };
30790 }();/*
30791  * Based on:
30792  * Ext JS Library 1.1.1
30793  * Copyright(c) 2006-2007, Ext JS, LLC.
30794  *
30795  * Originally Released Under LGPL - original licence link has changed is not relivant.
30796  *
30797  * Fork - LGPL
30798  * <script type="text/javascript">
30799  */
30800  
30801 /**
30802  * @class Roo.SplitButton
30803  * @extends Roo.Button
30804  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30805  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30806  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30807  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30808  * @cfg {String} arrowTooltip The title attribute of the arrow
30809  * @constructor
30810  * Create a new menu button
30811  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30812  * @param {Object} config The config object
30813  */
30814 Roo.SplitButton = function(renderTo, config){
30815     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30816     /**
30817      * @event arrowclick
30818      * Fires when this button's arrow is clicked
30819      * @param {SplitButton} this
30820      * @param {EventObject} e The click event
30821      */
30822     this.addEvents({"arrowclick":true});
30823 };
30824
30825 Roo.extend(Roo.SplitButton, Roo.Button, {
30826     render : function(renderTo){
30827         // this is one sweet looking template!
30828         var tpl = new Roo.Template(
30829             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30830             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30831             '<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>',
30832             "</tbody></table></td><td>",
30833             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30834             '<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>',
30835             "</tbody></table></td></tr></table>"
30836         );
30837         var btn = tpl.append(renderTo, [this.text, this.type], true);
30838         var btnEl = btn.child("button");
30839         if(this.cls){
30840             btn.addClass(this.cls);
30841         }
30842         if(this.icon){
30843             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30844         }
30845         if(this.iconCls){
30846             btnEl.addClass(this.iconCls);
30847             if(!this.cls){
30848                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30849             }
30850         }
30851         this.el = btn;
30852         if(this.handleMouseEvents){
30853             btn.on("mouseover", this.onMouseOver, this);
30854             btn.on("mouseout", this.onMouseOut, this);
30855             btn.on("mousedown", this.onMouseDown, this);
30856             btn.on("mouseup", this.onMouseUp, this);
30857         }
30858         btn.on(this.clickEvent, this.onClick, this);
30859         if(this.tooltip){
30860             if(typeof this.tooltip == 'object'){
30861                 Roo.QuickTips.tips(Roo.apply({
30862                       target: btnEl.id
30863                 }, this.tooltip));
30864             } else {
30865                 btnEl.dom[this.tooltipType] = this.tooltip;
30866             }
30867         }
30868         if(this.arrowTooltip){
30869             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30870         }
30871         if(this.hidden){
30872             this.hide();
30873         }
30874         if(this.disabled){
30875             this.disable();
30876         }
30877         if(this.pressed){
30878             this.el.addClass("x-btn-pressed");
30879         }
30880         if(Roo.isIE && !Roo.isIE7){
30881             this.autoWidth.defer(1, this);
30882         }else{
30883             this.autoWidth();
30884         }
30885         if(this.menu){
30886             this.menu.on("show", this.onMenuShow, this);
30887             this.menu.on("hide", this.onMenuHide, this);
30888         }
30889         this.fireEvent('render', this);
30890     },
30891
30892     // private
30893     autoWidth : function(){
30894         if(this.el){
30895             var tbl = this.el.child("table:first");
30896             var tbl2 = this.el.child("table:last");
30897             this.el.setWidth("auto");
30898             tbl.setWidth("auto");
30899             if(Roo.isIE7 && Roo.isStrict){
30900                 var ib = this.el.child('button:first');
30901                 if(ib && ib.getWidth() > 20){
30902                     ib.clip();
30903                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30904                 }
30905             }
30906             if(this.minWidth){
30907                 if(this.hidden){
30908                     this.el.beginMeasure();
30909                 }
30910                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30911                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30912                 }
30913                 if(this.hidden){
30914                     this.el.endMeasure();
30915                 }
30916             }
30917             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30918         } 
30919     },
30920     /**
30921      * Sets this button's click handler
30922      * @param {Function} handler The function to call when the button is clicked
30923      * @param {Object} scope (optional) Scope for the function passed above
30924      */
30925     setHandler : function(handler, scope){
30926         this.handler = handler;
30927         this.scope = scope;  
30928     },
30929     
30930     /**
30931      * Sets this button's arrow click handler
30932      * @param {Function} handler The function to call when the arrow is clicked
30933      * @param {Object} scope (optional) Scope for the function passed above
30934      */
30935     setArrowHandler : function(handler, scope){
30936         this.arrowHandler = handler;
30937         this.scope = scope;  
30938     },
30939     
30940     /**
30941      * Focus the button
30942      */
30943     focus : function(){
30944         if(this.el){
30945             this.el.child("button:first").focus();
30946         }
30947     },
30948
30949     // private
30950     onClick : function(e){
30951         e.preventDefault();
30952         if(!this.disabled){
30953             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30954                 if(this.menu && !this.menu.isVisible()){
30955                     this.menu.show(this.el, this.menuAlign);
30956                 }
30957                 this.fireEvent("arrowclick", this, e);
30958                 if(this.arrowHandler){
30959                     this.arrowHandler.call(this.scope || this, this, e);
30960                 }
30961             }else{
30962                 this.fireEvent("click", this, e);
30963                 if(this.handler){
30964                     this.handler.call(this.scope || this, this, e);
30965                 }
30966             }
30967         }
30968     },
30969     // private
30970     onMouseDown : function(e){
30971         if(!this.disabled){
30972             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30973         }
30974     },
30975     // private
30976     onMouseUp : function(e){
30977         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30978     }   
30979 });
30980
30981
30982 // backwards compat
30983 Roo.MenuButton = Roo.SplitButton;/*
30984  * Based on:
30985  * Ext JS Library 1.1.1
30986  * Copyright(c) 2006-2007, Ext JS, LLC.
30987  *
30988  * Originally Released Under LGPL - original licence link has changed is not relivant.
30989  *
30990  * Fork - LGPL
30991  * <script type="text/javascript">
30992  */
30993
30994 /**
30995  * @class Roo.Toolbar
30996  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
30997  * Basic Toolbar class.
30998  * @constructor
30999  * Creates a new Toolbar
31000  * @param {Object} container The config object
31001  */ 
31002 Roo.Toolbar = function(container, buttons, config)
31003 {
31004     /// old consturctor format still supported..
31005     if(container instanceof Array){ // omit the container for later rendering
31006         buttons = container;
31007         config = buttons;
31008         container = null;
31009     }
31010     if (typeof(container) == 'object' && container.xtype) {
31011         config = container;
31012         container = config.container;
31013         buttons = config.buttons || []; // not really - use items!!
31014     }
31015     var xitems = [];
31016     if (config && config.items) {
31017         xitems = config.items;
31018         delete config.items;
31019     }
31020     Roo.apply(this, config);
31021     this.buttons = buttons;
31022     
31023     if(container){
31024         this.render(container);
31025     }
31026     this.xitems = xitems;
31027     Roo.each(xitems, function(b) {
31028         this.add(b);
31029     }, this);
31030     
31031 };
31032
31033 Roo.Toolbar.prototype = {
31034     /**
31035      * @cfg {Array} items
31036      * array of button configs or elements to add (will be converted to a MixedCollection)
31037      */
31038     items: false,
31039     /**
31040      * @cfg {String/HTMLElement/Element} container
31041      * The id or element that will contain the toolbar
31042      */
31043     // private
31044     render : function(ct){
31045         this.el = Roo.get(ct);
31046         if(this.cls){
31047             this.el.addClass(this.cls);
31048         }
31049         // using a table allows for vertical alignment
31050         // 100% width is needed by Safari...
31051         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31052         this.tr = this.el.child("tr", true);
31053         var autoId = 0;
31054         this.items = new Roo.util.MixedCollection(false, function(o){
31055             return o.id || ("item" + (++autoId));
31056         });
31057         if(this.buttons){
31058             this.add.apply(this, this.buttons);
31059             delete this.buttons;
31060         }
31061     },
31062
31063     /**
31064      * Adds element(s) to the toolbar -- this function takes a variable number of 
31065      * arguments of mixed type and adds them to the toolbar.
31066      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31067      * <ul>
31068      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31069      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31070      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31071      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31072      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31073      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31074      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31075      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31076      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31077      * </ul>
31078      * @param {Mixed} arg2
31079      * @param {Mixed} etc.
31080      */
31081     add : function(){
31082         var a = arguments, l = a.length;
31083         for(var i = 0; i < l; i++){
31084             this._add(a[i]);
31085         }
31086     },
31087     // private..
31088     _add : function(el) {
31089         
31090         if (el.xtype) {
31091             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31092         }
31093         
31094         if (el.applyTo){ // some kind of form field
31095             return this.addField(el);
31096         } 
31097         if (el.render){ // some kind of Toolbar.Item
31098             return this.addItem(el);
31099         }
31100         if (typeof el == "string"){ // string
31101             if(el == "separator" || el == "-"){
31102                 return this.addSeparator();
31103             }
31104             if (el == " "){
31105                 return this.addSpacer();
31106             }
31107             if(el == "->"){
31108                 return this.addFill();
31109             }
31110             return this.addText(el);
31111             
31112         }
31113         if(el.tagName){ // element
31114             return this.addElement(el);
31115         }
31116         if(typeof el == "object"){ // must be button config?
31117             return this.addButton(el);
31118         }
31119         // and now what?!?!
31120         return false;
31121         
31122     },
31123     
31124     /**
31125      * Add an Xtype element
31126      * @param {Object} xtype Xtype Object
31127      * @return {Object} created Object
31128      */
31129     addxtype : function(e){
31130         return this.add(e);  
31131     },
31132     
31133     /**
31134      * Returns the Element for this toolbar.
31135      * @return {Roo.Element}
31136      */
31137     getEl : function(){
31138         return this.el;  
31139     },
31140     
31141     /**
31142      * Adds a separator
31143      * @return {Roo.Toolbar.Item} The separator item
31144      */
31145     addSeparator : function(){
31146         return this.addItem(new Roo.Toolbar.Separator());
31147     },
31148
31149     /**
31150      * Adds a spacer element
31151      * @return {Roo.Toolbar.Spacer} The spacer item
31152      */
31153     addSpacer : function(){
31154         return this.addItem(new Roo.Toolbar.Spacer());
31155     },
31156
31157     /**
31158      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31159      * @return {Roo.Toolbar.Fill} The fill item
31160      */
31161     addFill : function(){
31162         return this.addItem(new Roo.Toolbar.Fill());
31163     },
31164
31165     /**
31166      * Adds any standard HTML element to the toolbar
31167      * @param {String/HTMLElement/Element} el The element or id of the element to add
31168      * @return {Roo.Toolbar.Item} The element's item
31169      */
31170     addElement : function(el){
31171         return this.addItem(new Roo.Toolbar.Item(el));
31172     },
31173     /**
31174      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31175      * @type Roo.util.MixedCollection  
31176      */
31177     items : false,
31178      
31179     /**
31180      * Adds any Toolbar.Item or subclass
31181      * @param {Roo.Toolbar.Item} item
31182      * @return {Roo.Toolbar.Item} The item
31183      */
31184     addItem : function(item){
31185         var td = this.nextBlock();
31186         item.render(td);
31187         this.items.add(item);
31188         return item;
31189     },
31190     
31191     /**
31192      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31193      * @param {Object/Array} config A button config or array of configs
31194      * @return {Roo.Toolbar.Button/Array}
31195      */
31196     addButton : function(config){
31197         if(config instanceof Array){
31198             var buttons = [];
31199             for(var i = 0, len = config.length; i < len; i++) {
31200                 buttons.push(this.addButton(config[i]));
31201             }
31202             return buttons;
31203         }
31204         var b = config;
31205         if(!(config instanceof Roo.Toolbar.Button)){
31206             b = config.split ?
31207                 new Roo.Toolbar.SplitButton(config) :
31208                 new Roo.Toolbar.Button(config);
31209         }
31210         var td = this.nextBlock();
31211         b.render(td);
31212         this.items.add(b);
31213         return b;
31214     },
31215     
31216     /**
31217      * Adds text to the toolbar
31218      * @param {String} text The text to add
31219      * @return {Roo.Toolbar.Item} The element's item
31220      */
31221     addText : function(text){
31222         return this.addItem(new Roo.Toolbar.TextItem(text));
31223     },
31224     
31225     /**
31226      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31227      * @param {Number} index The index where the item is to be inserted
31228      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31229      * @return {Roo.Toolbar.Button/Item}
31230      */
31231     insertButton : function(index, item){
31232         if(item instanceof Array){
31233             var buttons = [];
31234             for(var i = 0, len = item.length; i < len; i++) {
31235                buttons.push(this.insertButton(index + i, item[i]));
31236             }
31237             return buttons;
31238         }
31239         if (!(item instanceof Roo.Toolbar.Button)){
31240            item = new Roo.Toolbar.Button(item);
31241         }
31242         var td = document.createElement("td");
31243         this.tr.insertBefore(td, this.tr.childNodes[index]);
31244         item.render(td);
31245         this.items.insert(index, item);
31246         return item;
31247     },
31248     
31249     /**
31250      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31251      * @param {Object} config
31252      * @return {Roo.Toolbar.Item} The element's item
31253      */
31254     addDom : function(config, returnEl){
31255         var td = this.nextBlock();
31256         Roo.DomHelper.overwrite(td, config);
31257         var ti = new Roo.Toolbar.Item(td.firstChild);
31258         ti.render(td);
31259         this.items.add(ti);
31260         return ti;
31261     },
31262
31263     /**
31264      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31265      * @type Roo.util.MixedCollection  
31266      */
31267     fields : false,
31268     
31269     /**
31270      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31271      * Note: the field should not have been rendered yet. For a field that has already been
31272      * rendered, use {@link #addElement}.
31273      * @param {Roo.form.Field} field
31274      * @return {Roo.ToolbarItem}
31275      */
31276      
31277       
31278     addField : function(field) {
31279         if (!this.fields) {
31280             var autoId = 0;
31281             this.fields = new Roo.util.MixedCollection(false, function(o){
31282                 return o.id || ("item" + (++autoId));
31283             });
31284
31285         }
31286         
31287         var td = this.nextBlock();
31288         field.render(td);
31289         var ti = new Roo.Toolbar.Item(td.firstChild);
31290         ti.render(td);
31291         this.items.add(ti);
31292         this.fields.add(field);
31293         return ti;
31294     },
31295     /**
31296      * Hide the toolbar
31297      * @method hide
31298      */
31299      
31300       
31301     hide : function()
31302     {
31303         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31304         this.el.child('div').hide();
31305     },
31306     /**
31307      * Show the toolbar
31308      * @method show
31309      */
31310     show : function()
31311     {
31312         this.el.child('div').show();
31313     },
31314       
31315     // private
31316     nextBlock : function(){
31317         var td = document.createElement("td");
31318         this.tr.appendChild(td);
31319         return td;
31320     },
31321
31322     // private
31323     destroy : function(){
31324         if(this.items){ // rendered?
31325             Roo.destroy.apply(Roo, this.items.items);
31326         }
31327         if(this.fields){ // rendered?
31328             Roo.destroy.apply(Roo, this.fields.items);
31329         }
31330         Roo.Element.uncache(this.el, this.tr);
31331     }
31332 };
31333
31334 /**
31335  * @class Roo.Toolbar.Item
31336  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31337  * @constructor
31338  * Creates a new Item
31339  * @param {HTMLElement} el 
31340  */
31341 Roo.Toolbar.Item = function(el){
31342     var cfg = {};
31343     if (typeof (el.xtype) != 'undefined') {
31344         cfg = el;
31345         el = cfg.el;
31346     }
31347     
31348     this.el = Roo.getDom(el);
31349     this.id = Roo.id(this.el);
31350     this.hidden = false;
31351     
31352     this.addEvents({
31353          /**
31354              * @event render
31355              * Fires when the button is rendered
31356              * @param {Button} this
31357              */
31358         'render': true
31359     });
31360     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31361 };
31362 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31363 //Roo.Toolbar.Item.prototype = {
31364     
31365     /**
31366      * Get this item's HTML Element
31367      * @return {HTMLElement}
31368      */
31369     getEl : function(){
31370        return this.el;  
31371     },
31372
31373     // private
31374     render : function(td){
31375         
31376          this.td = td;
31377         td.appendChild(this.el);
31378         
31379         this.fireEvent('render', this);
31380     },
31381     
31382     /**
31383      * Removes and destroys this item.
31384      */
31385     destroy : function(){
31386         this.td.parentNode.removeChild(this.td);
31387     },
31388     
31389     /**
31390      * Shows this item.
31391      */
31392     show: function(){
31393         this.hidden = false;
31394         this.td.style.display = "";
31395     },
31396     
31397     /**
31398      * Hides this item.
31399      */
31400     hide: function(){
31401         this.hidden = true;
31402         this.td.style.display = "none";
31403     },
31404     
31405     /**
31406      * Convenience function for boolean show/hide.
31407      * @param {Boolean} visible true to show/false to hide
31408      */
31409     setVisible: function(visible){
31410         if(visible) {
31411             this.show();
31412         }else{
31413             this.hide();
31414         }
31415     },
31416     
31417     /**
31418      * Try to focus this item.
31419      */
31420     focus : function(){
31421         Roo.fly(this.el).focus();
31422     },
31423     
31424     /**
31425      * Disables this item.
31426      */
31427     disable : function(){
31428         Roo.fly(this.td).addClass("x-item-disabled");
31429         this.disabled = true;
31430         this.el.disabled = true;
31431     },
31432     
31433     /**
31434      * Enables this item.
31435      */
31436     enable : function(){
31437         Roo.fly(this.td).removeClass("x-item-disabled");
31438         this.disabled = false;
31439         this.el.disabled = false;
31440     }
31441 });
31442
31443
31444 /**
31445  * @class Roo.Toolbar.Separator
31446  * @extends Roo.Toolbar.Item
31447  * A simple toolbar separator class
31448  * @constructor
31449  * Creates a new Separator
31450  */
31451 Roo.Toolbar.Separator = function(cfg){
31452     
31453     var s = document.createElement("span");
31454     s.className = "ytb-sep";
31455     if (cfg) {
31456         cfg.el = s;
31457     }
31458     
31459     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31460 };
31461 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31462     enable:Roo.emptyFn,
31463     disable:Roo.emptyFn,
31464     focus:Roo.emptyFn
31465 });
31466
31467 /**
31468  * @class Roo.Toolbar.Spacer
31469  * @extends Roo.Toolbar.Item
31470  * A simple element that adds extra horizontal space to a toolbar.
31471  * @constructor
31472  * Creates a new Spacer
31473  */
31474 Roo.Toolbar.Spacer = function(cfg){
31475     var s = document.createElement("div");
31476     s.className = "ytb-spacer";
31477     if (cfg) {
31478         cfg.el = s;
31479     }
31480     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31481 };
31482 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31483     enable:Roo.emptyFn,
31484     disable:Roo.emptyFn,
31485     focus:Roo.emptyFn
31486 });
31487
31488 /**
31489  * @class Roo.Toolbar.Fill
31490  * @extends Roo.Toolbar.Spacer
31491  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31492  * @constructor
31493  * Creates a new Spacer
31494  */
31495 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31496     // private
31497     render : function(td){
31498         td.style.width = '100%';
31499         Roo.Toolbar.Fill.superclass.render.call(this, td);
31500     }
31501 });
31502
31503 /**
31504  * @class Roo.Toolbar.TextItem
31505  * @extends Roo.Toolbar.Item
31506  * A simple class that renders text directly into a toolbar.
31507  * @constructor
31508  * Creates a new TextItem
31509  * @cfg {string} text 
31510  */
31511 Roo.Toolbar.TextItem = function(cfg){
31512     var  text = cfg || "";
31513     if (typeof(cfg) == 'object') {
31514         text = cfg.text || "";
31515     }  else {
31516         cfg = null;
31517     }
31518     var s = document.createElement("span");
31519     s.className = "ytb-text";
31520     s.innerHTML = text;
31521     if (cfg) {
31522         cfg.el  = s;
31523     }
31524     
31525     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31526 };
31527 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31528     
31529      
31530     enable:Roo.emptyFn,
31531     disable:Roo.emptyFn,
31532     focus:Roo.emptyFn,
31533      /**
31534      * Shows this button
31535      */
31536     show: function(){
31537         this.hidden = false;
31538         this.el.style.display = "";
31539     },
31540     
31541     /**
31542      * Hides this button
31543      */
31544     hide: function(){
31545         this.hidden = true;
31546         this.el.style.display = "none";
31547     }
31548     
31549 });
31550
31551 /**
31552  * @class Roo.Toolbar.Button
31553  * @extends Roo.Button
31554  * A button that renders into a toolbar.
31555  * @constructor
31556  * Creates a new Button
31557  * @param {Object} config A standard {@link Roo.Button} config object
31558  */
31559 Roo.Toolbar.Button = function(config){
31560     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31561 };
31562 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31563 {
31564     
31565     
31566     render : function(td){
31567         this.td = td;
31568         Roo.Toolbar.Button.superclass.render.call(this, td);
31569     },
31570     
31571     /**
31572      * Removes and destroys this button
31573      */
31574     destroy : function(){
31575         Roo.Toolbar.Button.superclass.destroy.call(this);
31576         this.td.parentNode.removeChild(this.td);
31577     },
31578     
31579     /**
31580      * Shows this button
31581      */
31582     show: function(){
31583         this.hidden = false;
31584         this.td.style.display = "";
31585     },
31586     
31587     /**
31588      * Hides this button
31589      */
31590     hide: function(){
31591         this.hidden = true;
31592         this.td.style.display = "none";
31593     },
31594
31595     /**
31596      * Disables this item
31597      */
31598     disable : function(){
31599         Roo.fly(this.td).addClass("x-item-disabled");
31600         this.disabled = true;
31601     },
31602
31603     /**
31604      * Enables this item
31605      */
31606     enable : function(){
31607         Roo.fly(this.td).removeClass("x-item-disabled");
31608         this.disabled = false;
31609     }
31610 });
31611 // backwards compat
31612 Roo.ToolbarButton = Roo.Toolbar.Button;
31613
31614 /**
31615  * @class Roo.Toolbar.SplitButton
31616  * @extends Roo.SplitButton
31617  * A menu button that renders into a toolbar.
31618  * @constructor
31619  * Creates a new SplitButton
31620  * @param {Object} config A standard {@link Roo.SplitButton} config object
31621  */
31622 Roo.Toolbar.SplitButton = function(config){
31623     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31624 };
31625 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31626     render : function(td){
31627         this.td = td;
31628         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31629     },
31630     
31631     /**
31632      * Removes and destroys this button
31633      */
31634     destroy : function(){
31635         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31636         this.td.parentNode.removeChild(this.td);
31637     },
31638     
31639     /**
31640      * Shows this button
31641      */
31642     show: function(){
31643         this.hidden = false;
31644         this.td.style.display = "";
31645     },
31646     
31647     /**
31648      * Hides this button
31649      */
31650     hide: function(){
31651         this.hidden = true;
31652         this.td.style.display = "none";
31653     }
31654 });
31655
31656 // backwards compat
31657 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31658  * Based on:
31659  * Ext JS Library 1.1.1
31660  * Copyright(c) 2006-2007, Ext JS, LLC.
31661  *
31662  * Originally Released Under LGPL - original licence link has changed is not relivant.
31663  *
31664  * Fork - LGPL
31665  * <script type="text/javascript">
31666  */
31667  
31668 /**
31669  * @class Roo.PagingToolbar
31670  * @extends Roo.Toolbar
31671  * @children   Roo.Toolbar.Item Roo.form.Field
31672  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31673  * @constructor
31674  * Create a new PagingToolbar
31675  * @param {Object} config The config object
31676  */
31677 Roo.PagingToolbar = function(el, ds, config)
31678 {
31679     // old args format still supported... - xtype is prefered..
31680     if (typeof(el) == 'object' && el.xtype) {
31681         // created from xtype...
31682         config = el;
31683         ds = el.dataSource;
31684         el = config.container;
31685     }
31686     var items = [];
31687     if (config.items) {
31688         items = config.items;
31689         config.items = [];
31690     }
31691     
31692     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31693     this.ds = ds;
31694     this.cursor = 0;
31695     this.renderButtons(this.el);
31696     this.bind(ds);
31697     
31698     // supprot items array.
31699    
31700     Roo.each(items, function(e) {
31701         this.add(Roo.factory(e));
31702     },this);
31703     
31704 };
31705
31706 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31707    
31708     /**
31709      * @cfg {String/HTMLElement/Element} container
31710      * container The id or element that will contain the toolbar
31711      */
31712     /**
31713      * @cfg {Boolean} displayInfo
31714      * True to display the displayMsg (defaults to false)
31715      */
31716     
31717     
31718     /**
31719      * @cfg {Number} pageSize
31720      * The number of records to display per page (defaults to 20)
31721      */
31722     pageSize: 20,
31723     /**
31724      * @cfg {String} displayMsg
31725      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31726      */
31727     displayMsg : 'Displaying {0} - {1} of {2}',
31728     /**
31729      * @cfg {String} emptyMsg
31730      * The message to display when no records are found (defaults to "No data to display")
31731      */
31732     emptyMsg : 'No data to display',
31733     /**
31734      * Customizable piece of the default paging text (defaults to "Page")
31735      * @type String
31736      */
31737     beforePageText : "Page",
31738     /**
31739      * Customizable piece of the default paging text (defaults to "of %0")
31740      * @type String
31741      */
31742     afterPageText : "of {0}",
31743     /**
31744      * Customizable piece of the default paging text (defaults to "First Page")
31745      * @type String
31746      */
31747     firstText : "First Page",
31748     /**
31749      * Customizable piece of the default paging text (defaults to "Previous Page")
31750      * @type String
31751      */
31752     prevText : "Previous Page",
31753     /**
31754      * Customizable piece of the default paging text (defaults to "Next Page")
31755      * @type String
31756      */
31757     nextText : "Next Page",
31758     /**
31759      * Customizable piece of the default paging text (defaults to "Last Page")
31760      * @type String
31761      */
31762     lastText : "Last Page",
31763     /**
31764      * Customizable piece of the default paging text (defaults to "Refresh")
31765      * @type String
31766      */
31767     refreshText : "Refresh",
31768
31769     // private
31770     renderButtons : function(el){
31771         Roo.PagingToolbar.superclass.render.call(this, el);
31772         this.first = this.addButton({
31773             tooltip: this.firstText,
31774             cls: "x-btn-icon x-grid-page-first",
31775             disabled: true,
31776             handler: this.onClick.createDelegate(this, ["first"])
31777         });
31778         this.prev = this.addButton({
31779             tooltip: this.prevText,
31780             cls: "x-btn-icon x-grid-page-prev",
31781             disabled: true,
31782             handler: this.onClick.createDelegate(this, ["prev"])
31783         });
31784         //this.addSeparator();
31785         this.add(this.beforePageText);
31786         this.field = Roo.get(this.addDom({
31787            tag: "input",
31788            type: "text",
31789            size: "3",
31790            value: "1",
31791            cls: "x-grid-page-number"
31792         }).el);
31793         this.field.on("keydown", this.onPagingKeydown, this);
31794         this.field.on("focus", function(){this.dom.select();});
31795         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31796         this.field.setHeight(18);
31797         //this.addSeparator();
31798         this.next = this.addButton({
31799             tooltip: this.nextText,
31800             cls: "x-btn-icon x-grid-page-next",
31801             disabled: true,
31802             handler: this.onClick.createDelegate(this, ["next"])
31803         });
31804         this.last = this.addButton({
31805             tooltip: this.lastText,
31806             cls: "x-btn-icon x-grid-page-last",
31807             disabled: true,
31808             handler: this.onClick.createDelegate(this, ["last"])
31809         });
31810         //this.addSeparator();
31811         this.loading = this.addButton({
31812             tooltip: this.refreshText,
31813             cls: "x-btn-icon x-grid-loading",
31814             handler: this.onClick.createDelegate(this, ["refresh"])
31815         });
31816
31817         if(this.displayInfo){
31818             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31819         }
31820     },
31821
31822     // private
31823     updateInfo : function(){
31824         if(this.displayEl){
31825             var count = this.ds.getCount();
31826             var msg = count == 0 ?
31827                 this.emptyMsg :
31828                 String.format(
31829                     this.displayMsg,
31830                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31831                 );
31832             this.displayEl.update(msg);
31833         }
31834     },
31835
31836     // private
31837     onLoad : function(ds, r, o){
31838        this.cursor = o.params ? o.params.start : 0;
31839        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31840
31841        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31842        this.field.dom.value = ap;
31843        this.first.setDisabled(ap == 1);
31844        this.prev.setDisabled(ap == 1);
31845        this.next.setDisabled(ap == ps);
31846        this.last.setDisabled(ap == ps);
31847        this.loading.enable();
31848        this.updateInfo();
31849     },
31850
31851     // private
31852     getPageData : function(){
31853         var total = this.ds.getTotalCount();
31854         return {
31855             total : total,
31856             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31857             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31858         };
31859     },
31860
31861     // private
31862     onLoadError : function(){
31863         this.loading.enable();
31864     },
31865
31866     // private
31867     onPagingKeydown : function(e){
31868         var k = e.getKey();
31869         var d = this.getPageData();
31870         if(k == e.RETURN){
31871             var v = this.field.dom.value, pageNum;
31872             if(!v || isNaN(pageNum = parseInt(v, 10))){
31873                 this.field.dom.value = d.activePage;
31874                 return;
31875             }
31876             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31877             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31878             e.stopEvent();
31879         }
31880         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))
31881         {
31882           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31883           this.field.dom.value = pageNum;
31884           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31885           e.stopEvent();
31886         }
31887         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31888         {
31889           var v = this.field.dom.value, pageNum; 
31890           var increment = (e.shiftKey) ? 10 : 1;
31891           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31892             increment *= -1;
31893           }
31894           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31895             this.field.dom.value = d.activePage;
31896             return;
31897           }
31898           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31899           {
31900             this.field.dom.value = parseInt(v, 10) + increment;
31901             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31902             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31903           }
31904           e.stopEvent();
31905         }
31906     },
31907
31908     // private
31909     beforeLoad : function(){
31910         if(this.loading){
31911             this.loading.disable();
31912         }
31913     },
31914
31915     // private
31916     onClick : function(which){
31917         var ds = this.ds;
31918         switch(which){
31919             case "first":
31920                 ds.load({params:{start: 0, limit: this.pageSize}});
31921             break;
31922             case "prev":
31923                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31924             break;
31925             case "next":
31926                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31927             break;
31928             case "last":
31929                 var total = ds.getTotalCount();
31930                 var extra = total % this.pageSize;
31931                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31932                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31933             break;
31934             case "refresh":
31935                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31936             break;
31937         }
31938     },
31939
31940     /**
31941      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31942      * @param {Roo.data.Store} store The data store to unbind
31943      */
31944     unbind : function(ds){
31945         ds.un("beforeload", this.beforeLoad, this);
31946         ds.un("load", this.onLoad, this);
31947         ds.un("loadexception", this.onLoadError, this);
31948         ds.un("remove", this.updateInfo, this);
31949         ds.un("add", this.updateInfo, this);
31950         this.ds = undefined;
31951     },
31952
31953     /**
31954      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31955      * @param {Roo.data.Store} store The data store to bind
31956      */
31957     bind : function(ds){
31958         ds.on("beforeload", this.beforeLoad, this);
31959         ds.on("load", this.onLoad, this);
31960         ds.on("loadexception", this.onLoadError, this);
31961         ds.on("remove", this.updateInfo, this);
31962         ds.on("add", this.updateInfo, this);
31963         this.ds = ds;
31964     }
31965 });/*
31966  * Based on:
31967  * Ext JS Library 1.1.1
31968  * Copyright(c) 2006-2007, Ext JS, LLC.
31969  *
31970  * Originally Released Under LGPL - original licence link has changed is not relivant.
31971  *
31972  * Fork - LGPL
31973  * <script type="text/javascript">
31974  */
31975
31976 /**
31977  * @class Roo.Resizable
31978  * @extends Roo.util.Observable
31979  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31980  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31981  * 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
31982  * the element will be wrapped for you automatically.</p>
31983  * <p>Here is the list of valid resize handles:</p>
31984  * <pre>
31985 Value   Description
31986 ------  -------------------
31987  'n'     north
31988  's'     south
31989  'e'     east
31990  'w'     west
31991  'nw'    northwest
31992  'sw'    southwest
31993  'se'    southeast
31994  'ne'    northeast
31995  'hd'    horizontal drag
31996  'all'   all
31997 </pre>
31998  * <p>Here's an example showing the creation of a typical Resizable:</p>
31999  * <pre><code>
32000 var resizer = new Roo.Resizable("element-id", {
32001     handles: 'all',
32002     minWidth: 200,
32003     minHeight: 100,
32004     maxWidth: 500,
32005     maxHeight: 400,
32006     pinned: true
32007 });
32008 resizer.on("resize", myHandler);
32009 </code></pre>
32010  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32011  * resizer.east.setDisplayed(false);</p>
32012  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32013  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32014  * resize operation's new size (defaults to [0, 0])
32015  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32016  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32017  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32018  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32019  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32020  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32021  * @cfg {Number} width The width of the element in pixels (defaults to null)
32022  * @cfg {Number} height The height of the element in pixels (defaults to null)
32023  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32024  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32025  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32026  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32027  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32028  * in favor of the handles config option (defaults to false)
32029  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32030  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32031  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32032  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32033  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32034  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32035  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32036  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32037  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32038  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32039  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32040  * @constructor
32041  * Create a new resizable component
32042  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32043  * @param {Object} config configuration options
32044   */
32045 Roo.Resizable = function(el, config)
32046 {
32047     this.el = Roo.get(el);
32048
32049     if(config && config.wrap){
32050         config.resizeChild = this.el;
32051         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32052         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32053         this.el.setStyle("overflow", "hidden");
32054         this.el.setPositioning(config.resizeChild.getPositioning());
32055         config.resizeChild.clearPositioning();
32056         if(!config.width || !config.height){
32057             var csize = config.resizeChild.getSize();
32058             this.el.setSize(csize.width, csize.height);
32059         }
32060         if(config.pinned && !config.adjustments){
32061             config.adjustments = "auto";
32062         }
32063     }
32064
32065     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32066     this.proxy.unselectable();
32067     this.proxy.enableDisplayMode('block');
32068
32069     Roo.apply(this, config);
32070
32071     if(this.pinned){
32072         this.disableTrackOver = true;
32073         this.el.addClass("x-resizable-pinned");
32074     }
32075     // if the element isn't positioned, make it relative
32076     var position = this.el.getStyle("position");
32077     if(position != "absolute" && position != "fixed"){
32078         this.el.setStyle("position", "relative");
32079     }
32080     if(!this.handles){ // no handles passed, must be legacy style
32081         this.handles = 's,e,se';
32082         if(this.multiDirectional){
32083             this.handles += ',n,w';
32084         }
32085     }
32086     if(this.handles == "all"){
32087         this.handles = "n s e w ne nw se sw";
32088     }
32089     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32090     var ps = Roo.Resizable.positions;
32091     for(var i = 0, len = hs.length; i < len; i++){
32092         if(hs[i] && ps[hs[i]]){
32093             var pos = ps[hs[i]];
32094             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32095         }
32096     }
32097     // legacy
32098     this.corner = this.southeast;
32099     
32100     // updateBox = the box can move..
32101     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32102         this.updateBox = true;
32103     }
32104
32105     this.activeHandle = null;
32106
32107     if(this.resizeChild){
32108         if(typeof this.resizeChild == "boolean"){
32109             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32110         }else{
32111             this.resizeChild = Roo.get(this.resizeChild, true);
32112         }
32113     }
32114     
32115     if(this.adjustments == "auto"){
32116         var rc = this.resizeChild;
32117         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32118         if(rc && (hw || hn)){
32119             rc.position("relative");
32120             rc.setLeft(hw ? hw.el.getWidth() : 0);
32121             rc.setTop(hn ? hn.el.getHeight() : 0);
32122         }
32123         this.adjustments = [
32124             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32125             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32126         ];
32127     }
32128
32129     if(this.draggable){
32130         this.dd = this.dynamic ?
32131             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32132         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32133     }
32134
32135     // public events
32136     this.addEvents({
32137         /**
32138          * @event beforeresize
32139          * Fired before resize is allowed. Set enabled to false to cancel resize.
32140          * @param {Roo.Resizable} this
32141          * @param {Roo.EventObject} e The mousedown event
32142          */
32143         "beforeresize" : true,
32144         /**
32145          * @event resizing
32146          * Fired a resizing.
32147          * @param {Roo.Resizable} this
32148          * @param {Number} x The new x position
32149          * @param {Number} y The new y position
32150          * @param {Number} w The new w width
32151          * @param {Number} h The new h hight
32152          * @param {Roo.EventObject} e The mouseup event
32153          */
32154         "resizing" : true,
32155         /**
32156          * @event resize
32157          * Fired after a resize.
32158          * @param {Roo.Resizable} this
32159          * @param {Number} width The new width
32160          * @param {Number} height The new height
32161          * @param {Roo.EventObject} e The mouseup event
32162          */
32163         "resize" : true
32164     });
32165
32166     if(this.width !== null && this.height !== null){
32167         this.resizeTo(this.width, this.height);
32168     }else{
32169         this.updateChildSize();
32170     }
32171     if(Roo.isIE){
32172         this.el.dom.style.zoom = 1;
32173     }
32174     Roo.Resizable.superclass.constructor.call(this);
32175 };
32176
32177 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32178         resizeChild : false,
32179         adjustments : [0, 0],
32180         minWidth : 5,
32181         minHeight : 5,
32182         maxWidth : 10000,
32183         maxHeight : 10000,
32184         enabled : true,
32185         animate : false,
32186         duration : .35,
32187         dynamic : false,
32188         handles : false,
32189         multiDirectional : false,
32190         disableTrackOver : false,
32191         easing : 'easeOutStrong',
32192         widthIncrement : 0,
32193         heightIncrement : 0,
32194         pinned : false,
32195         width : null,
32196         height : null,
32197         preserveRatio : false,
32198         transparent: false,
32199         minX: 0,
32200         minY: 0,
32201         draggable: false,
32202
32203         /**
32204          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32205          */
32206         constrainTo: undefined,
32207         /**
32208          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32209          */
32210         resizeRegion: undefined,
32211
32212
32213     /**
32214      * Perform a manual resize
32215      * @param {Number} width
32216      * @param {Number} height
32217      */
32218     resizeTo : function(width, height){
32219         this.el.setSize(width, height);
32220         this.updateChildSize();
32221         this.fireEvent("resize", this, width, height, null);
32222     },
32223
32224     // private
32225     startSizing : function(e, handle){
32226         this.fireEvent("beforeresize", this, e);
32227         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32228
32229             if(!this.overlay){
32230                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32231                 this.overlay.unselectable();
32232                 this.overlay.enableDisplayMode("block");
32233                 this.overlay.on("mousemove", this.onMouseMove, this);
32234                 this.overlay.on("mouseup", this.onMouseUp, this);
32235             }
32236             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32237
32238             this.resizing = true;
32239             this.startBox = this.el.getBox();
32240             this.startPoint = e.getXY();
32241             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32242                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32243
32244             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32245             this.overlay.show();
32246
32247             if(this.constrainTo) {
32248                 var ct = Roo.get(this.constrainTo);
32249                 this.resizeRegion = ct.getRegion().adjust(
32250                     ct.getFrameWidth('t'),
32251                     ct.getFrameWidth('l'),
32252                     -ct.getFrameWidth('b'),
32253                     -ct.getFrameWidth('r')
32254                 );
32255             }
32256
32257             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32258             this.proxy.show();
32259             this.proxy.setBox(this.startBox);
32260             if(!this.dynamic){
32261                 this.proxy.setStyle('visibility', 'visible');
32262             }
32263         }
32264     },
32265
32266     // private
32267     onMouseDown : function(handle, e){
32268         if(this.enabled){
32269             e.stopEvent();
32270             this.activeHandle = handle;
32271             this.startSizing(e, handle);
32272         }
32273     },
32274
32275     // private
32276     onMouseUp : function(e){
32277         var size = this.resizeElement();
32278         this.resizing = false;
32279         this.handleOut();
32280         this.overlay.hide();
32281         this.proxy.hide();
32282         this.fireEvent("resize", this, size.width, size.height, e);
32283     },
32284
32285     // private
32286     updateChildSize : function(){
32287         
32288         if(this.resizeChild){
32289             var el = this.el;
32290             var child = this.resizeChild;
32291             var adj = this.adjustments;
32292             if(el.dom.offsetWidth){
32293                 var b = el.getSize(true);
32294                 child.setSize(b.width+adj[0], b.height+adj[1]);
32295             }
32296             // Second call here for IE
32297             // The first call enables instant resizing and
32298             // the second call corrects scroll bars if they
32299             // exist
32300             if(Roo.isIE){
32301                 setTimeout(function(){
32302                     if(el.dom.offsetWidth){
32303                         var b = el.getSize(true);
32304                         child.setSize(b.width+adj[0], b.height+adj[1]);
32305                     }
32306                 }, 10);
32307             }
32308         }
32309     },
32310
32311     // private
32312     snap : function(value, inc, min){
32313         if(!inc || !value) {
32314             return value;
32315         }
32316         var newValue = value;
32317         var m = value % inc;
32318         if(m > 0){
32319             if(m > (inc/2)){
32320                 newValue = value + (inc-m);
32321             }else{
32322                 newValue = value - m;
32323             }
32324         }
32325         return Math.max(min, newValue);
32326     },
32327
32328     // private
32329     resizeElement : function(){
32330         var box = this.proxy.getBox();
32331         if(this.updateBox){
32332             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32333         }else{
32334             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32335         }
32336         this.updateChildSize();
32337         if(!this.dynamic){
32338             this.proxy.hide();
32339         }
32340         return box;
32341     },
32342
32343     // private
32344     constrain : function(v, diff, m, mx){
32345         if(v - diff < m){
32346             diff = v - m;
32347         }else if(v - diff > mx){
32348             diff = mx - v;
32349         }
32350         return diff;
32351     },
32352
32353     // private
32354     onMouseMove : function(e){
32355         
32356         if(this.enabled){
32357             try{// try catch so if something goes wrong the user doesn't get hung
32358
32359             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32360                 return;
32361             }
32362
32363             //var curXY = this.startPoint;
32364             var curSize = this.curSize || this.startBox;
32365             var x = this.startBox.x, y = this.startBox.y;
32366             var ox = x, oy = y;
32367             var w = curSize.width, h = curSize.height;
32368             var ow = w, oh = h;
32369             var mw = this.minWidth, mh = this.minHeight;
32370             var mxw = this.maxWidth, mxh = this.maxHeight;
32371             var wi = this.widthIncrement;
32372             var hi = this.heightIncrement;
32373
32374             var eventXY = e.getXY();
32375             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32376             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32377
32378             var pos = this.activeHandle.position;
32379
32380             switch(pos){
32381                 case "east":
32382                     w += diffX;
32383                     w = Math.min(Math.max(mw, w), mxw);
32384                     break;
32385              
32386                 case "south":
32387                     h += diffY;
32388                     h = Math.min(Math.max(mh, h), mxh);
32389                     break;
32390                 case "southeast":
32391                     w += diffX;
32392                     h += diffY;
32393                     w = Math.min(Math.max(mw, w), mxw);
32394                     h = Math.min(Math.max(mh, h), mxh);
32395                     break;
32396                 case "north":
32397                     diffY = this.constrain(h, diffY, mh, mxh);
32398                     y += diffY;
32399                     h -= diffY;
32400                     break;
32401                 case "hdrag":
32402                     
32403                     if (wi) {
32404                         var adiffX = Math.abs(diffX);
32405                         var sub = (adiffX % wi); // how much 
32406                         if (sub > (wi/2)) { // far enough to snap
32407                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32408                         } else {
32409                             // remove difference.. 
32410                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32411                         }
32412                     }
32413                     x += diffX;
32414                     x = Math.max(this.minX, x);
32415                     break;
32416                 case "west":
32417                     diffX = this.constrain(w, diffX, mw, mxw);
32418                     x += diffX;
32419                     w -= diffX;
32420                     break;
32421                 case "northeast":
32422                     w += diffX;
32423                     w = Math.min(Math.max(mw, w), mxw);
32424                     diffY = this.constrain(h, diffY, mh, mxh);
32425                     y += diffY;
32426                     h -= diffY;
32427                     break;
32428                 case "northwest":
32429                     diffX = this.constrain(w, diffX, mw, mxw);
32430                     diffY = this.constrain(h, diffY, mh, mxh);
32431                     y += diffY;
32432                     h -= diffY;
32433                     x += diffX;
32434                     w -= diffX;
32435                     break;
32436                case "southwest":
32437                     diffX = this.constrain(w, diffX, mw, mxw);
32438                     h += diffY;
32439                     h = Math.min(Math.max(mh, h), mxh);
32440                     x += diffX;
32441                     w -= diffX;
32442                     break;
32443             }
32444
32445             var sw = this.snap(w, wi, mw);
32446             var sh = this.snap(h, hi, mh);
32447             if(sw != w || sh != h){
32448                 switch(pos){
32449                     case "northeast":
32450                         y -= sh - h;
32451                     break;
32452                     case "north":
32453                         y -= sh - h;
32454                         break;
32455                     case "southwest":
32456                         x -= sw - w;
32457                     break;
32458                     case "west":
32459                         x -= sw - w;
32460                         break;
32461                     case "northwest":
32462                         x -= sw - w;
32463                         y -= sh - h;
32464                     break;
32465                 }
32466                 w = sw;
32467                 h = sh;
32468             }
32469
32470             if(this.preserveRatio){
32471                 switch(pos){
32472                     case "southeast":
32473                     case "east":
32474                         h = oh * (w/ow);
32475                         h = Math.min(Math.max(mh, h), mxh);
32476                         w = ow * (h/oh);
32477                        break;
32478                     case "south":
32479                         w = ow * (h/oh);
32480                         w = Math.min(Math.max(mw, w), mxw);
32481                         h = oh * (w/ow);
32482                         break;
32483                     case "northeast":
32484                         w = ow * (h/oh);
32485                         w = Math.min(Math.max(mw, w), mxw);
32486                         h = oh * (w/ow);
32487                     break;
32488                     case "north":
32489                         var tw = w;
32490                         w = ow * (h/oh);
32491                         w = Math.min(Math.max(mw, w), mxw);
32492                         h = oh * (w/ow);
32493                         x += (tw - w) / 2;
32494                         break;
32495                     case "southwest":
32496                         h = oh * (w/ow);
32497                         h = Math.min(Math.max(mh, h), mxh);
32498                         var tw = w;
32499                         w = ow * (h/oh);
32500                         x += tw - w;
32501                         break;
32502                     case "west":
32503                         var th = h;
32504                         h = oh * (w/ow);
32505                         h = Math.min(Math.max(mh, h), mxh);
32506                         y += (th - h) / 2;
32507                         var tw = w;
32508                         w = ow * (h/oh);
32509                         x += tw - w;
32510                        break;
32511                     case "northwest":
32512                         var tw = w;
32513                         var th = h;
32514                         h = oh * (w/ow);
32515                         h = Math.min(Math.max(mh, h), mxh);
32516                         w = ow * (h/oh);
32517                         y += th - h;
32518                         x += tw - w;
32519                        break;
32520
32521                 }
32522             }
32523             if (pos == 'hdrag') {
32524                 w = ow;
32525             }
32526             this.proxy.setBounds(x, y, w, h);
32527             if(this.dynamic){
32528                 this.resizeElement();
32529             }
32530             }catch(e){}
32531         }
32532         this.fireEvent("resizing", this, x, y, w, h, e);
32533     },
32534
32535     // private
32536     handleOver : function(){
32537         if(this.enabled){
32538             this.el.addClass("x-resizable-over");
32539         }
32540     },
32541
32542     // private
32543     handleOut : function(){
32544         if(!this.resizing){
32545             this.el.removeClass("x-resizable-over");
32546         }
32547     },
32548
32549     /**
32550      * Returns the element this component is bound to.
32551      * @return {Roo.Element}
32552      */
32553     getEl : function(){
32554         return this.el;
32555     },
32556
32557     /**
32558      * Returns the resizeChild element (or null).
32559      * @return {Roo.Element}
32560      */
32561     getResizeChild : function(){
32562         return this.resizeChild;
32563     },
32564     groupHandler : function()
32565     {
32566         
32567     },
32568     /**
32569      * Destroys this resizable. If the element was wrapped and
32570      * removeEl is not true then the element remains.
32571      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32572      */
32573     destroy : function(removeEl){
32574         this.proxy.remove();
32575         if(this.overlay){
32576             this.overlay.removeAllListeners();
32577             this.overlay.remove();
32578         }
32579         var ps = Roo.Resizable.positions;
32580         for(var k in ps){
32581             if(typeof ps[k] != "function" && this[ps[k]]){
32582                 var h = this[ps[k]];
32583                 h.el.removeAllListeners();
32584                 h.el.remove();
32585             }
32586         }
32587         if(removeEl){
32588             this.el.update("");
32589             this.el.remove();
32590         }
32591     }
32592 });
32593
32594 // private
32595 // hash to map config positions to true positions
32596 Roo.Resizable.positions = {
32597     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32598     hd: "hdrag"
32599 };
32600
32601 // private
32602 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32603     if(!this.tpl){
32604         // only initialize the template if resizable is used
32605         var tpl = Roo.DomHelper.createTemplate(
32606             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32607         );
32608         tpl.compile();
32609         Roo.Resizable.Handle.prototype.tpl = tpl;
32610     }
32611     this.position = pos;
32612     this.rz = rz;
32613     // show north drag fro topdra
32614     var handlepos = pos == 'hdrag' ? 'north' : pos;
32615     
32616     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32617     if (pos == 'hdrag') {
32618         this.el.setStyle('cursor', 'pointer');
32619     }
32620     this.el.unselectable();
32621     if(transparent){
32622         this.el.setOpacity(0);
32623     }
32624     this.el.on("mousedown", this.onMouseDown, this);
32625     if(!disableTrackOver){
32626         this.el.on("mouseover", this.onMouseOver, this);
32627         this.el.on("mouseout", this.onMouseOut, this);
32628     }
32629 };
32630
32631 // private
32632 Roo.Resizable.Handle.prototype = {
32633     afterResize : function(rz){
32634         Roo.log('after?');
32635         // do nothing
32636     },
32637     // private
32638     onMouseDown : function(e){
32639         this.rz.onMouseDown(this, e);
32640     },
32641     // private
32642     onMouseOver : function(e){
32643         this.rz.handleOver(this, e);
32644     },
32645     // private
32646     onMouseOut : function(e){
32647         this.rz.handleOut(this, e);
32648     }
32649 };/*
32650  * Based on:
32651  * Ext JS Library 1.1.1
32652  * Copyright(c) 2006-2007, Ext JS, LLC.
32653  *
32654  * Originally Released Under LGPL - original licence link has changed is not relivant.
32655  *
32656  * Fork - LGPL
32657  * <script type="text/javascript">
32658  */
32659
32660 /**
32661  * @class Roo.Editor
32662  * @extends Roo.Component
32663  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32664  * @constructor
32665  * Create a new Editor
32666  * @param {Roo.form.Field} field The Field object (or descendant)
32667  * @param {Object} config The config object
32668  */
32669 Roo.Editor = function(field, config){
32670     Roo.Editor.superclass.constructor.call(this, config);
32671     this.field = field;
32672     this.addEvents({
32673         /**
32674              * @event beforestartedit
32675              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32676              * false from the handler of this event.
32677              * @param {Editor} this
32678              * @param {Roo.Element} boundEl The underlying element bound to this editor
32679              * @param {Mixed} value The field value being set
32680              */
32681         "beforestartedit" : true,
32682         /**
32683              * @event startedit
32684              * Fires when this editor is displayed
32685              * @param {Roo.Element} boundEl The underlying element bound to this editor
32686              * @param {Mixed} value The starting field value
32687              */
32688         "startedit" : true,
32689         /**
32690              * @event beforecomplete
32691              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32692              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32693              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32694              * event will not fire since no edit actually occurred.
32695              * @param {Editor} this
32696              * @param {Mixed} value The current field value
32697              * @param {Mixed} startValue The original field value
32698              */
32699         "beforecomplete" : true,
32700         /**
32701              * @event complete
32702              * Fires after editing is complete and any changed value has been written to the underlying field.
32703              * @param {Editor} this
32704              * @param {Mixed} value The current field value
32705              * @param {Mixed} startValue The original field value
32706              */
32707         "complete" : true,
32708         /**
32709          * @event specialkey
32710          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32711          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32712          * @param {Roo.form.Field} this
32713          * @param {Roo.EventObject} e The event object
32714          */
32715         "specialkey" : true
32716     });
32717 };
32718
32719 Roo.extend(Roo.Editor, Roo.Component, {
32720     /**
32721      * @cfg {Boolean/String} autosize
32722      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32723      * or "height" to adopt the height only (defaults to false)
32724      */
32725     /**
32726      * @cfg {Boolean} revertInvalid
32727      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32728      * validation fails (defaults to true)
32729      */
32730     /**
32731      * @cfg {Boolean} ignoreNoChange
32732      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32733      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32734      * will never be ignored.
32735      */
32736     /**
32737      * @cfg {Boolean} hideEl
32738      * False to keep the bound element visible while the editor is displayed (defaults to true)
32739      */
32740     /**
32741      * @cfg {Mixed} value
32742      * The data value of the underlying field (defaults to "")
32743      */
32744     value : "",
32745     /**
32746      * @cfg {String} alignment
32747      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32748      */
32749     alignment: "c-c?",
32750     /**
32751      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32752      * for bottom-right shadow (defaults to "frame")
32753      */
32754     shadow : "frame",
32755     /**
32756      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32757      */
32758     constrain : false,
32759     /**
32760      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32761      */
32762     completeOnEnter : false,
32763     /**
32764      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32765      */
32766     cancelOnEsc : false,
32767     /**
32768      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32769      */
32770     updateEl : false,
32771
32772     // private
32773     onRender : function(ct, position){
32774         this.el = new Roo.Layer({
32775             shadow: this.shadow,
32776             cls: "x-editor",
32777             parentEl : ct,
32778             shim : this.shim,
32779             shadowOffset:4,
32780             id: this.id,
32781             constrain: this.constrain
32782         });
32783         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32784         if(this.field.msgTarget != 'title'){
32785             this.field.msgTarget = 'qtip';
32786         }
32787         this.field.render(this.el);
32788         if(Roo.isGecko){
32789             this.field.el.dom.setAttribute('autocomplete', 'off');
32790         }
32791         this.field.on("specialkey", this.onSpecialKey, this);
32792         if(this.swallowKeys){
32793             this.field.el.swallowEvent(['keydown','keypress']);
32794         }
32795         this.field.show();
32796         this.field.on("blur", this.onBlur, this);
32797         if(this.field.grow){
32798             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32799         }
32800     },
32801
32802     onSpecialKey : function(field, e)
32803     {
32804         //Roo.log('editor onSpecialKey');
32805         if(this.completeOnEnter && e.getKey() == e.ENTER){
32806             e.stopEvent();
32807             this.completeEdit();
32808             return;
32809         }
32810         // do not fire special key otherwise it might hide close the editor...
32811         if(e.getKey() == e.ENTER){    
32812             return;
32813         }
32814         if(this.cancelOnEsc && e.getKey() == e.ESC){
32815             this.cancelEdit();
32816             return;
32817         } 
32818         this.fireEvent('specialkey', field, e);
32819     
32820     },
32821
32822     /**
32823      * Starts the editing process and shows the editor.
32824      * @param {String/HTMLElement/Element} el The element to edit
32825      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32826       * to the innerHTML of el.
32827      */
32828     startEdit : function(el, value){
32829         if(this.editing){
32830             this.completeEdit();
32831         }
32832         this.boundEl = Roo.get(el);
32833         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32834         if(!this.rendered){
32835             this.render(this.parentEl || document.body);
32836         }
32837         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32838             return;
32839         }
32840         this.startValue = v;
32841         this.field.setValue(v);
32842         if(this.autoSize){
32843             var sz = this.boundEl.getSize();
32844             switch(this.autoSize){
32845                 case "width":
32846                 this.setSize(sz.width,  "");
32847                 break;
32848                 case "height":
32849                 this.setSize("",  sz.height);
32850                 break;
32851                 default:
32852                 this.setSize(sz.width,  sz.height);
32853             }
32854         }
32855         this.el.alignTo(this.boundEl, this.alignment);
32856         this.editing = true;
32857         if(Roo.QuickTips){
32858             Roo.QuickTips.disable();
32859         }
32860         this.show();
32861     },
32862
32863     /**
32864      * Sets the height and width of this editor.
32865      * @param {Number} width The new width
32866      * @param {Number} height The new height
32867      */
32868     setSize : function(w, h){
32869         this.field.setSize(w, h);
32870         if(this.el){
32871             this.el.sync();
32872         }
32873     },
32874
32875     /**
32876      * Realigns the editor to the bound field based on the current alignment config value.
32877      */
32878     realign : function(){
32879         this.el.alignTo(this.boundEl, this.alignment);
32880     },
32881
32882     /**
32883      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32884      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32885      */
32886     completeEdit : function(remainVisible){
32887         if(!this.editing){
32888             return;
32889         }
32890         var v = this.getValue();
32891         if(this.revertInvalid !== false && !this.field.isValid()){
32892             v = this.startValue;
32893             this.cancelEdit(true);
32894         }
32895         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32896             this.editing = false;
32897             this.hide();
32898             return;
32899         }
32900         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32901             this.editing = false;
32902             if(this.updateEl && this.boundEl){
32903                 this.boundEl.update(v);
32904             }
32905             if(remainVisible !== true){
32906                 this.hide();
32907             }
32908             this.fireEvent("complete", this, v, this.startValue);
32909         }
32910     },
32911
32912     // private
32913     onShow : function(){
32914         this.el.show();
32915         if(this.hideEl !== false){
32916             this.boundEl.hide();
32917         }
32918         this.field.show();
32919         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32920             this.fixIEFocus = true;
32921             this.deferredFocus.defer(50, this);
32922         }else{
32923             this.field.focus();
32924         }
32925         this.fireEvent("startedit", this.boundEl, this.startValue);
32926     },
32927
32928     deferredFocus : function(){
32929         if(this.editing){
32930             this.field.focus();
32931         }
32932     },
32933
32934     /**
32935      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32936      * reverted to the original starting value.
32937      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32938      * cancel (defaults to false)
32939      */
32940     cancelEdit : function(remainVisible){
32941         if(this.editing){
32942             this.setValue(this.startValue);
32943             if(remainVisible !== true){
32944                 this.hide();
32945             }
32946         }
32947     },
32948
32949     // private
32950     onBlur : function(){
32951         if(this.allowBlur !== true && this.editing){
32952             this.completeEdit();
32953         }
32954     },
32955
32956     // private
32957     onHide : function(){
32958         if(this.editing){
32959             this.completeEdit();
32960             return;
32961         }
32962         this.field.blur();
32963         if(this.field.collapse){
32964             this.field.collapse();
32965         }
32966         this.el.hide();
32967         if(this.hideEl !== false){
32968             this.boundEl.show();
32969         }
32970         if(Roo.QuickTips){
32971             Roo.QuickTips.enable();
32972         }
32973     },
32974
32975     /**
32976      * Sets the data value of the editor
32977      * @param {Mixed} value Any valid value supported by the underlying field
32978      */
32979     setValue : function(v){
32980         this.field.setValue(v);
32981     },
32982
32983     /**
32984      * Gets the data value of the editor
32985      * @return {Mixed} The data value
32986      */
32987     getValue : function(){
32988         return this.field.getValue();
32989     }
32990 });/*
32991  * Based on:
32992  * Ext JS Library 1.1.1
32993  * Copyright(c) 2006-2007, Ext JS, LLC.
32994  *
32995  * Originally Released Under LGPL - original licence link has changed is not relivant.
32996  *
32997  * Fork - LGPL
32998  * <script type="text/javascript">
32999  */
33000  
33001 /**
33002  * @class Roo.BasicDialog
33003  * @extends Roo.util.Observable
33004  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33005  * <pre><code>
33006 var dlg = new Roo.BasicDialog("my-dlg", {
33007     height: 200,
33008     width: 300,
33009     minHeight: 100,
33010     minWidth: 150,
33011     modal: true,
33012     proxyDrag: true,
33013     shadow: true
33014 });
33015 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33016 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33017 dlg.addButton('Cancel', dlg.hide, dlg);
33018 dlg.show();
33019 </code></pre>
33020   <b>A Dialog should always be a direct child of the body element.</b>
33021  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33022  * @cfg {String} title Default text to display in the title bar (defaults to null)
33023  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33024  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33025  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33026  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33027  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33028  * (defaults to null with no animation)
33029  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33030  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33031  * property for valid values (defaults to 'all')
33032  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33033  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33034  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33035  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33036  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33037  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33038  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33039  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33040  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33041  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33042  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33043  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33044  * draggable = true (defaults to false)
33045  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33046  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33047  * shadow (defaults to false)
33048  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33049  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33050  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33051  * @cfg {Array} buttons Array of buttons
33052  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33053  * @constructor
33054  * Create a new BasicDialog.
33055  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33056  * @param {Object} config Configuration options
33057  */
33058 Roo.BasicDialog = function(el, config){
33059     this.el = Roo.get(el);
33060     var dh = Roo.DomHelper;
33061     if(!this.el && config && config.autoCreate){
33062         if(typeof config.autoCreate == "object"){
33063             if(!config.autoCreate.id){
33064                 config.autoCreate.id = el;
33065             }
33066             this.el = dh.append(document.body,
33067                         config.autoCreate, true);
33068         }else{
33069             this.el = dh.append(document.body,
33070                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33071         }
33072     }
33073     el = this.el;
33074     el.setDisplayed(true);
33075     el.hide = this.hideAction;
33076     this.id = el.id;
33077     el.addClass("x-dlg");
33078
33079     Roo.apply(this, config);
33080
33081     this.proxy = el.createProxy("x-dlg-proxy");
33082     this.proxy.hide = this.hideAction;
33083     this.proxy.setOpacity(.5);
33084     this.proxy.hide();
33085
33086     if(config.width){
33087         el.setWidth(config.width);
33088     }
33089     if(config.height){
33090         el.setHeight(config.height);
33091     }
33092     this.size = el.getSize();
33093     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33094         this.xy = [config.x,config.y];
33095     }else{
33096         this.xy = el.getCenterXY(true);
33097     }
33098     /** The header element @type Roo.Element */
33099     this.header = el.child("> .x-dlg-hd");
33100     /** The body element @type Roo.Element */
33101     this.body = el.child("> .x-dlg-bd");
33102     /** The footer element @type Roo.Element */
33103     this.footer = el.child("> .x-dlg-ft");
33104
33105     if(!this.header){
33106         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33107     }
33108     if(!this.body){
33109         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33110     }
33111
33112     this.header.unselectable();
33113     if(this.title){
33114         this.header.update(this.title);
33115     }
33116     // this element allows the dialog to be focused for keyboard event
33117     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33118     this.focusEl.swallowEvent("click", true);
33119
33120     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33121
33122     // wrap the body and footer for special rendering
33123     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33124     if(this.footer){
33125         this.bwrap.dom.appendChild(this.footer.dom);
33126     }
33127
33128     this.bg = this.el.createChild({
33129         tag: "div", cls:"x-dlg-bg",
33130         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33131     });
33132     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33133
33134
33135     if(this.autoScroll !== false && !this.autoTabs){
33136         this.body.setStyle("overflow", "auto");
33137     }
33138
33139     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33140
33141     if(this.closable !== false){
33142         this.el.addClass("x-dlg-closable");
33143         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33144         this.close.on("click", this.closeClick, this);
33145         this.close.addClassOnOver("x-dlg-close-over");
33146     }
33147     if(this.collapsible !== false){
33148         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33149         this.collapseBtn.on("click", this.collapseClick, this);
33150         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33151         this.header.on("dblclick", this.collapseClick, this);
33152     }
33153     if(this.resizable !== false){
33154         this.el.addClass("x-dlg-resizable");
33155         this.resizer = new Roo.Resizable(el, {
33156             minWidth: this.minWidth || 80,
33157             minHeight:this.minHeight || 80,
33158             handles: this.resizeHandles || "all",
33159             pinned: true
33160         });
33161         this.resizer.on("beforeresize", this.beforeResize, this);
33162         this.resizer.on("resize", this.onResize, this);
33163     }
33164     if(this.draggable !== false){
33165         el.addClass("x-dlg-draggable");
33166         if (!this.proxyDrag) {
33167             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33168         }
33169         else {
33170             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33171         }
33172         dd.setHandleElId(this.header.id);
33173         dd.endDrag = this.endMove.createDelegate(this);
33174         dd.startDrag = this.startMove.createDelegate(this);
33175         dd.onDrag = this.onDrag.createDelegate(this);
33176         dd.scroll = false;
33177         this.dd = dd;
33178     }
33179     if(this.modal){
33180         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33181         this.mask.enableDisplayMode("block");
33182         this.mask.hide();
33183         this.el.addClass("x-dlg-modal");
33184     }
33185     if(this.shadow){
33186         this.shadow = new Roo.Shadow({
33187             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33188             offset : this.shadowOffset
33189         });
33190     }else{
33191         this.shadowOffset = 0;
33192     }
33193     if(Roo.useShims && this.shim !== false){
33194         this.shim = this.el.createShim();
33195         this.shim.hide = this.hideAction;
33196         this.shim.hide();
33197     }else{
33198         this.shim = false;
33199     }
33200     if(this.autoTabs){
33201         this.initTabs();
33202     }
33203     if (this.buttons) { 
33204         var bts= this.buttons;
33205         this.buttons = [];
33206         Roo.each(bts, function(b) {
33207             this.addButton(b);
33208         }, this);
33209     }
33210     
33211     
33212     this.addEvents({
33213         /**
33214          * @event keydown
33215          * Fires when a key is pressed
33216          * @param {Roo.BasicDialog} this
33217          * @param {Roo.EventObject} e
33218          */
33219         "keydown" : true,
33220         /**
33221          * @event move
33222          * Fires when this dialog is moved by the user.
33223          * @param {Roo.BasicDialog} this
33224          * @param {Number} x The new page X
33225          * @param {Number} y The new page Y
33226          */
33227         "move" : true,
33228         /**
33229          * @event resize
33230          * Fires when this dialog is resized by the user.
33231          * @param {Roo.BasicDialog} this
33232          * @param {Number} width The new width
33233          * @param {Number} height The new height
33234          */
33235         "resize" : true,
33236         /**
33237          * @event beforehide
33238          * Fires before this dialog is hidden.
33239          * @param {Roo.BasicDialog} this
33240          */
33241         "beforehide" : true,
33242         /**
33243          * @event hide
33244          * Fires when this dialog is hidden.
33245          * @param {Roo.BasicDialog} this
33246          */
33247         "hide" : true,
33248         /**
33249          * @event beforeshow
33250          * Fires before this dialog is shown.
33251          * @param {Roo.BasicDialog} this
33252          */
33253         "beforeshow" : true,
33254         /**
33255          * @event show
33256          * Fires when this dialog is shown.
33257          * @param {Roo.BasicDialog} this
33258          */
33259         "show" : true
33260     });
33261     el.on("keydown", this.onKeyDown, this);
33262     el.on("mousedown", this.toFront, this);
33263     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33264     this.el.hide();
33265     Roo.DialogManager.register(this);
33266     Roo.BasicDialog.superclass.constructor.call(this);
33267 };
33268
33269 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33270     shadowOffset: Roo.isIE ? 6 : 5,
33271     minHeight: 80,
33272     minWidth: 200,
33273     minButtonWidth: 75,
33274     defaultButton: null,
33275     buttonAlign: "right",
33276     tabTag: 'div',
33277     firstShow: true,
33278
33279     /**
33280      * Sets the dialog title text
33281      * @param {String} text The title text to display
33282      * @return {Roo.BasicDialog} this
33283      */
33284     setTitle : function(text){
33285         this.header.update(text);
33286         return this;
33287     },
33288
33289     // private
33290     closeClick : function(){
33291         this.hide();
33292     },
33293
33294     // private
33295     collapseClick : function(){
33296         this[this.collapsed ? "expand" : "collapse"]();
33297     },
33298
33299     /**
33300      * Collapses the dialog to its minimized state (only the title bar is visible).
33301      * Equivalent to the user clicking the collapse dialog button.
33302      */
33303     collapse : function(){
33304         if(!this.collapsed){
33305             this.collapsed = true;
33306             this.el.addClass("x-dlg-collapsed");
33307             this.restoreHeight = this.el.getHeight();
33308             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33309         }
33310     },
33311
33312     /**
33313      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33314      * clicking the expand dialog button.
33315      */
33316     expand : function(){
33317         if(this.collapsed){
33318             this.collapsed = false;
33319             this.el.removeClass("x-dlg-collapsed");
33320             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33321         }
33322     },
33323
33324     /**
33325      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33326      * @return {Roo.TabPanel} The tabs component
33327      */
33328     initTabs : function(){
33329         var tabs = this.getTabs();
33330         while(tabs.getTab(0)){
33331             tabs.removeTab(0);
33332         }
33333         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33334             var dom = el.dom;
33335             tabs.addTab(Roo.id(dom), dom.title);
33336             dom.title = "";
33337         });
33338         tabs.activate(0);
33339         return tabs;
33340     },
33341
33342     // private
33343     beforeResize : function(){
33344         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33345     },
33346
33347     // private
33348     onResize : function(){
33349         this.refreshSize();
33350         this.syncBodyHeight();
33351         this.adjustAssets();
33352         this.focus();
33353         this.fireEvent("resize", this, this.size.width, this.size.height);
33354     },
33355
33356     // private
33357     onKeyDown : function(e){
33358         if(this.isVisible()){
33359             this.fireEvent("keydown", this, e);
33360         }
33361     },
33362
33363     /**
33364      * Resizes the dialog.
33365      * @param {Number} width
33366      * @param {Number} height
33367      * @return {Roo.BasicDialog} this
33368      */
33369     resizeTo : function(width, height){
33370         this.el.setSize(width, height);
33371         this.size = {width: width, height: height};
33372         this.syncBodyHeight();
33373         if(this.fixedcenter){
33374             this.center();
33375         }
33376         if(this.isVisible()){
33377             this.constrainXY();
33378             this.adjustAssets();
33379         }
33380         this.fireEvent("resize", this, width, height);
33381         return this;
33382     },
33383
33384
33385     /**
33386      * Resizes the dialog to fit the specified content size.
33387      * @param {Number} width
33388      * @param {Number} height
33389      * @return {Roo.BasicDialog} this
33390      */
33391     setContentSize : function(w, h){
33392         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33393         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33394         //if(!this.el.isBorderBox()){
33395             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33396             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33397         //}
33398         if(this.tabs){
33399             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33400             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33401         }
33402         this.resizeTo(w, h);
33403         return this;
33404     },
33405
33406     /**
33407      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33408      * executed in response to a particular key being pressed while the dialog is active.
33409      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33410      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33411      * @param {Function} fn The function to call
33412      * @param {Object} scope (optional) The scope of the function
33413      * @return {Roo.BasicDialog} this
33414      */
33415     addKeyListener : function(key, fn, scope){
33416         var keyCode, shift, ctrl, alt;
33417         if(typeof key == "object" && !(key instanceof Array)){
33418             keyCode = key["key"];
33419             shift = key["shift"];
33420             ctrl = key["ctrl"];
33421             alt = key["alt"];
33422         }else{
33423             keyCode = key;
33424         }
33425         var handler = function(dlg, e){
33426             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33427                 var k = e.getKey();
33428                 if(keyCode instanceof Array){
33429                     for(var i = 0, len = keyCode.length; i < len; i++){
33430                         if(keyCode[i] == k){
33431                           fn.call(scope || window, dlg, k, e);
33432                           return;
33433                         }
33434                     }
33435                 }else{
33436                     if(k == keyCode){
33437                         fn.call(scope || window, dlg, k, e);
33438                     }
33439                 }
33440             }
33441         };
33442         this.on("keydown", handler);
33443         return this;
33444     },
33445
33446     /**
33447      * Returns the TabPanel component (creates it if it doesn't exist).
33448      * Note: If you wish to simply check for the existence of tabs without creating them,
33449      * check for a null 'tabs' property.
33450      * @return {Roo.TabPanel} The tabs component
33451      */
33452     getTabs : function(){
33453         if(!this.tabs){
33454             this.el.addClass("x-dlg-auto-tabs");
33455             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33456             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33457         }
33458         return this.tabs;
33459     },
33460
33461     /**
33462      * Adds a button to the footer section of the dialog.
33463      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33464      * object or a valid Roo.DomHelper element config
33465      * @param {Function} handler The function called when the button is clicked
33466      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33467      * @return {Roo.Button} The new button
33468      */
33469     addButton : function(config, handler, scope){
33470         var dh = Roo.DomHelper;
33471         if(!this.footer){
33472             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33473         }
33474         if(!this.btnContainer){
33475             var tb = this.footer.createChild({
33476
33477                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33478                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33479             }, null, true);
33480             this.btnContainer = tb.firstChild.firstChild.firstChild;
33481         }
33482         var bconfig = {
33483             handler: handler,
33484             scope: scope,
33485             minWidth: this.minButtonWidth,
33486             hideParent:true
33487         };
33488         if(typeof config == "string"){
33489             bconfig.text = config;
33490         }else{
33491             if(config.tag){
33492                 bconfig.dhconfig = config;
33493             }else{
33494                 Roo.apply(bconfig, config);
33495             }
33496         }
33497         var fc = false;
33498         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33499             bconfig.position = Math.max(0, bconfig.position);
33500             fc = this.btnContainer.childNodes[bconfig.position];
33501         }
33502          
33503         var btn = new Roo.Button(
33504             fc ? 
33505                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33506                 : this.btnContainer.appendChild(document.createElement("td")),
33507             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33508             bconfig
33509         );
33510         this.syncBodyHeight();
33511         if(!this.buttons){
33512             /**
33513              * Array of all the buttons that have been added to this dialog via addButton
33514              * @type Array
33515              */
33516             this.buttons = [];
33517         }
33518         this.buttons.push(btn);
33519         return btn;
33520     },
33521
33522     /**
33523      * Sets the default button to be focused when the dialog is displayed.
33524      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33525      * @return {Roo.BasicDialog} this
33526      */
33527     setDefaultButton : function(btn){
33528         this.defaultButton = btn;
33529         return this;
33530     },
33531
33532     // private
33533     getHeaderFooterHeight : function(safe){
33534         var height = 0;
33535         if(this.header){
33536            height += this.header.getHeight();
33537         }
33538         if(this.footer){
33539            var fm = this.footer.getMargins();
33540             height += (this.footer.getHeight()+fm.top+fm.bottom);
33541         }
33542         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33543         height += this.centerBg.getPadding("tb");
33544         return height;
33545     },
33546
33547     // private
33548     syncBodyHeight : function()
33549     {
33550         var bd = this.body, // the text
33551             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33552             bw = this.bwrap;
33553         var height = this.size.height - this.getHeaderFooterHeight(false);
33554         bd.setHeight(height-bd.getMargins("tb"));
33555         var hh = this.header.getHeight();
33556         var h = this.size.height-hh;
33557         cb.setHeight(h);
33558         
33559         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33560         bw.setHeight(h-cb.getPadding("tb"));
33561         
33562         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33563         bd.setWidth(bw.getWidth(true));
33564         if(this.tabs){
33565             this.tabs.syncHeight();
33566             if(Roo.isIE){
33567                 this.tabs.el.repaint();
33568             }
33569         }
33570     },
33571
33572     /**
33573      * Restores the previous state of the dialog if Roo.state is configured.
33574      * @return {Roo.BasicDialog} this
33575      */
33576     restoreState : function(){
33577         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33578         if(box && box.width){
33579             this.xy = [box.x, box.y];
33580             this.resizeTo(box.width, box.height);
33581         }
33582         return this;
33583     },
33584
33585     // private
33586     beforeShow : function(){
33587         this.expand();
33588         if(this.fixedcenter){
33589             this.xy = this.el.getCenterXY(true);
33590         }
33591         if(this.modal){
33592             Roo.get(document.body).addClass("x-body-masked");
33593             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33594             this.mask.show();
33595         }
33596         this.constrainXY();
33597     },
33598
33599     // private
33600     animShow : function(){
33601         var b = Roo.get(this.animateTarget).getBox();
33602         this.proxy.setSize(b.width, b.height);
33603         this.proxy.setLocation(b.x, b.y);
33604         this.proxy.show();
33605         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33606                     true, .35, this.showEl.createDelegate(this));
33607     },
33608
33609     /**
33610      * Shows the dialog.
33611      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33612      * @return {Roo.BasicDialog} this
33613      */
33614     show : function(animateTarget){
33615         if (this.fireEvent("beforeshow", this) === false){
33616             return;
33617         }
33618         if(this.syncHeightBeforeShow){
33619             this.syncBodyHeight();
33620         }else if(this.firstShow){
33621             this.firstShow = false;
33622             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33623         }
33624         this.animateTarget = animateTarget || this.animateTarget;
33625         if(!this.el.isVisible()){
33626             this.beforeShow();
33627             if(this.animateTarget && Roo.get(this.animateTarget)){
33628                 this.animShow();
33629             }else{
33630                 this.showEl();
33631             }
33632         }
33633         return this;
33634     },
33635
33636     // private
33637     showEl : function(){
33638         this.proxy.hide();
33639         this.el.setXY(this.xy);
33640         this.el.show();
33641         this.adjustAssets(true);
33642         this.toFront();
33643         this.focus();
33644         // IE peekaboo bug - fix found by Dave Fenwick
33645         if(Roo.isIE){
33646             this.el.repaint();
33647         }
33648         this.fireEvent("show", this);
33649     },
33650
33651     /**
33652      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33653      * dialog itself will receive focus.
33654      */
33655     focus : function(){
33656         if(this.defaultButton){
33657             this.defaultButton.focus();
33658         }else{
33659             this.focusEl.focus();
33660         }
33661     },
33662
33663     // private
33664     constrainXY : function(){
33665         if(this.constraintoviewport !== false){
33666             if(!this.viewSize){
33667                 if(this.container){
33668                     var s = this.container.getSize();
33669                     this.viewSize = [s.width, s.height];
33670                 }else{
33671                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33672                 }
33673             }
33674             var s = Roo.get(this.container||document).getScroll();
33675
33676             var x = this.xy[0], y = this.xy[1];
33677             var w = this.size.width, h = this.size.height;
33678             var vw = this.viewSize[0], vh = this.viewSize[1];
33679             // only move it if it needs it
33680             var moved = false;
33681             // first validate right/bottom
33682             if(x + w > vw+s.left){
33683                 x = vw - w;
33684                 moved = true;
33685             }
33686             if(y + h > vh+s.top){
33687                 y = vh - h;
33688                 moved = true;
33689             }
33690             // then make sure top/left isn't negative
33691             if(x < s.left){
33692                 x = s.left;
33693                 moved = true;
33694             }
33695             if(y < s.top){
33696                 y = s.top;
33697                 moved = true;
33698             }
33699             if(moved){
33700                 // cache xy
33701                 this.xy = [x, y];
33702                 if(this.isVisible()){
33703                     this.el.setLocation(x, y);
33704                     this.adjustAssets();
33705                 }
33706             }
33707         }
33708     },
33709
33710     // private
33711     onDrag : function(){
33712         if(!this.proxyDrag){
33713             this.xy = this.el.getXY();
33714             this.adjustAssets();
33715         }
33716     },
33717
33718     // private
33719     adjustAssets : function(doShow){
33720         var x = this.xy[0], y = this.xy[1];
33721         var w = this.size.width, h = this.size.height;
33722         if(doShow === true){
33723             if(this.shadow){
33724                 this.shadow.show(this.el);
33725             }
33726             if(this.shim){
33727                 this.shim.show();
33728             }
33729         }
33730         if(this.shadow && this.shadow.isVisible()){
33731             this.shadow.show(this.el);
33732         }
33733         if(this.shim && this.shim.isVisible()){
33734             this.shim.setBounds(x, y, w, h);
33735         }
33736     },
33737
33738     // private
33739     adjustViewport : function(w, h){
33740         if(!w || !h){
33741             w = Roo.lib.Dom.getViewWidth();
33742             h = Roo.lib.Dom.getViewHeight();
33743         }
33744         // cache the size
33745         this.viewSize = [w, h];
33746         if(this.modal && this.mask.isVisible()){
33747             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33748             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33749         }
33750         if(this.isVisible()){
33751             this.constrainXY();
33752         }
33753     },
33754
33755     /**
33756      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33757      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33758      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33759      */
33760     destroy : function(removeEl){
33761         if(this.isVisible()){
33762             this.animateTarget = null;
33763             this.hide();
33764         }
33765         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33766         if(this.tabs){
33767             this.tabs.destroy(removeEl);
33768         }
33769         Roo.destroy(
33770              this.shim,
33771              this.proxy,
33772              this.resizer,
33773              this.close,
33774              this.mask
33775         );
33776         if(this.dd){
33777             this.dd.unreg();
33778         }
33779         if(this.buttons){
33780            for(var i = 0, len = this.buttons.length; i < len; i++){
33781                this.buttons[i].destroy();
33782            }
33783         }
33784         this.el.removeAllListeners();
33785         if(removeEl === true){
33786             this.el.update("");
33787             this.el.remove();
33788         }
33789         Roo.DialogManager.unregister(this);
33790     },
33791
33792     // private
33793     startMove : function(){
33794         if(this.proxyDrag){
33795             this.proxy.show();
33796         }
33797         if(this.constraintoviewport !== false){
33798             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33799         }
33800     },
33801
33802     // private
33803     endMove : function(){
33804         if(!this.proxyDrag){
33805             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33806         }else{
33807             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33808             this.proxy.hide();
33809         }
33810         this.refreshSize();
33811         this.adjustAssets();
33812         this.focus();
33813         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33814     },
33815
33816     /**
33817      * Brings this dialog to the front of any other visible dialogs
33818      * @return {Roo.BasicDialog} this
33819      */
33820     toFront : function(){
33821         Roo.DialogManager.bringToFront(this);
33822         return this;
33823     },
33824
33825     /**
33826      * Sends this dialog to the back (under) of any other visible dialogs
33827      * @return {Roo.BasicDialog} this
33828      */
33829     toBack : function(){
33830         Roo.DialogManager.sendToBack(this);
33831         return this;
33832     },
33833
33834     /**
33835      * Centers this dialog in the viewport
33836      * @return {Roo.BasicDialog} this
33837      */
33838     center : function(){
33839         var xy = this.el.getCenterXY(true);
33840         this.moveTo(xy[0], xy[1]);
33841         return this;
33842     },
33843
33844     /**
33845      * Moves the dialog's top-left corner to the specified point
33846      * @param {Number} x
33847      * @param {Number} y
33848      * @return {Roo.BasicDialog} this
33849      */
33850     moveTo : function(x, y){
33851         this.xy = [x,y];
33852         if(this.isVisible()){
33853             this.el.setXY(this.xy);
33854             this.adjustAssets();
33855         }
33856         return this;
33857     },
33858
33859     /**
33860      * Aligns the dialog to the specified element
33861      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33862      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33863      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33864      * @return {Roo.BasicDialog} this
33865      */
33866     alignTo : function(element, position, offsets){
33867         this.xy = this.el.getAlignToXY(element, position, offsets);
33868         if(this.isVisible()){
33869             this.el.setXY(this.xy);
33870             this.adjustAssets();
33871         }
33872         return this;
33873     },
33874
33875     /**
33876      * Anchors an element to another element and realigns it when the window is resized.
33877      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33878      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33879      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33880      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33881      * is a number, it is used as the buffer delay (defaults to 50ms).
33882      * @return {Roo.BasicDialog} this
33883      */
33884     anchorTo : function(el, alignment, offsets, monitorScroll){
33885         var action = function(){
33886             this.alignTo(el, alignment, offsets);
33887         };
33888         Roo.EventManager.onWindowResize(action, this);
33889         var tm = typeof monitorScroll;
33890         if(tm != 'undefined'){
33891             Roo.EventManager.on(window, 'scroll', action, this,
33892                 {buffer: tm == 'number' ? monitorScroll : 50});
33893         }
33894         action.call(this);
33895         return this;
33896     },
33897
33898     /**
33899      * Returns true if the dialog is visible
33900      * @return {Boolean}
33901      */
33902     isVisible : function(){
33903         return this.el.isVisible();
33904     },
33905
33906     // private
33907     animHide : function(callback){
33908         var b = Roo.get(this.animateTarget).getBox();
33909         this.proxy.show();
33910         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33911         this.el.hide();
33912         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33913                     this.hideEl.createDelegate(this, [callback]));
33914     },
33915
33916     /**
33917      * Hides the dialog.
33918      * @param {Function} callback (optional) Function to call when the dialog is hidden
33919      * @return {Roo.BasicDialog} this
33920      */
33921     hide : function(callback){
33922         if (this.fireEvent("beforehide", this) === false){
33923             return;
33924         }
33925         if(this.shadow){
33926             this.shadow.hide();
33927         }
33928         if(this.shim) {
33929           this.shim.hide();
33930         }
33931         // sometimes animateTarget seems to get set.. causing problems...
33932         // this just double checks..
33933         if(this.animateTarget && Roo.get(this.animateTarget)) {
33934            this.animHide(callback);
33935         }else{
33936             this.el.hide();
33937             this.hideEl(callback);
33938         }
33939         return this;
33940     },
33941
33942     // private
33943     hideEl : function(callback){
33944         this.proxy.hide();
33945         if(this.modal){
33946             this.mask.hide();
33947             Roo.get(document.body).removeClass("x-body-masked");
33948         }
33949         this.fireEvent("hide", this);
33950         if(typeof callback == "function"){
33951             callback();
33952         }
33953     },
33954
33955     // private
33956     hideAction : function(){
33957         this.setLeft("-10000px");
33958         this.setTop("-10000px");
33959         this.setStyle("visibility", "hidden");
33960     },
33961
33962     // private
33963     refreshSize : function(){
33964         this.size = this.el.getSize();
33965         this.xy = this.el.getXY();
33966         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33967     },
33968
33969     // private
33970     // z-index is managed by the DialogManager and may be overwritten at any time
33971     setZIndex : function(index){
33972         if(this.modal){
33973             this.mask.setStyle("z-index", index);
33974         }
33975         if(this.shim){
33976             this.shim.setStyle("z-index", ++index);
33977         }
33978         if(this.shadow){
33979             this.shadow.setZIndex(++index);
33980         }
33981         this.el.setStyle("z-index", ++index);
33982         if(this.proxy){
33983             this.proxy.setStyle("z-index", ++index);
33984         }
33985         if(this.resizer){
33986             this.resizer.proxy.setStyle("z-index", ++index);
33987         }
33988
33989         this.lastZIndex = index;
33990     },
33991
33992     /**
33993      * Returns the element for this dialog
33994      * @return {Roo.Element} The underlying dialog Element
33995      */
33996     getEl : function(){
33997         return this.el;
33998     }
33999 });
34000
34001 /**
34002  * @class Roo.DialogManager
34003  * Provides global access to BasicDialogs that have been created and
34004  * support for z-indexing (layering) multiple open dialogs.
34005  */
34006 Roo.DialogManager = function(){
34007     var list = {};
34008     var accessList = [];
34009     var front = null;
34010
34011     // private
34012     var sortDialogs = function(d1, d2){
34013         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34014     };
34015
34016     // private
34017     var orderDialogs = function(){
34018         accessList.sort(sortDialogs);
34019         var seed = Roo.DialogManager.zseed;
34020         for(var i = 0, len = accessList.length; i < len; i++){
34021             var dlg = accessList[i];
34022             if(dlg){
34023                 dlg.setZIndex(seed + (i*10));
34024             }
34025         }
34026     };
34027
34028     return {
34029         /**
34030          * The starting z-index for BasicDialogs (defaults to 9000)
34031          * @type Number The z-index value
34032          */
34033         zseed : 9000,
34034
34035         // private
34036         register : function(dlg){
34037             list[dlg.id] = dlg;
34038             accessList.push(dlg);
34039         },
34040
34041         // private
34042         unregister : function(dlg){
34043             delete list[dlg.id];
34044             var i=0;
34045             var len=0;
34046             if(!accessList.indexOf){
34047                 for(  i = 0, len = accessList.length; i < len; i++){
34048                     if(accessList[i] == dlg){
34049                         accessList.splice(i, 1);
34050                         return;
34051                     }
34052                 }
34053             }else{
34054                  i = accessList.indexOf(dlg);
34055                 if(i != -1){
34056                     accessList.splice(i, 1);
34057                 }
34058             }
34059         },
34060
34061         /**
34062          * Gets a registered dialog by id
34063          * @param {String/Object} id The id of the dialog or a dialog
34064          * @return {Roo.BasicDialog} this
34065          */
34066         get : function(id){
34067             return typeof id == "object" ? id : list[id];
34068         },
34069
34070         /**
34071          * Brings the specified dialog to the front
34072          * @param {String/Object} dlg The id of the dialog or a dialog
34073          * @return {Roo.BasicDialog} this
34074          */
34075         bringToFront : function(dlg){
34076             dlg = this.get(dlg);
34077             if(dlg != front){
34078                 front = dlg;
34079                 dlg._lastAccess = new Date().getTime();
34080                 orderDialogs();
34081             }
34082             return dlg;
34083         },
34084
34085         /**
34086          * Sends the specified dialog to the back
34087          * @param {String/Object} dlg The id of the dialog or a dialog
34088          * @return {Roo.BasicDialog} this
34089          */
34090         sendToBack : function(dlg){
34091             dlg = this.get(dlg);
34092             dlg._lastAccess = -(new Date().getTime());
34093             orderDialogs();
34094             return dlg;
34095         },
34096
34097         /**
34098          * Hides all dialogs
34099          */
34100         hideAll : function(){
34101             for(var id in list){
34102                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34103                     list[id].hide();
34104                 }
34105             }
34106         }
34107     };
34108 }();
34109
34110 /**
34111  * @class Roo.LayoutDialog
34112  * @extends Roo.BasicDialog
34113  * @children Roo.ContentPanel
34114  * @parent builder none
34115  * Dialog which provides adjustments for working with a layout in a Dialog.
34116  * Add your necessary layout config options to the dialog's config.<br>
34117  * Example usage (including a nested layout):
34118  * <pre><code>
34119 if(!dialog){
34120     dialog = new Roo.LayoutDialog("download-dlg", {
34121         modal: true,
34122         width:600,
34123         height:450,
34124         shadow:true,
34125         minWidth:500,
34126         minHeight:350,
34127         autoTabs:true,
34128         proxyDrag:true,
34129         // layout config merges with the dialog config
34130         center:{
34131             tabPosition: "top",
34132             alwaysShowTabs: true
34133         }
34134     });
34135     dialog.addKeyListener(27, dialog.hide, dialog);
34136     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34137     dialog.addButton("Build It!", this.getDownload, this);
34138
34139     // we can even add nested layouts
34140     var innerLayout = new Roo.BorderLayout("dl-inner", {
34141         east: {
34142             initialSize: 200,
34143             autoScroll:true,
34144             split:true
34145         },
34146         center: {
34147             autoScroll:true
34148         }
34149     });
34150     innerLayout.beginUpdate();
34151     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34152     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34153     innerLayout.endUpdate(true);
34154
34155     var layout = dialog.getLayout();
34156     layout.beginUpdate();
34157     layout.add("center", new Roo.ContentPanel("standard-panel",
34158                         {title: "Download the Source", fitToFrame:true}));
34159     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34160                {title: "Build your own roo.js"}));
34161     layout.getRegion("center").showPanel(sp);
34162     layout.endUpdate();
34163 }
34164 </code></pre>
34165     * @constructor
34166     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34167     * @param {Object} config configuration options
34168   */
34169 Roo.LayoutDialog = function(el, cfg){
34170     
34171     var config=  cfg;
34172     if (typeof(cfg) == 'undefined') {
34173         config = Roo.apply({}, el);
34174         // not sure why we use documentElement here.. - it should always be body.
34175         // IE7 borks horribly if we use documentElement.
34176         // webkit also does not like documentElement - it creates a body element...
34177         el = Roo.get( document.body || document.documentElement ).createChild();
34178         //config.autoCreate = true;
34179     }
34180     
34181     
34182     config.autoTabs = false;
34183     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34184     this.body.setStyle({overflow:"hidden", position:"relative"});
34185     this.layout = new Roo.BorderLayout(this.body.dom, config);
34186     this.layout.monitorWindowResize = false;
34187     this.el.addClass("x-dlg-auto-layout");
34188     // fix case when center region overwrites center function
34189     this.center = Roo.BasicDialog.prototype.center;
34190     this.on("show", this.layout.layout, this.layout, true);
34191     if (config.items) {
34192         var xitems = config.items;
34193         delete config.items;
34194         Roo.each(xitems, this.addxtype, this);
34195     }
34196     
34197     
34198 };
34199 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34200     
34201     
34202     /**
34203      * @cfg {Roo.LayoutRegion} east  
34204      */
34205     /**
34206      * @cfg {Roo.LayoutRegion} west
34207      */
34208     /**
34209      * @cfg {Roo.LayoutRegion} south
34210      */
34211     /**
34212      * @cfg {Roo.LayoutRegion} north
34213      */
34214     /**
34215      * @cfg {Roo.LayoutRegion} center
34216      */
34217     /**
34218      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34219      */
34220     
34221     
34222     /**
34223      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34224      * @deprecated
34225      */
34226     endUpdate : function(){
34227         this.layout.endUpdate();
34228     },
34229
34230     /**
34231      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34232      *  @deprecated
34233      */
34234     beginUpdate : function(){
34235         this.layout.beginUpdate();
34236     },
34237
34238     /**
34239      * Get the BorderLayout for this dialog
34240      * @return {Roo.BorderLayout}
34241      */
34242     getLayout : function(){
34243         return this.layout;
34244     },
34245
34246     showEl : function(){
34247         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34248         if(Roo.isIE7){
34249             this.layout.layout();
34250         }
34251     },
34252
34253     // private
34254     // Use the syncHeightBeforeShow config option to control this automatically
34255     syncBodyHeight : function(){
34256         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34257         if(this.layout){this.layout.layout();}
34258     },
34259     
34260       /**
34261      * Add an xtype element (actually adds to the layout.)
34262      * @return {Object} xdata xtype object data.
34263      */
34264     
34265     addxtype : function(c) {
34266         return this.layout.addxtype(c);
34267     }
34268 });/*
34269  * Based on:
34270  * Ext JS Library 1.1.1
34271  * Copyright(c) 2006-2007, Ext JS, LLC.
34272  *
34273  * Originally Released Under LGPL - original licence link has changed is not relivant.
34274  *
34275  * Fork - LGPL
34276  * <script type="text/javascript">
34277  */
34278  
34279 /**
34280  * @class Roo.MessageBox
34281  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34282  * Example usage:
34283  *<pre><code>
34284 // Basic alert:
34285 Roo.Msg.alert('Status', 'Changes saved successfully.');
34286
34287 // Prompt for user data:
34288 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34289     if (btn == 'ok'){
34290         // process text value...
34291     }
34292 });
34293
34294 // Show a dialog using config options:
34295 Roo.Msg.show({
34296    title:'Save Changes?',
34297    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34298    buttons: Roo.Msg.YESNOCANCEL,
34299    fn: processResult,
34300    animEl: 'elId'
34301 });
34302 </code></pre>
34303  * @static
34304  */
34305 Roo.MessageBox = function(){
34306     var dlg, opt, mask, waitTimer;
34307     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34308     var buttons, activeTextEl, bwidth;
34309
34310     // private
34311     var handleButton = function(button){
34312         dlg.hide();
34313         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34314     };
34315
34316     // private
34317     var handleHide = function(){
34318         if(opt && opt.cls){
34319             dlg.el.removeClass(opt.cls);
34320         }
34321         if(waitTimer){
34322             Roo.TaskMgr.stop(waitTimer);
34323             waitTimer = null;
34324         }
34325     };
34326
34327     // private
34328     var updateButtons = function(b){
34329         var width = 0;
34330         if(!b){
34331             buttons["ok"].hide();
34332             buttons["cancel"].hide();
34333             buttons["yes"].hide();
34334             buttons["no"].hide();
34335             dlg.footer.dom.style.display = 'none';
34336             return width;
34337         }
34338         dlg.footer.dom.style.display = '';
34339         for(var k in buttons){
34340             if(typeof buttons[k] != "function"){
34341                 if(b[k]){
34342                     buttons[k].show();
34343                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34344                     width += buttons[k].el.getWidth()+15;
34345                 }else{
34346                     buttons[k].hide();
34347                 }
34348             }
34349         }
34350         return width;
34351     };
34352
34353     // private
34354     var handleEsc = function(d, k, e){
34355         if(opt && opt.closable !== false){
34356             dlg.hide();
34357         }
34358         if(e){
34359             e.stopEvent();
34360         }
34361     };
34362
34363     return {
34364         /**
34365          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34366          * @return {Roo.BasicDialog} The BasicDialog element
34367          */
34368         getDialog : function(){
34369            if(!dlg){
34370                 dlg = new Roo.BasicDialog("x-msg-box", {
34371                     autoCreate : true,
34372                     shadow: true,
34373                     draggable: true,
34374                     resizable:false,
34375                     constraintoviewport:false,
34376                     fixedcenter:true,
34377                     collapsible : false,
34378                     shim:true,
34379                     modal: true,
34380                     width:400, height:100,
34381                     buttonAlign:"center",
34382                     closeClick : function(){
34383                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34384                             handleButton("no");
34385                         }else{
34386                             handleButton("cancel");
34387                         }
34388                     }
34389                 });
34390                 dlg.on("hide", handleHide);
34391                 mask = dlg.mask;
34392                 dlg.addKeyListener(27, handleEsc);
34393                 buttons = {};
34394                 var bt = this.buttonText;
34395                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34396                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34397                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34398                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34399                 bodyEl = dlg.body.createChild({
34400
34401                     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>'
34402                 });
34403                 msgEl = bodyEl.dom.firstChild;
34404                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34405                 textboxEl.enableDisplayMode();
34406                 textboxEl.addKeyListener([10,13], function(){
34407                     if(dlg.isVisible() && opt && opt.buttons){
34408                         if(opt.buttons.ok){
34409                             handleButton("ok");
34410                         }else if(opt.buttons.yes){
34411                             handleButton("yes");
34412                         }
34413                     }
34414                 });
34415                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34416                 textareaEl.enableDisplayMode();
34417                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34418                 progressEl.enableDisplayMode();
34419                 var pf = progressEl.dom.firstChild;
34420                 if (pf) {
34421                     pp = Roo.get(pf.firstChild);
34422                     pp.setHeight(pf.offsetHeight);
34423                 }
34424                 
34425             }
34426             return dlg;
34427         },
34428
34429         /**
34430          * Updates the message box body text
34431          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34432          * the XHTML-compliant non-breaking space character '&amp;#160;')
34433          * @return {Roo.MessageBox} This message box
34434          */
34435         updateText : function(text){
34436             if(!dlg.isVisible() && !opt.width){
34437                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34438             }
34439             msgEl.innerHTML = text || '&#160;';
34440       
34441             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34442             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34443             var w = Math.max(
34444                     Math.min(opt.width || cw , this.maxWidth), 
34445                     Math.max(opt.minWidth || this.minWidth, bwidth)
34446             );
34447             if(opt.prompt){
34448                 activeTextEl.setWidth(w);
34449             }
34450             if(dlg.isVisible()){
34451                 dlg.fixedcenter = false;
34452             }
34453             // to big, make it scroll. = But as usual stupid IE does not support
34454             // !important..
34455             
34456             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34457                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34458                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34459             } else {
34460                 bodyEl.dom.style.height = '';
34461                 bodyEl.dom.style.overflowY = '';
34462             }
34463             if (cw > w) {
34464                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34465             } else {
34466                 bodyEl.dom.style.overflowX = '';
34467             }
34468             
34469             dlg.setContentSize(w, bodyEl.getHeight());
34470             if(dlg.isVisible()){
34471                 dlg.fixedcenter = true;
34472             }
34473             return this;
34474         },
34475
34476         /**
34477          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34478          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34479          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34480          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34481          * @return {Roo.MessageBox} This message box
34482          */
34483         updateProgress : function(value, text){
34484             if(text){
34485                 this.updateText(text);
34486             }
34487             if (pp) { // weird bug on my firefox - for some reason this is not defined
34488                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34489             }
34490             return this;
34491         },        
34492
34493         /**
34494          * Returns true if the message box is currently displayed
34495          * @return {Boolean} True if the message box is visible, else false
34496          */
34497         isVisible : function(){
34498             return dlg && dlg.isVisible();  
34499         },
34500
34501         /**
34502          * Hides the message box if it is displayed
34503          */
34504         hide : function(){
34505             if(this.isVisible()){
34506                 dlg.hide();
34507             }  
34508         },
34509
34510         /**
34511          * Displays a new message box, or reinitializes an existing message box, based on the config options
34512          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34513          * The following config object properties are supported:
34514          * <pre>
34515 Property    Type             Description
34516 ----------  ---------------  ------------------------------------------------------------------------------------
34517 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34518                                    closes (defaults to undefined)
34519 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34520                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34521 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34522                                    progress and wait dialogs will ignore this property and always hide the
34523                                    close button as they can only be closed programmatically.
34524 cls               String           A custom CSS class to apply to the message box element
34525 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34526                                    displayed (defaults to 75)
34527 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34528                                    function will be btn (the name of the button that was clicked, if applicable,
34529                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34530                                    Progress and wait dialogs will ignore this option since they do not respond to
34531                                    user actions and can only be closed programmatically, so any required function
34532                                    should be called by the same code after it closes the dialog.
34533 icon              String           A CSS class that provides a background image to be used as an icon for
34534                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34535 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34536 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34537 modal             Boolean          False to allow user interaction with the page while the message box is
34538                                    displayed (defaults to true)
34539 msg               String           A string that will replace the existing message box body text (defaults
34540                                    to the XHTML-compliant non-breaking space character '&#160;')
34541 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34542 progress          Boolean          True to display a progress bar (defaults to false)
34543 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34544 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34545 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34546 title             String           The title text
34547 value             String           The string value to set into the active textbox element if displayed
34548 wait              Boolean          True to display a progress bar (defaults to false)
34549 width             Number           The width of the dialog in pixels
34550 </pre>
34551          *
34552          * Example usage:
34553          * <pre><code>
34554 Roo.Msg.show({
34555    title: 'Address',
34556    msg: 'Please enter your address:',
34557    width: 300,
34558    buttons: Roo.MessageBox.OKCANCEL,
34559    multiline: true,
34560    fn: saveAddress,
34561    animEl: 'addAddressBtn'
34562 });
34563 </code></pre>
34564          * @param {Object} config Configuration options
34565          * @return {Roo.MessageBox} This message box
34566          */
34567         show : function(options)
34568         {
34569             
34570             // this causes nightmares if you show one dialog after another
34571             // especially on callbacks..
34572              
34573             if(this.isVisible()){
34574                 
34575                 this.hide();
34576                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34577                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34578                 Roo.log("New Dialog Message:" +  options.msg )
34579                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34580                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34581                 
34582             }
34583             var d = this.getDialog();
34584             opt = options;
34585             d.setTitle(opt.title || "&#160;");
34586             d.close.setDisplayed(opt.closable !== false);
34587             activeTextEl = textboxEl;
34588             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34589             if(opt.prompt){
34590                 if(opt.multiline){
34591                     textboxEl.hide();
34592                     textareaEl.show();
34593                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34594                         opt.multiline : this.defaultTextHeight);
34595                     activeTextEl = textareaEl;
34596                 }else{
34597                     textboxEl.show();
34598                     textareaEl.hide();
34599                 }
34600             }else{
34601                 textboxEl.hide();
34602                 textareaEl.hide();
34603             }
34604             progressEl.setDisplayed(opt.progress === true);
34605             this.updateProgress(0);
34606             activeTextEl.dom.value = opt.value || "";
34607             if(opt.prompt){
34608                 dlg.setDefaultButton(activeTextEl);
34609             }else{
34610                 var bs = opt.buttons;
34611                 var db = null;
34612                 if(bs && bs.ok){
34613                     db = buttons["ok"];
34614                 }else if(bs && bs.yes){
34615                     db = buttons["yes"];
34616                 }
34617                 dlg.setDefaultButton(db);
34618             }
34619             bwidth = updateButtons(opt.buttons);
34620             this.updateText(opt.msg);
34621             if(opt.cls){
34622                 d.el.addClass(opt.cls);
34623             }
34624             d.proxyDrag = opt.proxyDrag === true;
34625             d.modal = opt.modal !== false;
34626             d.mask = opt.modal !== false ? mask : false;
34627             if(!d.isVisible()){
34628                 // force it to the end of the z-index stack so it gets a cursor in FF
34629                 document.body.appendChild(dlg.el.dom);
34630                 d.animateTarget = null;
34631                 d.show(options.animEl);
34632             }
34633             return this;
34634         },
34635
34636         /**
34637          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34638          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34639          * and closing the message box when the process is complete.
34640          * @param {String} title The title bar text
34641          * @param {String} msg The message box body text
34642          * @return {Roo.MessageBox} This message box
34643          */
34644         progress : function(title, msg){
34645             this.show({
34646                 title : title,
34647                 msg : msg,
34648                 buttons: false,
34649                 progress:true,
34650                 closable:false,
34651                 minWidth: this.minProgressWidth,
34652                 modal : true
34653             });
34654             return this;
34655         },
34656
34657         /**
34658          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34659          * If a callback function is passed it will be called after the user clicks the button, and the
34660          * id of the button that was clicked will be passed as the only parameter to the callback
34661          * (could also be the top-right close button).
34662          * @param {String} title The title bar text
34663          * @param {String} msg The message box body text
34664          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34665          * @param {Object} scope (optional) The scope of the callback function
34666          * @return {Roo.MessageBox} This message box
34667          */
34668         alert : function(title, msg, fn, scope){
34669             this.show({
34670                 title : title,
34671                 msg : msg,
34672                 buttons: this.OK,
34673                 fn: fn,
34674                 scope : scope,
34675                 modal : true
34676             });
34677             return this;
34678         },
34679
34680         /**
34681          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34682          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34683          * You are responsible for closing the message box when the process is complete.
34684          * @param {String} msg The message box body text
34685          * @param {String} title (optional) The title bar text
34686          * @return {Roo.MessageBox} This message box
34687          */
34688         wait : function(msg, title){
34689             this.show({
34690                 title : title,
34691                 msg : msg,
34692                 buttons: false,
34693                 closable:false,
34694                 progress:true,
34695                 modal:true,
34696                 width:300,
34697                 wait:true
34698             });
34699             waitTimer = Roo.TaskMgr.start({
34700                 run: function(i){
34701                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34702                 },
34703                 interval: 1000
34704             });
34705             return this;
34706         },
34707
34708         /**
34709          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34710          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34711          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34712          * @param {String} title The title bar text
34713          * @param {String} msg The message box body text
34714          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34715          * @param {Object} scope (optional) The scope of the callback function
34716          * @return {Roo.MessageBox} This message box
34717          */
34718         confirm : function(title, msg, fn, scope){
34719             this.show({
34720                 title : title,
34721                 msg : msg,
34722                 buttons: this.YESNO,
34723                 fn: fn,
34724                 scope : scope,
34725                 modal : true
34726             });
34727             return this;
34728         },
34729
34730         /**
34731          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34732          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34733          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34734          * (could also be the top-right close button) and the text that was entered will be passed as the two
34735          * parameters to the callback.
34736          * @param {String} title The title bar text
34737          * @param {String} msg The message box body text
34738          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34739          * @param {Object} scope (optional) The scope of the callback function
34740          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34741          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34742          * @return {Roo.MessageBox} This message box
34743          */
34744         prompt : function(title, msg, fn, scope, multiline){
34745             this.show({
34746                 title : title,
34747                 msg : msg,
34748                 buttons: this.OKCANCEL,
34749                 fn: fn,
34750                 minWidth:250,
34751                 scope : scope,
34752                 prompt:true,
34753                 multiline: multiline,
34754                 modal : true
34755             });
34756             return this;
34757         },
34758
34759         /**
34760          * Button config that displays a single OK button
34761          * @type Object
34762          */
34763         OK : {ok:true},
34764         /**
34765          * Button config that displays Yes and No buttons
34766          * @type Object
34767          */
34768         YESNO : {yes:true, no:true},
34769         /**
34770          * Button config that displays OK and Cancel buttons
34771          * @type Object
34772          */
34773         OKCANCEL : {ok:true, cancel:true},
34774         /**
34775          * Button config that displays Yes, No and Cancel buttons
34776          * @type Object
34777          */
34778         YESNOCANCEL : {yes:true, no:true, cancel:true},
34779
34780         /**
34781          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34782          * @type Number
34783          */
34784         defaultTextHeight : 75,
34785         /**
34786          * The maximum width in pixels of the message box (defaults to 600)
34787          * @type Number
34788          */
34789         maxWidth : 600,
34790         /**
34791          * The minimum width in pixels of the message box (defaults to 100)
34792          * @type Number
34793          */
34794         minWidth : 100,
34795         /**
34796          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34797          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34798          * @type Number
34799          */
34800         minProgressWidth : 250,
34801         /**
34802          * An object containing the default button text strings that can be overriden for localized language support.
34803          * Supported properties are: ok, cancel, yes and no.
34804          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34805          * @type Object
34806          */
34807         buttonText : {
34808             ok : "OK",
34809             cancel : "Cancel",
34810             yes : "Yes",
34811             no : "No"
34812         }
34813     };
34814 }();
34815
34816 /**
34817  * Shorthand for {@link Roo.MessageBox}
34818  */
34819 Roo.Msg = Roo.MessageBox;/*
34820  * Based on:
34821  * Ext JS Library 1.1.1
34822  * Copyright(c) 2006-2007, Ext JS, LLC.
34823  *
34824  * Originally Released Under LGPL - original licence link has changed is not relivant.
34825  *
34826  * Fork - LGPL
34827  * <script type="text/javascript">
34828  */
34829 /**
34830  * @class Roo.QuickTips
34831  * Provides attractive and customizable tooltips for any element.
34832  * @static
34833  */
34834 Roo.QuickTips = function(){
34835     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34836     var ce, bd, xy, dd;
34837     var visible = false, disabled = true, inited = false;
34838     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34839     
34840     var onOver = function(e){
34841         if(disabled){
34842             return;
34843         }
34844         var t = e.getTarget();
34845         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34846             return;
34847         }
34848         if(ce && t == ce.el){
34849             clearTimeout(hideProc);
34850             return;
34851         }
34852         if(t && tagEls[t.id]){
34853             tagEls[t.id].el = t;
34854             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34855             return;
34856         }
34857         var ttp, et = Roo.fly(t);
34858         var ns = cfg.namespace;
34859         if(tm.interceptTitles && t.title){
34860             ttp = t.title;
34861             t.qtip = ttp;
34862             t.removeAttribute("title");
34863             e.preventDefault();
34864         }else{
34865             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34866         }
34867         if(ttp){
34868             showProc = show.defer(tm.showDelay, tm, [{
34869                 el: t, 
34870                 text: ttp.replace(/\\n/g,'<br/>'),
34871                 width: et.getAttributeNS(ns, cfg.width),
34872                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34873                 title: et.getAttributeNS(ns, cfg.title),
34874                     cls: et.getAttributeNS(ns, cfg.cls)
34875             }]);
34876         }
34877     };
34878     
34879     var onOut = function(e){
34880         clearTimeout(showProc);
34881         var t = e.getTarget();
34882         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34883             hideProc = setTimeout(hide, tm.hideDelay);
34884         }
34885     };
34886     
34887     var onMove = function(e){
34888         if(disabled){
34889             return;
34890         }
34891         xy = e.getXY();
34892         xy[1] += 18;
34893         if(tm.trackMouse && ce){
34894             el.setXY(xy);
34895         }
34896     };
34897     
34898     var onDown = function(e){
34899         clearTimeout(showProc);
34900         clearTimeout(hideProc);
34901         if(!e.within(el)){
34902             if(tm.hideOnClick){
34903                 hide();
34904                 tm.disable();
34905                 tm.enable.defer(100, tm);
34906             }
34907         }
34908     };
34909     
34910     var getPad = function(){
34911         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34912     };
34913
34914     var show = function(o){
34915         if(disabled){
34916             return;
34917         }
34918         clearTimeout(dismissProc);
34919         ce = o;
34920         if(removeCls){ // in case manually hidden
34921             el.removeClass(removeCls);
34922             removeCls = null;
34923         }
34924         if(ce.cls){
34925             el.addClass(ce.cls);
34926             removeCls = ce.cls;
34927         }
34928         if(ce.title){
34929             tipTitle.update(ce.title);
34930             tipTitle.show();
34931         }else{
34932             tipTitle.update('');
34933             tipTitle.hide();
34934         }
34935         el.dom.style.width  = tm.maxWidth+'px';
34936         //tipBody.dom.style.width = '';
34937         tipBodyText.update(o.text);
34938         var p = getPad(), w = ce.width;
34939         if(!w){
34940             var td = tipBodyText.dom;
34941             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34942             if(aw > tm.maxWidth){
34943                 w = tm.maxWidth;
34944             }else if(aw < tm.minWidth){
34945                 w = tm.minWidth;
34946             }else{
34947                 w = aw;
34948             }
34949         }
34950         //tipBody.setWidth(w);
34951         el.setWidth(parseInt(w, 10) + p);
34952         if(ce.autoHide === false){
34953             close.setDisplayed(true);
34954             if(dd){
34955                 dd.unlock();
34956             }
34957         }else{
34958             close.setDisplayed(false);
34959             if(dd){
34960                 dd.lock();
34961             }
34962         }
34963         if(xy){
34964             el.avoidY = xy[1]-18;
34965             el.setXY(xy);
34966         }
34967         if(tm.animate){
34968             el.setOpacity(.1);
34969             el.setStyle("visibility", "visible");
34970             el.fadeIn({callback: afterShow});
34971         }else{
34972             afterShow();
34973         }
34974     };
34975     
34976     var afterShow = function(){
34977         if(ce){
34978             el.show();
34979             esc.enable();
34980             if(tm.autoDismiss && ce.autoHide !== false){
34981                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34982             }
34983         }
34984     };
34985     
34986     var hide = function(noanim){
34987         clearTimeout(dismissProc);
34988         clearTimeout(hideProc);
34989         ce = null;
34990         if(el.isVisible()){
34991             esc.disable();
34992             if(noanim !== true && tm.animate){
34993                 el.fadeOut({callback: afterHide});
34994             }else{
34995                 afterHide();
34996             } 
34997         }
34998     };
34999     
35000     var afterHide = function(){
35001         el.hide();
35002         if(removeCls){
35003             el.removeClass(removeCls);
35004             removeCls = null;
35005         }
35006     };
35007     
35008     return {
35009         /**
35010         * @cfg {Number} minWidth
35011         * The minimum width of the quick tip (defaults to 40)
35012         */
35013        minWidth : 40,
35014         /**
35015         * @cfg {Number} maxWidth
35016         * The maximum width of the quick tip (defaults to 300)
35017         */
35018        maxWidth : 300,
35019         /**
35020         * @cfg {Boolean} interceptTitles
35021         * True to automatically use the element's DOM title value if available (defaults to false)
35022         */
35023        interceptTitles : false,
35024         /**
35025         * @cfg {Boolean} trackMouse
35026         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35027         */
35028        trackMouse : false,
35029         /**
35030         * @cfg {Boolean} hideOnClick
35031         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35032         */
35033        hideOnClick : true,
35034         /**
35035         * @cfg {Number} showDelay
35036         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35037         */
35038        showDelay : 500,
35039         /**
35040         * @cfg {Number} hideDelay
35041         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35042         */
35043        hideDelay : 200,
35044         /**
35045         * @cfg {Boolean} autoHide
35046         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35047         * Used in conjunction with hideDelay.
35048         */
35049        autoHide : true,
35050         /**
35051         * @cfg {Boolean}
35052         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35053         * (defaults to true).  Used in conjunction with autoDismissDelay.
35054         */
35055        autoDismiss : true,
35056         /**
35057         * @cfg {Number}
35058         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35059         */
35060        autoDismissDelay : 5000,
35061        /**
35062         * @cfg {Boolean} animate
35063         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35064         */
35065        animate : false,
35066
35067        /**
35068         * @cfg {String} title
35069         * Title text to display (defaults to '').  This can be any valid HTML markup.
35070         */
35071         title: '',
35072        /**
35073         * @cfg {String} text
35074         * Body text to display (defaults to '').  This can be any valid HTML markup.
35075         */
35076         text : '',
35077        /**
35078         * @cfg {String} cls
35079         * A CSS class to apply to the base quick tip element (defaults to '').
35080         */
35081         cls : '',
35082        /**
35083         * @cfg {Number} width
35084         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35085         * minWidth or maxWidth.
35086         */
35087         width : null,
35088
35089     /**
35090      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35091      * or display QuickTips in a page.
35092      */
35093        init : function(){
35094           tm = Roo.QuickTips;
35095           cfg = tm.tagConfig;
35096           if(!inited){
35097               if(!Roo.isReady){ // allow calling of init() before onReady
35098                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35099                   return;
35100               }
35101               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35102               el.fxDefaults = {stopFx: true};
35103               // maximum custom styling
35104               //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>');
35105               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>');              
35106               tipTitle = el.child('h3');
35107               tipTitle.enableDisplayMode("block");
35108               tipBody = el.child('div.x-tip-bd');
35109               tipBodyText = el.child('div.x-tip-bd-inner');
35110               //bdLeft = el.child('div.x-tip-bd-left');
35111               //bdRight = el.child('div.x-tip-bd-right');
35112               close = el.child('div.x-tip-close');
35113               close.enableDisplayMode("block");
35114               close.on("click", hide);
35115               var d = Roo.get(document);
35116               d.on("mousedown", onDown);
35117               d.on("mouseover", onOver);
35118               d.on("mouseout", onOut);
35119               d.on("mousemove", onMove);
35120               esc = d.addKeyListener(27, hide);
35121               esc.disable();
35122               if(Roo.dd.DD){
35123                   dd = el.initDD("default", null, {
35124                       onDrag : function(){
35125                           el.sync();  
35126                       }
35127                   });
35128                   dd.setHandleElId(tipTitle.id);
35129                   dd.lock();
35130               }
35131               inited = true;
35132           }
35133           this.enable(); 
35134        },
35135
35136     /**
35137      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35138      * are supported:
35139      * <pre>
35140 Property    Type                   Description
35141 ----------  ---------------------  ------------------------------------------------------------------------
35142 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35143      * </ul>
35144      * @param {Object} config The config object
35145      */
35146        register : function(config){
35147            var cs = config instanceof Array ? config : arguments;
35148            for(var i = 0, len = cs.length; i < len; i++) {
35149                var c = cs[i];
35150                var target = c.target;
35151                if(target){
35152                    if(target instanceof Array){
35153                        for(var j = 0, jlen = target.length; j < jlen; j++){
35154                            tagEls[target[j]] = c;
35155                        }
35156                    }else{
35157                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35158                    }
35159                }
35160            }
35161        },
35162
35163     /**
35164      * Removes this quick tip from its element and destroys it.
35165      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35166      */
35167        unregister : function(el){
35168            delete tagEls[Roo.id(el)];
35169        },
35170
35171     /**
35172      * Enable this quick tip.
35173      */
35174        enable : function(){
35175            if(inited && disabled){
35176                locks.pop();
35177                if(locks.length < 1){
35178                    disabled = false;
35179                }
35180            }
35181        },
35182
35183     /**
35184      * Disable this quick tip.
35185      */
35186        disable : function(){
35187           disabled = true;
35188           clearTimeout(showProc);
35189           clearTimeout(hideProc);
35190           clearTimeout(dismissProc);
35191           if(ce){
35192               hide(true);
35193           }
35194           locks.push(1);
35195        },
35196
35197     /**
35198      * Returns true if the quick tip is enabled, else false.
35199      */
35200        isEnabled : function(){
35201             return !disabled;
35202        },
35203
35204         // private
35205        tagConfig : {
35206            namespace : "roo", // was ext?? this may break..
35207            alt_namespace : "ext",
35208            attribute : "qtip",
35209            width : "width",
35210            target : "target",
35211            title : "qtitle",
35212            hide : "hide",
35213            cls : "qclass"
35214        }
35215    };
35216 }();
35217
35218 // backwards compat
35219 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35220  * Based on:
35221  * Ext JS Library 1.1.1
35222  * Copyright(c) 2006-2007, Ext JS, LLC.
35223  *
35224  * Originally Released Under LGPL - original licence link has changed is not relivant.
35225  *
35226  * Fork - LGPL
35227  * <script type="text/javascript">
35228  */
35229  
35230
35231 /**
35232  * @class Roo.tree.TreePanel
35233  * @extends Roo.data.Tree
35234  * @cfg {Roo.tree.TreeNode} root The root node
35235  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35236  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35237  * @cfg {Boolean} enableDD true to enable drag and drop
35238  * @cfg {Boolean} enableDrag true to enable just drag
35239  * @cfg {Boolean} enableDrop true to enable just drop
35240  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35241  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35242  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35243  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35244  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35245  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35246  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35247  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35248  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35249  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35250  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35251  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35252  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35253  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35254  * @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>
35255  * @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>
35256  * 
35257  * @constructor
35258  * @param {String/HTMLElement/Element} el The container element
35259  * @param {Object} config
35260  */
35261 Roo.tree.TreePanel = function(el, config){
35262     var root = false;
35263     var loader = false;
35264     if (config.root) {
35265         root = config.root;
35266         delete config.root;
35267     }
35268     if (config.loader) {
35269         loader = config.loader;
35270         delete config.loader;
35271     }
35272     
35273     Roo.apply(this, config);
35274     Roo.tree.TreePanel.superclass.constructor.call(this);
35275     this.el = Roo.get(el);
35276     this.el.addClass('x-tree');
35277     //console.log(root);
35278     if (root) {
35279         this.setRootNode( Roo.factory(root, Roo.tree));
35280     }
35281     if (loader) {
35282         this.loader = Roo.factory(loader, Roo.tree);
35283     }
35284    /**
35285     * Read-only. The id of the container element becomes this TreePanel's id.
35286     */
35287     this.id = this.el.id;
35288     this.addEvents({
35289         /**
35290         * @event beforeload
35291         * Fires before a node is loaded, return false to cancel
35292         * @param {Node} node The node being loaded
35293         */
35294         "beforeload" : true,
35295         /**
35296         * @event load
35297         * Fires when a node is loaded
35298         * @param {Node} node The node that was loaded
35299         */
35300         "load" : true,
35301         /**
35302         * @event textchange
35303         * Fires when the text for a node is changed
35304         * @param {Node} node The node
35305         * @param {String} text The new text
35306         * @param {String} oldText The old text
35307         */
35308         "textchange" : true,
35309         /**
35310         * @event beforeexpand
35311         * Fires before a node is expanded, return false to cancel.
35312         * @param {Node} node The node
35313         * @param {Boolean} deep
35314         * @param {Boolean} anim
35315         */
35316         "beforeexpand" : true,
35317         /**
35318         * @event beforecollapse
35319         * Fires before a node is collapsed, return false to cancel.
35320         * @param {Node} node The node
35321         * @param {Boolean} deep
35322         * @param {Boolean} anim
35323         */
35324         "beforecollapse" : true,
35325         /**
35326         * @event expand
35327         * Fires when a node is expanded
35328         * @param {Node} node The node
35329         */
35330         "expand" : true,
35331         /**
35332         * @event disabledchange
35333         * Fires when the disabled status of a node changes
35334         * @param {Node} node The node
35335         * @param {Boolean} disabled
35336         */
35337         "disabledchange" : true,
35338         /**
35339         * @event collapse
35340         * Fires when a node is collapsed
35341         * @param {Node} node The node
35342         */
35343         "collapse" : true,
35344         /**
35345         * @event beforeclick
35346         * Fires before click processing on a node. Return false to cancel the default action.
35347         * @param {Node} node The node
35348         * @param {Roo.EventObject} e The event object
35349         */
35350         "beforeclick":true,
35351         /**
35352         * @event checkchange
35353         * Fires when a node with a checkbox's checked property changes
35354         * @param {Node} this This node
35355         * @param {Boolean} checked
35356         */
35357         "checkchange":true,
35358         /**
35359         * @event click
35360         * Fires when a node is clicked
35361         * @param {Node} node The node
35362         * @param {Roo.EventObject} e The event object
35363         */
35364         "click":true,
35365         /**
35366         * @event dblclick
35367         * Fires when a node is double clicked
35368         * @param {Node} node The node
35369         * @param {Roo.EventObject} e The event object
35370         */
35371         "dblclick":true,
35372         /**
35373         * @event contextmenu
35374         * Fires when a node is right clicked
35375         * @param {Node} node The node
35376         * @param {Roo.EventObject} e The event object
35377         */
35378         "contextmenu":true,
35379         /**
35380         * @event beforechildrenrendered
35381         * Fires right before the child nodes for a node are rendered
35382         * @param {Node} node The node
35383         */
35384         "beforechildrenrendered":true,
35385         /**
35386         * @event startdrag
35387         * Fires when a node starts being dragged
35388         * @param {Roo.tree.TreePanel} this
35389         * @param {Roo.tree.TreeNode} node
35390         * @param {event} e The raw browser event
35391         */ 
35392        "startdrag" : true,
35393        /**
35394         * @event enddrag
35395         * Fires when a drag operation is complete
35396         * @param {Roo.tree.TreePanel} this
35397         * @param {Roo.tree.TreeNode} node
35398         * @param {event} e The raw browser event
35399         */
35400        "enddrag" : true,
35401        /**
35402         * @event dragdrop
35403         * Fires when a dragged node is dropped on a valid DD target
35404         * @param {Roo.tree.TreePanel} this
35405         * @param {Roo.tree.TreeNode} node
35406         * @param {DD} dd The dd it was dropped on
35407         * @param {event} e The raw browser event
35408         */
35409        "dragdrop" : true,
35410        /**
35411         * @event beforenodedrop
35412         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35413         * passed to handlers has the following properties:<br />
35414         * <ul style="padding:5px;padding-left:16px;">
35415         * <li>tree - The TreePanel</li>
35416         * <li>target - The node being targeted for the drop</li>
35417         * <li>data - The drag data from the drag source</li>
35418         * <li>point - The point of the drop - append, above or below</li>
35419         * <li>source - The drag source</li>
35420         * <li>rawEvent - Raw mouse event</li>
35421         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35422         * to be inserted by setting them on this object.</li>
35423         * <li>cancel - Set this to true to cancel the drop.</li>
35424         * </ul>
35425         * @param {Object} dropEvent
35426         */
35427        "beforenodedrop" : true,
35428        /**
35429         * @event nodedrop
35430         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35431         * passed to handlers has the following properties:<br />
35432         * <ul style="padding:5px;padding-left:16px;">
35433         * <li>tree - The TreePanel</li>
35434         * <li>target - The node being targeted for the drop</li>
35435         * <li>data - The drag data from the drag source</li>
35436         * <li>point - The point of the drop - append, above or below</li>
35437         * <li>source - The drag source</li>
35438         * <li>rawEvent - Raw mouse event</li>
35439         * <li>dropNode - Dropped node(s).</li>
35440         * </ul>
35441         * @param {Object} dropEvent
35442         */
35443        "nodedrop" : true,
35444         /**
35445         * @event nodedragover
35446         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35447         * passed to handlers has the following properties:<br />
35448         * <ul style="padding:5px;padding-left:16px;">
35449         * <li>tree - The TreePanel</li>
35450         * <li>target - The node being targeted for the drop</li>
35451         * <li>data - The drag data from the drag source</li>
35452         * <li>point - The point of the drop - append, above or below</li>
35453         * <li>source - The drag source</li>
35454         * <li>rawEvent - Raw mouse event</li>
35455         * <li>dropNode - Drop node(s) provided by the source.</li>
35456         * <li>cancel - Set this to true to signal drop not allowed.</li>
35457         * </ul>
35458         * @param {Object} dragOverEvent
35459         */
35460        "nodedragover" : true,
35461        /**
35462         * @event appendnode
35463         * Fires when append node to the tree
35464         * @param {Roo.tree.TreePanel} this
35465         * @param {Roo.tree.TreeNode} node
35466         * @param {Number} index The index of the newly appended node
35467         */
35468        "appendnode" : true
35469         
35470     });
35471     if(this.singleExpand){
35472        this.on("beforeexpand", this.restrictExpand, this);
35473     }
35474     if (this.editor) {
35475         this.editor.tree = this;
35476         this.editor = Roo.factory(this.editor, Roo.tree);
35477     }
35478     
35479     if (this.selModel) {
35480         this.selModel = Roo.factory(this.selModel, Roo.tree);
35481     }
35482    
35483 };
35484 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35485     rootVisible : true,
35486     animate: Roo.enableFx,
35487     lines : true,
35488     enableDD : false,
35489     hlDrop : Roo.enableFx,
35490   
35491     renderer: false,
35492     
35493     rendererTip: false,
35494     // private
35495     restrictExpand : function(node){
35496         var p = node.parentNode;
35497         if(p){
35498             if(p.expandedChild && p.expandedChild.parentNode == p){
35499                 p.expandedChild.collapse();
35500             }
35501             p.expandedChild = node;
35502         }
35503     },
35504
35505     // private override
35506     setRootNode : function(node){
35507         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35508         if(!this.rootVisible){
35509             node.ui = new Roo.tree.RootTreeNodeUI(node);
35510         }
35511         return node;
35512     },
35513
35514     /**
35515      * Returns the container element for this TreePanel
35516      */
35517     getEl : function(){
35518         return this.el;
35519     },
35520
35521     /**
35522      * Returns the default TreeLoader for this TreePanel
35523      */
35524     getLoader : function(){
35525         return this.loader;
35526     },
35527
35528     /**
35529      * Expand all nodes
35530      */
35531     expandAll : function(){
35532         this.root.expand(true);
35533     },
35534
35535     /**
35536      * Collapse all nodes
35537      */
35538     collapseAll : function(){
35539         this.root.collapse(true);
35540     },
35541
35542     /**
35543      * Returns the selection model used by this TreePanel
35544      */
35545     getSelectionModel : function(){
35546         if(!this.selModel){
35547             this.selModel = new Roo.tree.DefaultSelectionModel();
35548         }
35549         return this.selModel;
35550     },
35551
35552     /**
35553      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35554      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35555      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35556      * @return {Array}
35557      */
35558     getChecked : function(a, startNode){
35559         startNode = startNode || this.root;
35560         var r = [];
35561         var f = function(){
35562             if(this.attributes.checked){
35563                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35564             }
35565         }
35566         startNode.cascade(f);
35567         return r;
35568     },
35569
35570     /**
35571      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35572      * @param {String} path
35573      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35574      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35575      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35576      */
35577     expandPath : function(path, attr, callback){
35578         attr = attr || "id";
35579         var keys = path.split(this.pathSeparator);
35580         var curNode = this.root;
35581         if(curNode.attributes[attr] != keys[1]){ // invalid root
35582             if(callback){
35583                 callback(false, null);
35584             }
35585             return;
35586         }
35587         var index = 1;
35588         var f = function(){
35589             if(++index == keys.length){
35590                 if(callback){
35591                     callback(true, curNode);
35592                 }
35593                 return;
35594             }
35595             var c = curNode.findChild(attr, keys[index]);
35596             if(!c){
35597                 if(callback){
35598                     callback(false, curNode);
35599                 }
35600                 return;
35601             }
35602             curNode = c;
35603             c.expand(false, false, f);
35604         };
35605         curNode.expand(false, false, f);
35606     },
35607
35608     /**
35609      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35610      * @param {String} path
35611      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35612      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35613      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35614      */
35615     selectPath : function(path, attr, callback){
35616         attr = attr || "id";
35617         var keys = path.split(this.pathSeparator);
35618         var v = keys.pop();
35619         if(keys.length > 0){
35620             var f = function(success, node){
35621                 if(success && node){
35622                     var n = node.findChild(attr, v);
35623                     if(n){
35624                         n.select();
35625                         if(callback){
35626                             callback(true, n);
35627                         }
35628                     }else if(callback){
35629                         callback(false, n);
35630                     }
35631                 }else{
35632                     if(callback){
35633                         callback(false, n);
35634                     }
35635                 }
35636             };
35637             this.expandPath(keys.join(this.pathSeparator), attr, f);
35638         }else{
35639             this.root.select();
35640             if(callback){
35641                 callback(true, this.root);
35642             }
35643         }
35644     },
35645
35646     getTreeEl : function(){
35647         return this.el;
35648     },
35649
35650     /**
35651      * Trigger rendering of this TreePanel
35652      */
35653     render : function(){
35654         if (this.innerCt) {
35655             return this; // stop it rendering more than once!!
35656         }
35657         
35658         this.innerCt = this.el.createChild({tag:"ul",
35659                cls:"x-tree-root-ct " +
35660                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35661
35662         if(this.containerScroll){
35663             Roo.dd.ScrollManager.register(this.el);
35664         }
35665         if((this.enableDD || this.enableDrop) && !this.dropZone){
35666            /**
35667             * The dropZone used by this tree if drop is enabled
35668             * @type Roo.tree.TreeDropZone
35669             */
35670              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35671                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35672            });
35673         }
35674         if((this.enableDD || this.enableDrag) && !this.dragZone){
35675            /**
35676             * The dragZone used by this tree if drag is enabled
35677             * @type Roo.tree.TreeDragZone
35678             */
35679             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35680                ddGroup: this.ddGroup || "TreeDD",
35681                scroll: this.ddScroll
35682            });
35683         }
35684         this.getSelectionModel().init(this);
35685         if (!this.root) {
35686             Roo.log("ROOT not set in tree");
35687             return this;
35688         }
35689         this.root.render();
35690         if(!this.rootVisible){
35691             this.root.renderChildren();
35692         }
35693         return this;
35694     }
35695 });/*
35696  * Based on:
35697  * Ext JS Library 1.1.1
35698  * Copyright(c) 2006-2007, Ext JS, LLC.
35699  *
35700  * Originally Released Under LGPL - original licence link has changed is not relivant.
35701  *
35702  * Fork - LGPL
35703  * <script type="text/javascript">
35704  */
35705  
35706
35707 /**
35708  * @class Roo.tree.DefaultSelectionModel
35709  * @extends Roo.util.Observable
35710  * The default single selection for a TreePanel.
35711  * @param {Object} cfg Configuration
35712  */
35713 Roo.tree.DefaultSelectionModel = function(cfg){
35714    this.selNode = null;
35715    
35716    
35717    
35718    this.addEvents({
35719        /**
35720         * @event selectionchange
35721         * Fires when the selected node changes
35722         * @param {DefaultSelectionModel} this
35723         * @param {TreeNode} node the new selection
35724         */
35725        "selectionchange" : true,
35726
35727        /**
35728         * @event beforeselect
35729         * Fires before the selected node changes, return false to cancel the change
35730         * @param {DefaultSelectionModel} this
35731         * @param {TreeNode} node the new selection
35732         * @param {TreeNode} node the old selection
35733         */
35734        "beforeselect" : true
35735    });
35736    
35737     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35738 };
35739
35740 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35741     init : function(tree){
35742         this.tree = tree;
35743         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35744         tree.on("click", this.onNodeClick, this);
35745     },
35746     
35747     onNodeClick : function(node, e){
35748         if (e.ctrlKey && this.selNode == node)  {
35749             this.unselect(node);
35750             return;
35751         }
35752         this.select(node);
35753     },
35754     
35755     /**
35756      * Select a node.
35757      * @param {TreeNode} node The node to select
35758      * @return {TreeNode} The selected node
35759      */
35760     select : function(node){
35761         var last = this.selNode;
35762         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35763             if(last){
35764                 last.ui.onSelectedChange(false);
35765             }
35766             this.selNode = node;
35767             node.ui.onSelectedChange(true);
35768             this.fireEvent("selectionchange", this, node, last);
35769         }
35770         return node;
35771     },
35772     
35773     /**
35774      * Deselect a node.
35775      * @param {TreeNode} node The node to unselect
35776      */
35777     unselect : function(node){
35778         if(this.selNode == node){
35779             this.clearSelections();
35780         }    
35781     },
35782     
35783     /**
35784      * Clear all selections
35785      */
35786     clearSelections : function(){
35787         var n = this.selNode;
35788         if(n){
35789             n.ui.onSelectedChange(false);
35790             this.selNode = null;
35791             this.fireEvent("selectionchange", this, null);
35792         }
35793         return n;
35794     },
35795     
35796     /**
35797      * Get the selected node
35798      * @return {TreeNode} The selected node
35799      */
35800     getSelectedNode : function(){
35801         return this.selNode;    
35802     },
35803     
35804     /**
35805      * Returns true if the node is selected
35806      * @param {TreeNode} node The node to check
35807      * @return {Boolean}
35808      */
35809     isSelected : function(node){
35810         return this.selNode == node;  
35811     },
35812
35813     /**
35814      * Selects the node above the selected node in the tree, intelligently walking the nodes
35815      * @return TreeNode The new selection
35816      */
35817     selectPrevious : function(){
35818         var s = this.selNode || this.lastSelNode;
35819         if(!s){
35820             return null;
35821         }
35822         var ps = s.previousSibling;
35823         if(ps){
35824             if(!ps.isExpanded() || ps.childNodes.length < 1){
35825                 return this.select(ps);
35826             } else{
35827                 var lc = ps.lastChild;
35828                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35829                     lc = lc.lastChild;
35830                 }
35831                 return this.select(lc);
35832             }
35833         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35834             return this.select(s.parentNode);
35835         }
35836         return null;
35837     },
35838
35839     /**
35840      * Selects the node above the selected node in the tree, intelligently walking the nodes
35841      * @return TreeNode The new selection
35842      */
35843     selectNext : function(){
35844         var s = this.selNode || this.lastSelNode;
35845         if(!s){
35846             return null;
35847         }
35848         if(s.firstChild && s.isExpanded()){
35849              return this.select(s.firstChild);
35850          }else if(s.nextSibling){
35851              return this.select(s.nextSibling);
35852          }else if(s.parentNode){
35853             var newS = null;
35854             s.parentNode.bubble(function(){
35855                 if(this.nextSibling){
35856                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35857                     return false;
35858                 }
35859             });
35860             return newS;
35861          }
35862         return null;
35863     },
35864
35865     onKeyDown : function(e){
35866         var s = this.selNode || this.lastSelNode;
35867         // undesirable, but required
35868         var sm = this;
35869         if(!s){
35870             return;
35871         }
35872         var k = e.getKey();
35873         switch(k){
35874              case e.DOWN:
35875                  e.stopEvent();
35876                  this.selectNext();
35877              break;
35878              case e.UP:
35879                  e.stopEvent();
35880                  this.selectPrevious();
35881              break;
35882              case e.RIGHT:
35883                  e.preventDefault();
35884                  if(s.hasChildNodes()){
35885                      if(!s.isExpanded()){
35886                          s.expand();
35887                      }else if(s.firstChild){
35888                          this.select(s.firstChild, e);
35889                      }
35890                  }
35891              break;
35892              case e.LEFT:
35893                  e.preventDefault();
35894                  if(s.hasChildNodes() && s.isExpanded()){
35895                      s.collapse();
35896                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35897                      this.select(s.parentNode, e);
35898                  }
35899              break;
35900         };
35901     }
35902 });
35903
35904 /**
35905  * @class Roo.tree.MultiSelectionModel
35906  * @extends Roo.util.Observable
35907  * Multi selection for a TreePanel.
35908  * @param {Object} cfg Configuration
35909  */
35910 Roo.tree.MultiSelectionModel = function(){
35911    this.selNodes = [];
35912    this.selMap = {};
35913    this.addEvents({
35914        /**
35915         * @event selectionchange
35916         * Fires when the selected nodes change
35917         * @param {MultiSelectionModel} this
35918         * @param {Array} nodes Array of the selected nodes
35919         */
35920        "selectionchange" : true
35921    });
35922    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35923    
35924 };
35925
35926 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35927     init : function(tree){
35928         this.tree = tree;
35929         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35930         tree.on("click", this.onNodeClick, this);
35931     },
35932     
35933     onNodeClick : function(node, e){
35934         this.select(node, e, e.ctrlKey);
35935     },
35936     
35937     /**
35938      * Select a node.
35939      * @param {TreeNode} node The node to select
35940      * @param {EventObject} e (optional) An event associated with the selection
35941      * @param {Boolean} keepExisting True to retain existing selections
35942      * @return {TreeNode} The selected node
35943      */
35944     select : function(node, e, keepExisting){
35945         if(keepExisting !== true){
35946             this.clearSelections(true);
35947         }
35948         if(this.isSelected(node)){
35949             this.lastSelNode = node;
35950             return node;
35951         }
35952         this.selNodes.push(node);
35953         this.selMap[node.id] = node;
35954         this.lastSelNode = node;
35955         node.ui.onSelectedChange(true);
35956         this.fireEvent("selectionchange", this, this.selNodes);
35957         return node;
35958     },
35959     
35960     /**
35961      * Deselect a node.
35962      * @param {TreeNode} node The node to unselect
35963      */
35964     unselect : function(node){
35965         if(this.selMap[node.id]){
35966             node.ui.onSelectedChange(false);
35967             var sn = this.selNodes;
35968             var index = -1;
35969             if(sn.indexOf){
35970                 index = sn.indexOf(node);
35971             }else{
35972                 for(var i = 0, len = sn.length; i < len; i++){
35973                     if(sn[i] == node){
35974                         index = i;
35975                         break;
35976                     }
35977                 }
35978             }
35979             if(index != -1){
35980                 this.selNodes.splice(index, 1);
35981             }
35982             delete this.selMap[node.id];
35983             this.fireEvent("selectionchange", this, this.selNodes);
35984         }
35985     },
35986     
35987     /**
35988      * Clear all selections
35989      */
35990     clearSelections : function(suppressEvent){
35991         var sn = this.selNodes;
35992         if(sn.length > 0){
35993             for(var i = 0, len = sn.length; i < len; i++){
35994                 sn[i].ui.onSelectedChange(false);
35995             }
35996             this.selNodes = [];
35997             this.selMap = {};
35998             if(suppressEvent !== true){
35999                 this.fireEvent("selectionchange", this, this.selNodes);
36000             }
36001         }
36002     },
36003     
36004     /**
36005      * Returns true if the node is selected
36006      * @param {TreeNode} node The node to check
36007      * @return {Boolean}
36008      */
36009     isSelected : function(node){
36010         return this.selMap[node.id] ? true : false;  
36011     },
36012     
36013     /**
36014      * Returns an array of the selected nodes
36015      * @return {Array}
36016      */
36017     getSelectedNodes : function(){
36018         return this.selNodes;    
36019     },
36020
36021     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36022
36023     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36024
36025     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36026 });/*
36027  * Based on:
36028  * Ext JS Library 1.1.1
36029  * Copyright(c) 2006-2007, Ext JS, LLC.
36030  *
36031  * Originally Released Under LGPL - original licence link has changed is not relivant.
36032  *
36033  * Fork - LGPL
36034  * <script type="text/javascript">
36035  */
36036  
36037 /**
36038  * @class Roo.tree.TreeNode
36039  * @extends Roo.data.Node
36040  * @cfg {String} text The text for this node
36041  * @cfg {Boolean} expanded true to start the node expanded
36042  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36043  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36044  * @cfg {Boolean} disabled true to start the node disabled
36045  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36046  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36047  * @cfg {String} cls A css class to be added to the node
36048  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36049  * @cfg {String} href URL of the link used for the node (defaults to #)
36050  * @cfg {String} hrefTarget target frame for the link
36051  * @cfg {String} qtip An Ext QuickTip for the node
36052  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36053  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36054  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36055  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36056  * (defaults to undefined with no checkbox rendered)
36057  * @constructor
36058  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36059  */
36060 Roo.tree.TreeNode = function(attributes){
36061     attributes = attributes || {};
36062     if(typeof attributes == "string"){
36063         attributes = {text: attributes};
36064     }
36065     this.childrenRendered = false;
36066     this.rendered = false;
36067     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36068     this.expanded = attributes.expanded === true;
36069     this.isTarget = attributes.isTarget !== false;
36070     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36071     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36072
36073     /**
36074      * Read-only. The text for this node. To change it use setText().
36075      * @type String
36076      */
36077     this.text = attributes.text;
36078     /**
36079      * True if this node is disabled.
36080      * @type Boolean
36081      */
36082     this.disabled = attributes.disabled === true;
36083
36084     this.addEvents({
36085         /**
36086         * @event textchange
36087         * Fires when the text for this node is changed
36088         * @param {Node} this This node
36089         * @param {String} text The new text
36090         * @param {String} oldText The old text
36091         */
36092         "textchange" : true,
36093         /**
36094         * @event beforeexpand
36095         * Fires before this node is expanded, return false to cancel.
36096         * @param {Node} this This node
36097         * @param {Boolean} deep
36098         * @param {Boolean} anim
36099         */
36100         "beforeexpand" : true,
36101         /**
36102         * @event beforecollapse
36103         * Fires before this node is collapsed, return false to cancel.
36104         * @param {Node} this This node
36105         * @param {Boolean} deep
36106         * @param {Boolean} anim
36107         */
36108         "beforecollapse" : true,
36109         /**
36110         * @event expand
36111         * Fires when this node is expanded
36112         * @param {Node} this This node
36113         */
36114         "expand" : true,
36115         /**
36116         * @event disabledchange
36117         * Fires when the disabled status of this node changes
36118         * @param {Node} this This node
36119         * @param {Boolean} disabled
36120         */
36121         "disabledchange" : true,
36122         /**
36123         * @event collapse
36124         * Fires when this node is collapsed
36125         * @param {Node} this This node
36126         */
36127         "collapse" : true,
36128         /**
36129         * @event beforeclick
36130         * Fires before click processing. Return false to cancel the default action.
36131         * @param {Node} this This node
36132         * @param {Roo.EventObject} e The event object
36133         */
36134         "beforeclick":true,
36135         /**
36136         * @event checkchange
36137         * Fires when a node with a checkbox's checked property changes
36138         * @param {Node} this This node
36139         * @param {Boolean} checked
36140         */
36141         "checkchange":true,
36142         /**
36143         * @event click
36144         * Fires when this node is clicked
36145         * @param {Node} this This node
36146         * @param {Roo.EventObject} e The event object
36147         */
36148         "click":true,
36149         /**
36150         * @event dblclick
36151         * Fires when this node is double clicked
36152         * @param {Node} this This node
36153         * @param {Roo.EventObject} e The event object
36154         */
36155         "dblclick":true,
36156         /**
36157         * @event contextmenu
36158         * Fires when this node is right clicked
36159         * @param {Node} this This node
36160         * @param {Roo.EventObject} e The event object
36161         */
36162         "contextmenu":true,
36163         /**
36164         * @event beforechildrenrendered
36165         * Fires right before the child nodes for this node are rendered
36166         * @param {Node} this This node
36167         */
36168         "beforechildrenrendered":true
36169     });
36170
36171     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36172
36173     /**
36174      * Read-only. The UI for this node
36175      * @type TreeNodeUI
36176      */
36177     this.ui = new uiClass(this);
36178     
36179     // finally support items[]
36180     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36181         return;
36182     }
36183     
36184     
36185     Roo.each(this.attributes.items, function(c) {
36186         this.appendChild(Roo.factory(c,Roo.Tree));
36187     }, this);
36188     delete this.attributes.items;
36189     
36190     
36191     
36192 };
36193 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36194     preventHScroll: true,
36195     /**
36196      * Returns true if this node is expanded
36197      * @return {Boolean}
36198      */
36199     isExpanded : function(){
36200         return this.expanded;
36201     },
36202
36203     /**
36204      * Returns the UI object for this node
36205      * @return {TreeNodeUI}
36206      */
36207     getUI : function(){
36208         return this.ui;
36209     },
36210
36211     // private override
36212     setFirstChild : function(node){
36213         var of = this.firstChild;
36214         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36215         if(this.childrenRendered && of && node != of){
36216             of.renderIndent(true, true);
36217         }
36218         if(this.rendered){
36219             this.renderIndent(true, true);
36220         }
36221     },
36222
36223     // private override
36224     setLastChild : function(node){
36225         var ol = this.lastChild;
36226         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36227         if(this.childrenRendered && ol && node != ol){
36228             ol.renderIndent(true, true);
36229         }
36230         if(this.rendered){
36231             this.renderIndent(true, true);
36232         }
36233     },
36234
36235     // these methods are overridden to provide lazy rendering support
36236     // private override
36237     appendChild : function()
36238     {
36239         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36240         if(node && this.childrenRendered){
36241             node.render();
36242         }
36243         this.ui.updateExpandIcon();
36244         return node;
36245     },
36246
36247     // private override
36248     removeChild : function(node){
36249         this.ownerTree.getSelectionModel().unselect(node);
36250         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36251         // if it's been rendered remove dom node
36252         if(this.childrenRendered){
36253             node.ui.remove();
36254         }
36255         if(this.childNodes.length < 1){
36256             this.collapse(false, false);
36257         }else{
36258             this.ui.updateExpandIcon();
36259         }
36260         if(!this.firstChild) {
36261             this.childrenRendered = false;
36262         }
36263         return node;
36264     },
36265
36266     // private override
36267     insertBefore : function(node, refNode){
36268         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36269         if(newNode && refNode && this.childrenRendered){
36270             node.render();
36271         }
36272         this.ui.updateExpandIcon();
36273         return newNode;
36274     },
36275
36276     /**
36277      * Sets the text for this node
36278      * @param {String} text
36279      */
36280     setText : function(text){
36281         var oldText = this.text;
36282         this.text = text;
36283         this.attributes.text = text;
36284         if(this.rendered){ // event without subscribing
36285             this.ui.onTextChange(this, text, oldText);
36286         }
36287         this.fireEvent("textchange", this, text, oldText);
36288     },
36289
36290     /**
36291      * Triggers selection of this node
36292      */
36293     select : function(){
36294         this.getOwnerTree().getSelectionModel().select(this);
36295     },
36296
36297     /**
36298      * Triggers deselection of this node
36299      */
36300     unselect : function(){
36301         this.getOwnerTree().getSelectionModel().unselect(this);
36302     },
36303
36304     /**
36305      * Returns true if this node is selected
36306      * @return {Boolean}
36307      */
36308     isSelected : function(){
36309         return this.getOwnerTree().getSelectionModel().isSelected(this);
36310     },
36311
36312     /**
36313      * Expand this node.
36314      * @param {Boolean} deep (optional) True to expand all children as well
36315      * @param {Boolean} anim (optional) false to cancel the default animation
36316      * @param {Function} callback (optional) A callback to be called when
36317      * expanding this node completes (does not wait for deep expand to complete).
36318      * Called with 1 parameter, this node.
36319      */
36320     expand : function(deep, anim, callback){
36321         if(!this.expanded){
36322             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36323                 return;
36324             }
36325             if(!this.childrenRendered){
36326                 this.renderChildren();
36327             }
36328             this.expanded = true;
36329             
36330             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36331                 this.ui.animExpand(function(){
36332                     this.fireEvent("expand", this);
36333                     if(typeof callback == "function"){
36334                         callback(this);
36335                     }
36336                     if(deep === true){
36337                         this.expandChildNodes(true);
36338                     }
36339                 }.createDelegate(this));
36340                 return;
36341             }else{
36342                 this.ui.expand();
36343                 this.fireEvent("expand", this);
36344                 if(typeof callback == "function"){
36345                     callback(this);
36346                 }
36347             }
36348         }else{
36349            if(typeof callback == "function"){
36350                callback(this);
36351            }
36352         }
36353         if(deep === true){
36354             this.expandChildNodes(true);
36355         }
36356     },
36357
36358     isHiddenRoot : function(){
36359         return this.isRoot && !this.getOwnerTree().rootVisible;
36360     },
36361
36362     /**
36363      * Collapse this node.
36364      * @param {Boolean} deep (optional) True to collapse all children as well
36365      * @param {Boolean} anim (optional) false to cancel the default animation
36366      */
36367     collapse : function(deep, anim){
36368         if(this.expanded && !this.isHiddenRoot()){
36369             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36370                 return;
36371             }
36372             this.expanded = false;
36373             if((this.getOwnerTree().animate && anim !== false) || anim){
36374                 this.ui.animCollapse(function(){
36375                     this.fireEvent("collapse", this);
36376                     if(deep === true){
36377                         this.collapseChildNodes(true);
36378                     }
36379                 }.createDelegate(this));
36380                 return;
36381             }else{
36382                 this.ui.collapse();
36383                 this.fireEvent("collapse", this);
36384             }
36385         }
36386         if(deep === true){
36387             var cs = this.childNodes;
36388             for(var i = 0, len = cs.length; i < len; i++) {
36389                 cs[i].collapse(true, false);
36390             }
36391         }
36392     },
36393
36394     // private
36395     delayedExpand : function(delay){
36396         if(!this.expandProcId){
36397             this.expandProcId = this.expand.defer(delay, this);
36398         }
36399     },
36400
36401     // private
36402     cancelExpand : function(){
36403         if(this.expandProcId){
36404             clearTimeout(this.expandProcId);
36405         }
36406         this.expandProcId = false;
36407     },
36408
36409     /**
36410      * Toggles expanded/collapsed state of the node
36411      */
36412     toggle : function(){
36413         if(this.expanded){
36414             this.collapse();
36415         }else{
36416             this.expand();
36417         }
36418     },
36419
36420     /**
36421      * Ensures all parent nodes are expanded
36422      */
36423     ensureVisible : function(callback){
36424         var tree = this.getOwnerTree();
36425         tree.expandPath(this.parentNode.getPath(), false, function(){
36426             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36427             Roo.callback(callback);
36428         }.createDelegate(this));
36429     },
36430
36431     /**
36432      * Expand all child nodes
36433      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36434      */
36435     expandChildNodes : function(deep){
36436         var cs = this.childNodes;
36437         for(var i = 0, len = cs.length; i < len; i++) {
36438                 cs[i].expand(deep);
36439         }
36440     },
36441
36442     /**
36443      * Collapse all child nodes
36444      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36445      */
36446     collapseChildNodes : function(deep){
36447         var cs = this.childNodes;
36448         for(var i = 0, len = cs.length; i < len; i++) {
36449                 cs[i].collapse(deep);
36450         }
36451     },
36452
36453     /**
36454      * Disables this node
36455      */
36456     disable : function(){
36457         this.disabled = true;
36458         this.unselect();
36459         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36460             this.ui.onDisableChange(this, true);
36461         }
36462         this.fireEvent("disabledchange", this, true);
36463     },
36464
36465     /**
36466      * Enables this node
36467      */
36468     enable : function(){
36469         this.disabled = false;
36470         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36471             this.ui.onDisableChange(this, false);
36472         }
36473         this.fireEvent("disabledchange", this, false);
36474     },
36475
36476     // private
36477     renderChildren : function(suppressEvent){
36478         if(suppressEvent !== false){
36479             this.fireEvent("beforechildrenrendered", this);
36480         }
36481         var cs = this.childNodes;
36482         for(var i = 0, len = cs.length; i < len; i++){
36483             cs[i].render(true);
36484         }
36485         this.childrenRendered = true;
36486     },
36487
36488     // private
36489     sort : function(fn, scope){
36490         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36491         if(this.childrenRendered){
36492             var cs = this.childNodes;
36493             for(var i = 0, len = cs.length; i < len; i++){
36494                 cs[i].render(true);
36495             }
36496         }
36497     },
36498
36499     // private
36500     render : function(bulkRender){
36501         this.ui.render(bulkRender);
36502         if(!this.rendered){
36503             this.rendered = true;
36504             if(this.expanded){
36505                 this.expanded = false;
36506                 this.expand(false, false);
36507             }
36508         }
36509     },
36510
36511     // private
36512     renderIndent : function(deep, refresh){
36513         if(refresh){
36514             this.ui.childIndent = null;
36515         }
36516         this.ui.renderIndent();
36517         if(deep === true && this.childrenRendered){
36518             var cs = this.childNodes;
36519             for(var i = 0, len = cs.length; i < len; i++){
36520                 cs[i].renderIndent(true, refresh);
36521             }
36522         }
36523     }
36524 });/*
36525  * Based on:
36526  * Ext JS Library 1.1.1
36527  * Copyright(c) 2006-2007, Ext JS, LLC.
36528  *
36529  * Originally Released Under LGPL - original licence link has changed is not relivant.
36530  *
36531  * Fork - LGPL
36532  * <script type="text/javascript">
36533  */
36534  
36535 /**
36536  * @class Roo.tree.AsyncTreeNode
36537  * @extends Roo.tree.TreeNode
36538  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36539  * @constructor
36540  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36541  */
36542  Roo.tree.AsyncTreeNode = function(config){
36543     this.loaded = false;
36544     this.loading = false;
36545     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36546     /**
36547     * @event beforeload
36548     * Fires before this node is loaded, return false to cancel
36549     * @param {Node} this This node
36550     */
36551     this.addEvents({'beforeload':true, 'load': true});
36552     /**
36553     * @event load
36554     * Fires when this node is loaded
36555     * @param {Node} this This node
36556     */
36557     /**
36558      * The loader used by this node (defaults to using the tree's defined loader)
36559      * @type TreeLoader
36560      * @property loader
36561      */
36562 };
36563 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36564     expand : function(deep, anim, callback){
36565         if(this.loading){ // if an async load is already running, waiting til it's done
36566             var timer;
36567             var f = function(){
36568                 if(!this.loading){ // done loading
36569                     clearInterval(timer);
36570                     this.expand(deep, anim, callback);
36571                 }
36572             }.createDelegate(this);
36573             timer = setInterval(f, 200);
36574             return;
36575         }
36576         if(!this.loaded){
36577             if(this.fireEvent("beforeload", this) === false){
36578                 return;
36579             }
36580             this.loading = true;
36581             this.ui.beforeLoad(this);
36582             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36583             if(loader){
36584                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36585                 return;
36586             }
36587         }
36588         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36589     },
36590     
36591     /**
36592      * Returns true if this node is currently loading
36593      * @return {Boolean}
36594      */
36595     isLoading : function(){
36596         return this.loading;  
36597     },
36598     
36599     loadComplete : function(deep, anim, callback){
36600         this.loading = false;
36601         this.loaded = true;
36602         this.ui.afterLoad(this);
36603         this.fireEvent("load", this);
36604         this.expand(deep, anim, callback);
36605     },
36606     
36607     /**
36608      * Returns true if this node has been loaded
36609      * @return {Boolean}
36610      */
36611     isLoaded : function(){
36612         return this.loaded;
36613     },
36614     
36615     hasChildNodes : function(){
36616         if(!this.isLeaf() && !this.loaded){
36617             return true;
36618         }else{
36619             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36620         }
36621     },
36622
36623     /**
36624      * Trigger a reload for this node
36625      * @param {Function} callback
36626      */
36627     reload : function(callback){
36628         this.collapse(false, false);
36629         while(this.firstChild){
36630             this.removeChild(this.firstChild);
36631         }
36632         this.childrenRendered = false;
36633         this.loaded = false;
36634         if(this.isHiddenRoot()){
36635             this.expanded = false;
36636         }
36637         this.expand(false, false, callback);
36638     }
36639 });/*
36640  * Based on:
36641  * Ext JS Library 1.1.1
36642  * Copyright(c) 2006-2007, Ext JS, LLC.
36643  *
36644  * Originally Released Under LGPL - original licence link has changed is not relivant.
36645  *
36646  * Fork - LGPL
36647  * <script type="text/javascript">
36648  */
36649  
36650 /**
36651  * @class Roo.tree.TreeNodeUI
36652  * @constructor
36653  * @param {Object} node The node to render
36654  * The TreeNode UI implementation is separate from the
36655  * tree implementation. Unless you are customizing the tree UI,
36656  * you should never have to use this directly.
36657  */
36658 Roo.tree.TreeNodeUI = function(node){
36659     this.node = node;
36660     this.rendered = false;
36661     this.animating = false;
36662     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36663 };
36664
36665 Roo.tree.TreeNodeUI.prototype = {
36666     removeChild : function(node){
36667         if(this.rendered){
36668             this.ctNode.removeChild(node.ui.getEl());
36669         }
36670     },
36671
36672     beforeLoad : function(){
36673          this.addClass("x-tree-node-loading");
36674     },
36675
36676     afterLoad : function(){
36677          this.removeClass("x-tree-node-loading");
36678     },
36679
36680     onTextChange : function(node, text, oldText){
36681         if(this.rendered){
36682             this.textNode.innerHTML = text;
36683         }
36684     },
36685
36686     onDisableChange : function(node, state){
36687         this.disabled = state;
36688         if(state){
36689             this.addClass("x-tree-node-disabled");
36690         }else{
36691             this.removeClass("x-tree-node-disabled");
36692         }
36693     },
36694
36695     onSelectedChange : function(state){
36696         if(state){
36697             this.focus();
36698             this.addClass("x-tree-selected");
36699         }else{
36700             //this.blur();
36701             this.removeClass("x-tree-selected");
36702         }
36703     },
36704
36705     onMove : function(tree, node, oldParent, newParent, index, refNode){
36706         this.childIndent = null;
36707         if(this.rendered){
36708             var targetNode = newParent.ui.getContainer();
36709             if(!targetNode){//target not rendered
36710                 this.holder = document.createElement("div");
36711                 this.holder.appendChild(this.wrap);
36712                 return;
36713             }
36714             var insertBefore = refNode ? refNode.ui.getEl() : null;
36715             if(insertBefore){
36716                 targetNode.insertBefore(this.wrap, insertBefore);
36717             }else{
36718                 targetNode.appendChild(this.wrap);
36719             }
36720             this.node.renderIndent(true);
36721         }
36722     },
36723
36724     addClass : function(cls){
36725         if(this.elNode){
36726             Roo.fly(this.elNode).addClass(cls);
36727         }
36728     },
36729
36730     removeClass : function(cls){
36731         if(this.elNode){
36732             Roo.fly(this.elNode).removeClass(cls);
36733         }
36734     },
36735
36736     remove : function(){
36737         if(this.rendered){
36738             this.holder = document.createElement("div");
36739             this.holder.appendChild(this.wrap);
36740         }
36741     },
36742
36743     fireEvent : function(){
36744         return this.node.fireEvent.apply(this.node, arguments);
36745     },
36746
36747     initEvents : function(){
36748         this.node.on("move", this.onMove, this);
36749         var E = Roo.EventManager;
36750         var a = this.anchor;
36751
36752         var el = Roo.fly(a, '_treeui');
36753
36754         if(Roo.isOpera){ // opera render bug ignores the CSS
36755             el.setStyle("text-decoration", "none");
36756         }
36757
36758         el.on("click", this.onClick, this);
36759         el.on("dblclick", this.onDblClick, this);
36760
36761         if(this.checkbox){
36762             Roo.EventManager.on(this.checkbox,
36763                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36764         }
36765
36766         el.on("contextmenu", this.onContextMenu, this);
36767
36768         var icon = Roo.fly(this.iconNode);
36769         icon.on("click", this.onClick, this);
36770         icon.on("dblclick", this.onDblClick, this);
36771         icon.on("contextmenu", this.onContextMenu, this);
36772         E.on(this.ecNode, "click", this.ecClick, this, true);
36773
36774         if(this.node.disabled){
36775             this.addClass("x-tree-node-disabled");
36776         }
36777         if(this.node.hidden){
36778             this.addClass("x-tree-node-disabled");
36779         }
36780         var ot = this.node.getOwnerTree();
36781         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36782         if(dd && (!this.node.isRoot || ot.rootVisible)){
36783             Roo.dd.Registry.register(this.elNode, {
36784                 node: this.node,
36785                 handles: this.getDDHandles(),
36786                 isHandle: false
36787             });
36788         }
36789     },
36790
36791     getDDHandles : function(){
36792         return [this.iconNode, this.textNode];
36793     },
36794
36795     hide : function(){
36796         if(this.rendered){
36797             this.wrap.style.display = "none";
36798         }
36799     },
36800
36801     show : function(){
36802         if(this.rendered){
36803             this.wrap.style.display = "";
36804         }
36805     },
36806
36807     onContextMenu : function(e){
36808         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36809             e.preventDefault();
36810             this.focus();
36811             this.fireEvent("contextmenu", this.node, e);
36812         }
36813     },
36814
36815     onClick : function(e){
36816         if(this.dropping){
36817             e.stopEvent();
36818             return;
36819         }
36820         if(this.fireEvent("beforeclick", this.node, e) !== false){
36821             if(!this.disabled && this.node.attributes.href){
36822                 this.fireEvent("click", this.node, e);
36823                 return;
36824             }
36825             e.preventDefault();
36826             if(this.disabled){
36827                 return;
36828             }
36829
36830             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36831                 this.node.toggle();
36832             }
36833
36834             this.fireEvent("click", this.node, e);
36835         }else{
36836             e.stopEvent();
36837         }
36838     },
36839
36840     onDblClick : function(e){
36841         e.preventDefault();
36842         if(this.disabled){
36843             return;
36844         }
36845         if(this.checkbox){
36846             this.toggleCheck();
36847         }
36848         if(!this.animating && this.node.hasChildNodes()){
36849             this.node.toggle();
36850         }
36851         this.fireEvent("dblclick", this.node, e);
36852     },
36853
36854     onCheckChange : function(){
36855         var checked = this.checkbox.checked;
36856         this.node.attributes.checked = checked;
36857         this.fireEvent('checkchange', this.node, checked);
36858     },
36859
36860     ecClick : function(e){
36861         if(!this.animating && this.node.hasChildNodes()){
36862             this.node.toggle();
36863         }
36864     },
36865
36866     startDrop : function(){
36867         this.dropping = true;
36868     },
36869
36870     // delayed drop so the click event doesn't get fired on a drop
36871     endDrop : function(){
36872        setTimeout(function(){
36873            this.dropping = false;
36874        }.createDelegate(this), 50);
36875     },
36876
36877     expand : function(){
36878         this.updateExpandIcon();
36879         this.ctNode.style.display = "";
36880     },
36881
36882     focus : function(){
36883         if(!this.node.preventHScroll){
36884             try{this.anchor.focus();
36885             }catch(e){}
36886         }else if(!Roo.isIE){
36887             try{
36888                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36889                 var l = noscroll.scrollLeft;
36890                 this.anchor.focus();
36891                 noscroll.scrollLeft = l;
36892             }catch(e){}
36893         }
36894     },
36895
36896     toggleCheck : function(value){
36897         var cb = this.checkbox;
36898         if(cb){
36899             cb.checked = (value === undefined ? !cb.checked : value);
36900         }
36901     },
36902
36903     blur : function(){
36904         try{
36905             this.anchor.blur();
36906         }catch(e){}
36907     },
36908
36909     animExpand : function(callback){
36910         var ct = Roo.get(this.ctNode);
36911         ct.stopFx();
36912         if(!this.node.hasChildNodes()){
36913             this.updateExpandIcon();
36914             this.ctNode.style.display = "";
36915             Roo.callback(callback);
36916             return;
36917         }
36918         this.animating = true;
36919         this.updateExpandIcon();
36920
36921         ct.slideIn('t', {
36922            callback : function(){
36923                this.animating = false;
36924                Roo.callback(callback);
36925             },
36926             scope: this,
36927             duration: this.node.ownerTree.duration || .25
36928         });
36929     },
36930
36931     highlight : function(){
36932         var tree = this.node.getOwnerTree();
36933         Roo.fly(this.wrap).highlight(
36934             tree.hlColor || "C3DAF9",
36935             {endColor: tree.hlBaseColor}
36936         );
36937     },
36938
36939     collapse : function(){
36940         this.updateExpandIcon();
36941         this.ctNode.style.display = "none";
36942     },
36943
36944     animCollapse : function(callback){
36945         var ct = Roo.get(this.ctNode);
36946         ct.enableDisplayMode('block');
36947         ct.stopFx();
36948
36949         this.animating = true;
36950         this.updateExpandIcon();
36951
36952         ct.slideOut('t', {
36953             callback : function(){
36954                this.animating = false;
36955                Roo.callback(callback);
36956             },
36957             scope: this,
36958             duration: this.node.ownerTree.duration || .25
36959         });
36960     },
36961
36962     getContainer : function(){
36963         return this.ctNode;
36964     },
36965
36966     getEl : function(){
36967         return this.wrap;
36968     },
36969
36970     appendDDGhost : function(ghostNode){
36971         ghostNode.appendChild(this.elNode.cloneNode(true));
36972     },
36973
36974     getDDRepairXY : function(){
36975         return Roo.lib.Dom.getXY(this.iconNode);
36976     },
36977
36978     onRender : function(){
36979         this.render();
36980     },
36981
36982     render : function(bulkRender){
36983         var n = this.node, a = n.attributes;
36984         var targetNode = n.parentNode ?
36985               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36986
36987         if(!this.rendered){
36988             this.rendered = true;
36989
36990             this.renderElements(n, a, targetNode, bulkRender);
36991
36992             if(a.qtip){
36993                if(this.textNode.setAttributeNS){
36994                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36995                    if(a.qtipTitle){
36996                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36997                    }
36998                }else{
36999                    this.textNode.setAttribute("ext:qtip", a.qtip);
37000                    if(a.qtipTitle){
37001                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37002                    }
37003                }
37004             }else if(a.qtipCfg){
37005                 a.qtipCfg.target = Roo.id(this.textNode);
37006                 Roo.QuickTips.register(a.qtipCfg);
37007             }
37008             this.initEvents();
37009             if(!this.node.expanded){
37010                 this.updateExpandIcon();
37011             }
37012         }else{
37013             if(bulkRender === true) {
37014                 targetNode.appendChild(this.wrap);
37015             }
37016         }
37017     },
37018
37019     renderElements : function(n, a, targetNode, bulkRender)
37020     {
37021         // add some indent caching, this helps performance when rendering a large tree
37022         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37023         var t = n.getOwnerTree();
37024         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37025         if (typeof(n.attributes.html) != 'undefined') {
37026             txt = n.attributes.html;
37027         }
37028         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37029         var cb = typeof a.checked == 'boolean';
37030         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37031         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37032             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37033             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37034             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37035             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37036             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37037              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37038                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37039             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37040             "</li>"];
37041
37042         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37043             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37044                                 n.nextSibling.ui.getEl(), buf.join(""));
37045         }else{
37046             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37047         }
37048
37049         this.elNode = this.wrap.childNodes[0];
37050         this.ctNode = this.wrap.childNodes[1];
37051         var cs = this.elNode.childNodes;
37052         this.indentNode = cs[0];
37053         this.ecNode = cs[1];
37054         this.iconNode = cs[2];
37055         var index = 3;
37056         if(cb){
37057             this.checkbox = cs[3];
37058             index++;
37059         }
37060         this.anchor = cs[index];
37061         this.textNode = cs[index].firstChild;
37062     },
37063
37064     getAnchor : function(){
37065         return this.anchor;
37066     },
37067
37068     getTextEl : function(){
37069         return this.textNode;
37070     },
37071
37072     getIconEl : function(){
37073         return this.iconNode;
37074     },
37075
37076     isChecked : function(){
37077         return this.checkbox ? this.checkbox.checked : false;
37078     },
37079
37080     updateExpandIcon : function(){
37081         if(this.rendered){
37082             var n = this.node, c1, c2;
37083             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37084             var hasChild = n.hasChildNodes();
37085             if(hasChild){
37086                 if(n.expanded){
37087                     cls += "-minus";
37088                     c1 = "x-tree-node-collapsed";
37089                     c2 = "x-tree-node-expanded";
37090                 }else{
37091                     cls += "-plus";
37092                     c1 = "x-tree-node-expanded";
37093                     c2 = "x-tree-node-collapsed";
37094                 }
37095                 if(this.wasLeaf){
37096                     this.removeClass("x-tree-node-leaf");
37097                     this.wasLeaf = false;
37098                 }
37099                 if(this.c1 != c1 || this.c2 != c2){
37100                     Roo.fly(this.elNode).replaceClass(c1, c2);
37101                     this.c1 = c1; this.c2 = c2;
37102                 }
37103             }else{
37104                 // this changes non-leafs into leafs if they have no children.
37105                 // it's not very rational behaviour..
37106                 
37107                 if(!this.wasLeaf && this.node.leaf){
37108                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37109                     delete this.c1;
37110                     delete this.c2;
37111                     this.wasLeaf = true;
37112                 }
37113             }
37114             var ecc = "x-tree-ec-icon "+cls;
37115             if(this.ecc != ecc){
37116                 this.ecNode.className = ecc;
37117                 this.ecc = ecc;
37118             }
37119         }
37120     },
37121
37122     getChildIndent : function(){
37123         if(!this.childIndent){
37124             var buf = [];
37125             var p = this.node;
37126             while(p){
37127                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37128                     if(!p.isLast()) {
37129                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37130                     } else {
37131                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37132                     }
37133                 }
37134                 p = p.parentNode;
37135             }
37136             this.childIndent = buf.join("");
37137         }
37138         return this.childIndent;
37139     },
37140
37141     renderIndent : function(){
37142         if(this.rendered){
37143             var indent = "";
37144             var p = this.node.parentNode;
37145             if(p){
37146                 indent = p.ui.getChildIndent();
37147             }
37148             if(this.indentMarkup != indent){ // don't rerender if not required
37149                 this.indentNode.innerHTML = indent;
37150                 this.indentMarkup = indent;
37151             }
37152             this.updateExpandIcon();
37153         }
37154     }
37155 };
37156
37157 Roo.tree.RootTreeNodeUI = function(){
37158     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37159 };
37160 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37161     render : function(){
37162         if(!this.rendered){
37163             var targetNode = this.node.ownerTree.innerCt.dom;
37164             this.node.expanded = true;
37165             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37166             this.wrap = this.ctNode = targetNode.firstChild;
37167         }
37168     },
37169     collapse : function(){
37170     },
37171     expand : function(){
37172     }
37173 });/*
37174  * Based on:
37175  * Ext JS Library 1.1.1
37176  * Copyright(c) 2006-2007, Ext JS, LLC.
37177  *
37178  * Originally Released Under LGPL - original licence link has changed is not relivant.
37179  *
37180  * Fork - LGPL
37181  * <script type="text/javascript">
37182  */
37183 /**
37184  * @class Roo.tree.TreeLoader
37185  * @extends Roo.util.Observable
37186  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37187  * nodes from a specified URL. The response must be a javascript Array definition
37188  * who's elements are node definition objects. eg:
37189  * <pre><code>
37190 {  success : true,
37191    data :      [
37192    
37193     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37194     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37195     ]
37196 }
37197
37198
37199 </code></pre>
37200  * <br><br>
37201  * The old style respose with just an array is still supported, but not recommended.
37202  * <br><br>
37203  *
37204  * A server request is sent, and child nodes are loaded only when a node is expanded.
37205  * The loading node's id is passed to the server under the parameter name "node" to
37206  * enable the server to produce the correct child nodes.
37207  * <br><br>
37208  * To pass extra parameters, an event handler may be attached to the "beforeload"
37209  * event, and the parameters specified in the TreeLoader's baseParams property:
37210  * <pre><code>
37211     myTreeLoader.on("beforeload", function(treeLoader, node) {
37212         this.baseParams.category = node.attributes.category;
37213     }, this);
37214     
37215 </code></pre>
37216  *
37217  * This would pass an HTTP parameter called "category" to the server containing
37218  * the value of the Node's "category" attribute.
37219  * @constructor
37220  * Creates a new Treeloader.
37221  * @param {Object} config A config object containing config properties.
37222  */
37223 Roo.tree.TreeLoader = function(config){
37224     this.baseParams = {};
37225     this.requestMethod = "POST";
37226     Roo.apply(this, config);
37227
37228     this.addEvents({
37229     
37230         /**
37231          * @event beforeload
37232          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37233          * @param {Object} This TreeLoader object.
37234          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37235          * @param {Object} callback The callback function specified in the {@link #load} call.
37236          */
37237         beforeload : true,
37238         /**
37239          * @event load
37240          * Fires when the node has been successfuly loaded.
37241          * @param {Object} This TreeLoader object.
37242          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37243          * @param {Object} response The response object containing the data from the server.
37244          */
37245         load : true,
37246         /**
37247          * @event loadexception
37248          * Fires if the network request failed.
37249          * @param {Object} This TreeLoader object.
37250          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37251          * @param {Object} response The response object containing the data from the server.
37252          */
37253         loadexception : true,
37254         /**
37255          * @event create
37256          * Fires before a node is created, enabling you to return custom Node types 
37257          * @param {Object} This TreeLoader object.
37258          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37259          */
37260         create : true
37261     });
37262
37263     Roo.tree.TreeLoader.superclass.constructor.call(this);
37264 };
37265
37266 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37267     /**
37268     * @cfg {String} dataUrl The URL from which to request a Json string which
37269     * specifies an array of node definition object representing the child nodes
37270     * to be loaded.
37271     */
37272     /**
37273     * @cfg {String} requestMethod either GET or POST
37274     * defaults to POST (due to BC)
37275     * to be loaded.
37276     */
37277     /**
37278     * @cfg {Object} baseParams (optional) An object containing properties which
37279     * specify HTTP parameters to be passed to each request for child nodes.
37280     */
37281     /**
37282     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37283     * created by this loader. If the attributes sent by the server have an attribute in this object,
37284     * they take priority.
37285     */
37286     /**
37287     * @cfg {Object} uiProviders (optional) An object containing properties which
37288     * 
37289     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37290     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37291     * <i>uiProvider</i> attribute of a returned child node is a string rather
37292     * than a reference to a TreeNodeUI implementation, this that string value
37293     * is used as a property name in the uiProviders object. You can define the provider named
37294     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37295     */
37296     uiProviders : {},
37297
37298     /**
37299     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37300     * child nodes before loading.
37301     */
37302     clearOnLoad : true,
37303
37304     /**
37305     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37306     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37307     * Grid query { data : [ .....] }
37308     */
37309     
37310     root : false,
37311      /**
37312     * @cfg {String} queryParam (optional) 
37313     * Name of the query as it will be passed on the querystring (defaults to 'node')
37314     * eg. the request will be ?node=[id]
37315     */
37316     
37317     
37318     queryParam: false,
37319     
37320     /**
37321      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37322      * This is called automatically when a node is expanded, but may be used to reload
37323      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37324      * @param {Roo.tree.TreeNode} node
37325      * @param {Function} callback
37326      */
37327     load : function(node, callback){
37328         if(this.clearOnLoad){
37329             while(node.firstChild){
37330                 node.removeChild(node.firstChild);
37331             }
37332         }
37333         if(node.attributes.children){ // preloaded json children
37334             var cs = node.attributes.children;
37335             for(var i = 0, len = cs.length; i < len; i++){
37336                 node.appendChild(this.createNode(cs[i]));
37337             }
37338             if(typeof callback == "function"){
37339                 callback();
37340             }
37341         }else if(this.dataUrl){
37342             this.requestData(node, callback);
37343         }
37344     },
37345
37346     getParams: function(node){
37347         var buf = [], bp = this.baseParams;
37348         for(var key in bp){
37349             if(typeof bp[key] != "function"){
37350                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37351             }
37352         }
37353         var n = this.queryParam === false ? 'node' : this.queryParam;
37354         buf.push(n + "=", encodeURIComponent(node.id));
37355         return buf.join("");
37356     },
37357
37358     requestData : function(node, callback){
37359         if(this.fireEvent("beforeload", this, node, callback) !== false){
37360             this.transId = Roo.Ajax.request({
37361                 method:this.requestMethod,
37362                 url: this.dataUrl||this.url,
37363                 success: this.handleResponse,
37364                 failure: this.handleFailure,
37365                 scope: this,
37366                 argument: {callback: callback, node: node},
37367                 params: this.getParams(node)
37368             });
37369         }else{
37370             // if the load is cancelled, make sure we notify
37371             // the node that we are done
37372             if(typeof callback == "function"){
37373                 callback();
37374             }
37375         }
37376     },
37377
37378     isLoading : function(){
37379         return this.transId ? true : false;
37380     },
37381
37382     abort : function(){
37383         if(this.isLoading()){
37384             Roo.Ajax.abort(this.transId);
37385         }
37386     },
37387
37388     // private
37389     createNode : function(attr)
37390     {
37391         // apply baseAttrs, nice idea Corey!
37392         if(this.baseAttrs){
37393             Roo.applyIf(attr, this.baseAttrs);
37394         }
37395         if(this.applyLoader !== false){
37396             attr.loader = this;
37397         }
37398         // uiProvider = depreciated..
37399         
37400         if(typeof(attr.uiProvider) == 'string'){
37401            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37402                 /**  eval:var:attr */ eval(attr.uiProvider);
37403         }
37404         if(typeof(this.uiProviders['default']) != 'undefined') {
37405             attr.uiProvider = this.uiProviders['default'];
37406         }
37407         
37408         this.fireEvent('create', this, attr);
37409         
37410         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37411         return(attr.leaf ?
37412                         new Roo.tree.TreeNode(attr) :
37413                         new Roo.tree.AsyncTreeNode(attr));
37414     },
37415
37416     processResponse : function(response, node, callback)
37417     {
37418         var json = response.responseText;
37419         try {
37420             
37421             var o = Roo.decode(json);
37422             
37423             if (this.root === false && typeof(o.success) != undefined) {
37424                 this.root = 'data'; // the default behaviour for list like data..
37425                 }
37426                 
37427             if (this.root !== false &&  !o.success) {
37428                 // it's a failure condition.
37429                 var a = response.argument;
37430                 this.fireEvent("loadexception", this, a.node, response);
37431                 Roo.log("Load failed - should have a handler really");
37432                 return;
37433             }
37434             
37435             
37436             
37437             if (this.root !== false) {
37438                  o = o[this.root];
37439             }
37440             
37441             for(var i = 0, len = o.length; i < len; i++){
37442                 var n = this.createNode(o[i]);
37443                 if(n){
37444                     node.appendChild(n);
37445                 }
37446             }
37447             if(typeof callback == "function"){
37448                 callback(this, node);
37449             }
37450         }catch(e){
37451             this.handleFailure(response);
37452         }
37453     },
37454
37455     handleResponse : function(response){
37456         this.transId = false;
37457         var a = response.argument;
37458         this.processResponse(response, a.node, a.callback);
37459         this.fireEvent("load", this, a.node, response);
37460     },
37461
37462     handleFailure : function(response)
37463     {
37464         // should handle failure better..
37465         this.transId = false;
37466         var a = response.argument;
37467         this.fireEvent("loadexception", this, a.node, response);
37468         if(typeof a.callback == "function"){
37469             a.callback(this, a.node);
37470         }
37471     }
37472 });/*
37473  * Based on:
37474  * Ext JS Library 1.1.1
37475  * Copyright(c) 2006-2007, Ext JS, LLC.
37476  *
37477  * Originally Released Under LGPL - original licence link has changed is not relivant.
37478  *
37479  * Fork - LGPL
37480  * <script type="text/javascript">
37481  */
37482
37483 /**
37484 * @class Roo.tree.TreeFilter
37485 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37486 * @param {TreePanel} tree
37487 * @param {Object} config (optional)
37488  */
37489 Roo.tree.TreeFilter = function(tree, config){
37490     this.tree = tree;
37491     this.filtered = {};
37492     Roo.apply(this, config);
37493 };
37494
37495 Roo.tree.TreeFilter.prototype = {
37496     clearBlank:false,
37497     reverse:false,
37498     autoClear:false,
37499     remove:false,
37500
37501      /**
37502      * Filter the data by a specific attribute.
37503      * @param {String/RegExp} value Either string that the attribute value
37504      * should start with or a RegExp to test against the attribute
37505      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37506      * @param {TreeNode} startNode (optional) The node to start the filter at.
37507      */
37508     filter : function(value, attr, startNode){
37509         attr = attr || "text";
37510         var f;
37511         if(typeof value == "string"){
37512             var vlen = value.length;
37513             // auto clear empty filter
37514             if(vlen == 0 && this.clearBlank){
37515                 this.clear();
37516                 return;
37517             }
37518             value = value.toLowerCase();
37519             f = function(n){
37520                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37521             };
37522         }else if(value.exec){ // regex?
37523             f = function(n){
37524                 return value.test(n.attributes[attr]);
37525             };
37526         }else{
37527             throw 'Illegal filter type, must be string or regex';
37528         }
37529         this.filterBy(f, null, startNode);
37530         },
37531
37532     /**
37533      * Filter by a function. The passed function will be called with each
37534      * node in the tree (or from the startNode). If the function returns true, the node is kept
37535      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37536      * @param {Function} fn The filter function
37537      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37538      */
37539     filterBy : function(fn, scope, startNode){
37540         startNode = startNode || this.tree.root;
37541         if(this.autoClear){
37542             this.clear();
37543         }
37544         var af = this.filtered, rv = this.reverse;
37545         var f = function(n){
37546             if(n == startNode){
37547                 return true;
37548             }
37549             if(af[n.id]){
37550                 return false;
37551             }
37552             var m = fn.call(scope || n, n);
37553             if(!m || rv){
37554                 af[n.id] = n;
37555                 n.ui.hide();
37556                 return false;
37557             }
37558             return true;
37559         };
37560         startNode.cascade(f);
37561         if(this.remove){
37562            for(var id in af){
37563                if(typeof id != "function"){
37564                    var n = af[id];
37565                    if(n && n.parentNode){
37566                        n.parentNode.removeChild(n);
37567                    }
37568                }
37569            }
37570         }
37571     },
37572
37573     /**
37574      * Clears the current filter. Note: with the "remove" option
37575      * set a filter cannot be cleared.
37576      */
37577     clear : function(){
37578         var t = this.tree;
37579         var af = this.filtered;
37580         for(var id in af){
37581             if(typeof id != "function"){
37582                 var n = af[id];
37583                 if(n){
37584                     n.ui.show();
37585                 }
37586             }
37587         }
37588         this.filtered = {};
37589     }
37590 };
37591 /*
37592  * Based on:
37593  * Ext JS Library 1.1.1
37594  * Copyright(c) 2006-2007, Ext JS, LLC.
37595  *
37596  * Originally Released Under LGPL - original licence link has changed is not relivant.
37597  *
37598  * Fork - LGPL
37599  * <script type="text/javascript">
37600  */
37601  
37602
37603 /**
37604  * @class Roo.tree.TreeSorter
37605  * Provides sorting of nodes in a TreePanel
37606  * 
37607  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37608  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37609  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37610  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37611  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37612  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37613  * @constructor
37614  * @param {TreePanel} tree
37615  * @param {Object} config
37616  */
37617 Roo.tree.TreeSorter = function(tree, config){
37618     Roo.apply(this, config);
37619     tree.on("beforechildrenrendered", this.doSort, this);
37620     tree.on("append", this.updateSort, this);
37621     tree.on("insert", this.updateSort, this);
37622     
37623     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37624     var p = this.property || "text";
37625     var sortType = this.sortType;
37626     var fs = this.folderSort;
37627     var cs = this.caseSensitive === true;
37628     var leafAttr = this.leafAttr || 'leaf';
37629
37630     this.sortFn = function(n1, n2){
37631         if(fs){
37632             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37633                 return 1;
37634             }
37635             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37636                 return -1;
37637             }
37638         }
37639         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37640         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37641         if(v1 < v2){
37642                         return dsc ? +1 : -1;
37643                 }else if(v1 > v2){
37644                         return dsc ? -1 : +1;
37645         }else{
37646                 return 0;
37647         }
37648     };
37649 };
37650
37651 Roo.tree.TreeSorter.prototype = {
37652     doSort : function(node){
37653         node.sort(this.sortFn);
37654     },
37655     
37656     compareNodes : function(n1, n2){
37657         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37658     },
37659     
37660     updateSort : function(tree, node){
37661         if(node.childrenRendered){
37662             this.doSort.defer(1, this, [node]);
37663         }
37664     }
37665 };/*
37666  * Based on:
37667  * Ext JS Library 1.1.1
37668  * Copyright(c) 2006-2007, Ext JS, LLC.
37669  *
37670  * Originally Released Under LGPL - original licence link has changed is not relivant.
37671  *
37672  * Fork - LGPL
37673  * <script type="text/javascript">
37674  */
37675
37676 if(Roo.dd.DropZone){
37677     
37678 Roo.tree.TreeDropZone = function(tree, config){
37679     this.allowParentInsert = false;
37680     this.allowContainerDrop = false;
37681     this.appendOnly = false;
37682     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37683     this.tree = tree;
37684     this.lastInsertClass = "x-tree-no-status";
37685     this.dragOverData = {};
37686 };
37687
37688 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37689     ddGroup : "TreeDD",
37690     scroll:  true,
37691     
37692     expandDelay : 1000,
37693     
37694     expandNode : function(node){
37695         if(node.hasChildNodes() && !node.isExpanded()){
37696             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37697         }
37698     },
37699     
37700     queueExpand : function(node){
37701         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37702     },
37703     
37704     cancelExpand : function(){
37705         if(this.expandProcId){
37706             clearTimeout(this.expandProcId);
37707             this.expandProcId = false;
37708         }
37709     },
37710     
37711     isValidDropPoint : function(n, pt, dd, e, data){
37712         if(!n || !data){ return false; }
37713         var targetNode = n.node;
37714         var dropNode = data.node;
37715         // default drop rules
37716         if(!(targetNode && targetNode.isTarget && pt)){
37717             return false;
37718         }
37719         if(pt == "append" && targetNode.allowChildren === false){
37720             return false;
37721         }
37722         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37723             return false;
37724         }
37725         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37726             return false;
37727         }
37728         // reuse the object
37729         var overEvent = this.dragOverData;
37730         overEvent.tree = this.tree;
37731         overEvent.target = targetNode;
37732         overEvent.data = data;
37733         overEvent.point = pt;
37734         overEvent.source = dd;
37735         overEvent.rawEvent = e;
37736         overEvent.dropNode = dropNode;
37737         overEvent.cancel = false;  
37738         var result = this.tree.fireEvent("nodedragover", overEvent);
37739         return overEvent.cancel === false && result !== false;
37740     },
37741     
37742     getDropPoint : function(e, n, dd)
37743     {
37744         var tn = n.node;
37745         if(tn.isRoot){
37746             return tn.allowChildren !== false ? "append" : false; // always append for root
37747         }
37748         var dragEl = n.ddel;
37749         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37750         var y = Roo.lib.Event.getPageY(e);
37751         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37752         
37753         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37754         var noAppend = tn.allowChildren === false;
37755         if(this.appendOnly || tn.parentNode.allowChildren === false){
37756             return noAppend ? false : "append";
37757         }
37758         var noBelow = false;
37759         if(!this.allowParentInsert){
37760             noBelow = tn.hasChildNodes() && tn.isExpanded();
37761         }
37762         var q = (b - t) / (noAppend ? 2 : 3);
37763         if(y >= t && y < (t + q)){
37764             return "above";
37765         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37766             return "below";
37767         }else{
37768             return "append";
37769         }
37770     },
37771     
37772     onNodeEnter : function(n, dd, e, data)
37773     {
37774         this.cancelExpand();
37775     },
37776     
37777     onNodeOver : function(n, dd, e, data)
37778     {
37779        
37780         var pt = this.getDropPoint(e, n, dd);
37781         var node = n.node;
37782         
37783         // auto node expand check
37784         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37785             this.queueExpand(node);
37786         }else if(pt != "append"){
37787             this.cancelExpand();
37788         }
37789         
37790         // set the insert point style on the target node
37791         var returnCls = this.dropNotAllowed;
37792         if(this.isValidDropPoint(n, pt, dd, e, data)){
37793            if(pt){
37794                var el = n.ddel;
37795                var cls;
37796                if(pt == "above"){
37797                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37798                    cls = "x-tree-drag-insert-above";
37799                }else if(pt == "below"){
37800                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37801                    cls = "x-tree-drag-insert-below";
37802                }else{
37803                    returnCls = "x-tree-drop-ok-append";
37804                    cls = "x-tree-drag-append";
37805                }
37806                if(this.lastInsertClass != cls){
37807                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37808                    this.lastInsertClass = cls;
37809                }
37810            }
37811        }
37812        return returnCls;
37813     },
37814     
37815     onNodeOut : function(n, dd, e, data){
37816         
37817         this.cancelExpand();
37818         this.removeDropIndicators(n);
37819     },
37820     
37821     onNodeDrop : function(n, dd, e, data){
37822         var point = this.getDropPoint(e, n, dd);
37823         var targetNode = n.node;
37824         targetNode.ui.startDrop();
37825         if(!this.isValidDropPoint(n, point, dd, e, data)){
37826             targetNode.ui.endDrop();
37827             return false;
37828         }
37829         // first try to find the drop node
37830         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37831         var dropEvent = {
37832             tree : this.tree,
37833             target: targetNode,
37834             data: data,
37835             point: point,
37836             source: dd,
37837             rawEvent: e,
37838             dropNode: dropNode,
37839             cancel: !dropNode   
37840         };
37841         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37842         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37843             targetNode.ui.endDrop();
37844             return false;
37845         }
37846         // allow target changing
37847         targetNode = dropEvent.target;
37848         if(point == "append" && !targetNode.isExpanded()){
37849             targetNode.expand(false, null, function(){
37850                 this.completeDrop(dropEvent);
37851             }.createDelegate(this));
37852         }else{
37853             this.completeDrop(dropEvent);
37854         }
37855         return true;
37856     },
37857     
37858     completeDrop : function(de){
37859         var ns = de.dropNode, p = de.point, t = de.target;
37860         if(!(ns instanceof Array)){
37861             ns = [ns];
37862         }
37863         var n;
37864         for(var i = 0, len = ns.length; i < len; i++){
37865             n = ns[i];
37866             if(p == "above"){
37867                 t.parentNode.insertBefore(n, t);
37868             }else if(p == "below"){
37869                 t.parentNode.insertBefore(n, t.nextSibling);
37870             }else{
37871                 t.appendChild(n);
37872             }
37873         }
37874         n.ui.focus();
37875         if(this.tree.hlDrop){
37876             n.ui.highlight();
37877         }
37878         t.ui.endDrop();
37879         this.tree.fireEvent("nodedrop", de);
37880     },
37881     
37882     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37883         if(this.tree.hlDrop){
37884             dropNode.ui.focus();
37885             dropNode.ui.highlight();
37886         }
37887         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37888     },
37889     
37890     getTree : function(){
37891         return this.tree;
37892     },
37893     
37894     removeDropIndicators : function(n){
37895         if(n && n.ddel){
37896             var el = n.ddel;
37897             Roo.fly(el).removeClass([
37898                     "x-tree-drag-insert-above",
37899                     "x-tree-drag-insert-below",
37900                     "x-tree-drag-append"]);
37901             this.lastInsertClass = "_noclass";
37902         }
37903     },
37904     
37905     beforeDragDrop : function(target, e, id){
37906         this.cancelExpand();
37907         return true;
37908     },
37909     
37910     afterRepair : function(data){
37911         if(data && Roo.enableFx){
37912             data.node.ui.highlight();
37913         }
37914         this.hideProxy();
37915     } 
37916     
37917 });
37918
37919 }
37920 /*
37921  * Based on:
37922  * Ext JS Library 1.1.1
37923  * Copyright(c) 2006-2007, Ext JS, LLC.
37924  *
37925  * Originally Released Under LGPL - original licence link has changed is not relivant.
37926  *
37927  * Fork - LGPL
37928  * <script type="text/javascript">
37929  */
37930  
37931
37932 if(Roo.dd.DragZone){
37933 Roo.tree.TreeDragZone = function(tree, config){
37934     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37935     this.tree = tree;
37936 };
37937
37938 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37939     ddGroup : "TreeDD",
37940    
37941     onBeforeDrag : function(data, e){
37942         var n = data.node;
37943         return n && n.draggable && !n.disabled;
37944     },
37945      
37946     
37947     onInitDrag : function(e){
37948         var data = this.dragData;
37949         this.tree.getSelectionModel().select(data.node);
37950         this.proxy.update("");
37951         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37952         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37953     },
37954     
37955     getRepairXY : function(e, data){
37956         return data.node.ui.getDDRepairXY();
37957     },
37958     
37959     onEndDrag : function(data, e){
37960         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37961         
37962         
37963     },
37964     
37965     onValidDrop : function(dd, e, id){
37966         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37967         this.hideProxy();
37968     },
37969     
37970     beforeInvalidDrop : function(e, id){
37971         // this scrolls the original position back into view
37972         var sm = this.tree.getSelectionModel();
37973         sm.clearSelections();
37974         sm.select(this.dragData.node);
37975     }
37976 });
37977 }/*
37978  * Based on:
37979  * Ext JS Library 1.1.1
37980  * Copyright(c) 2006-2007, Ext JS, LLC.
37981  *
37982  * Originally Released Under LGPL - original licence link has changed is not relivant.
37983  *
37984  * Fork - LGPL
37985  * <script type="text/javascript">
37986  */
37987 /**
37988  * @class Roo.tree.TreeEditor
37989  * @extends Roo.Editor
37990  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37991  * as the editor field.
37992  * @constructor
37993  * @param {Object} config (used to be the tree panel.)
37994  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37995  * 
37996  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37997  * @cfg {Roo.form.TextField} field [required] The field configuration
37998  *
37999  * 
38000  */
38001 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38002     var tree = config;
38003     var field;
38004     if (oldconfig) { // old style..
38005         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38006     } else {
38007         // new style..
38008         tree = config.tree;
38009         config.field = config.field  || {};
38010         config.field.xtype = 'TextField';
38011         field = Roo.factory(config.field, Roo.form);
38012     }
38013     config = config || {};
38014     
38015     
38016     this.addEvents({
38017         /**
38018          * @event beforenodeedit
38019          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38020          * false from the handler of this event.
38021          * @param {Editor} this
38022          * @param {Roo.tree.Node} node 
38023          */
38024         "beforenodeedit" : true
38025     });
38026     
38027     //Roo.log(config);
38028     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38029
38030     this.tree = tree;
38031
38032     tree.on('beforeclick', this.beforeNodeClick, this);
38033     tree.getTreeEl().on('mousedown', this.hide, this);
38034     this.on('complete', this.updateNode, this);
38035     this.on('beforestartedit', this.fitToTree, this);
38036     this.on('startedit', this.bindScroll, this, {delay:10});
38037     this.on('specialkey', this.onSpecialKey, this);
38038 };
38039
38040 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38041     /**
38042      * @cfg {String} alignment
38043      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38044      */
38045     alignment: "l-l",
38046     // inherit
38047     autoSize: false,
38048     /**
38049      * @cfg {Boolean} hideEl
38050      * True to hide the bound element while the editor is displayed (defaults to false)
38051      */
38052     hideEl : false,
38053     /**
38054      * @cfg {String} cls
38055      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38056      */
38057     cls: "x-small-editor x-tree-editor",
38058     /**
38059      * @cfg {Boolean} shim
38060      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38061      */
38062     shim:false,
38063     // inherit
38064     shadow:"frame",
38065     /**
38066      * @cfg {Number} maxWidth
38067      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38068      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38069      * scroll and client offsets into account prior to each edit.
38070      */
38071     maxWidth: 250,
38072
38073     editDelay : 350,
38074
38075     // private
38076     fitToTree : function(ed, el){
38077         var td = this.tree.getTreeEl().dom, nd = el.dom;
38078         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38079             td.scrollLeft = nd.offsetLeft;
38080         }
38081         var w = Math.min(
38082                 this.maxWidth,
38083                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38084         this.setSize(w, '');
38085         
38086         return this.fireEvent('beforenodeedit', this, this.editNode);
38087         
38088     },
38089
38090     // private
38091     triggerEdit : function(node){
38092         this.completeEdit();
38093         this.editNode = node;
38094         this.startEdit(node.ui.textNode, node.text);
38095     },
38096
38097     // private
38098     bindScroll : function(){
38099         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38100     },
38101
38102     // private
38103     beforeNodeClick : function(node, e){
38104         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38105         this.lastClick = new Date();
38106         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38107             e.stopEvent();
38108             this.triggerEdit(node);
38109             return false;
38110         }
38111         return true;
38112     },
38113
38114     // private
38115     updateNode : function(ed, value){
38116         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38117         this.editNode.setText(value);
38118     },
38119
38120     // private
38121     onHide : function(){
38122         Roo.tree.TreeEditor.superclass.onHide.call(this);
38123         if(this.editNode){
38124             this.editNode.ui.focus();
38125         }
38126     },
38127
38128     // private
38129     onSpecialKey : function(field, e){
38130         var k = e.getKey();
38131         if(k == e.ESC){
38132             e.stopEvent();
38133             this.cancelEdit();
38134         }else if(k == e.ENTER && !e.hasModifier()){
38135             e.stopEvent();
38136             this.completeEdit();
38137         }
38138     }
38139 });//<Script type="text/javascript">
38140 /*
38141  * Based on:
38142  * Ext JS Library 1.1.1
38143  * Copyright(c) 2006-2007, Ext JS, LLC.
38144  *
38145  * Originally Released Under LGPL - original licence link has changed is not relivant.
38146  *
38147  * Fork - LGPL
38148  * <script type="text/javascript">
38149  */
38150  
38151 /**
38152  * Not documented??? - probably should be...
38153  */
38154
38155 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38156     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38157     
38158     renderElements : function(n, a, targetNode, bulkRender){
38159         //consel.log("renderElements?");
38160         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38161
38162         var t = n.getOwnerTree();
38163         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38164         
38165         var cols = t.columns;
38166         var bw = t.borderWidth;
38167         var c = cols[0];
38168         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38169          var cb = typeof a.checked == "boolean";
38170         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38171         var colcls = 'x-t-' + tid + '-c0';
38172         var buf = [
38173             '<li class="x-tree-node">',
38174             
38175                 
38176                 '<div class="x-tree-node-el ', a.cls,'">',
38177                     // extran...
38178                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38179                 
38180                 
38181                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38182                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38183                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38184                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38185                            (a.iconCls ? ' '+a.iconCls : ''),
38186                            '" unselectable="on" />',
38187                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38188                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38189                              
38190                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38191                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38192                             '<span unselectable="on" qtip="' + tx + '">',
38193                              tx,
38194                              '</span></a>' ,
38195                     '</div>',
38196                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38197                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38198                  ];
38199         for(var i = 1, len = cols.length; i < len; i++){
38200             c = cols[i];
38201             colcls = 'x-t-' + tid + '-c' +i;
38202             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38203             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38204                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38205                       "</div>");
38206          }
38207          
38208          buf.push(
38209             '</a>',
38210             '<div class="x-clear"></div></div>',
38211             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38212             "</li>");
38213         
38214         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38215             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38216                                 n.nextSibling.ui.getEl(), buf.join(""));
38217         }else{
38218             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38219         }
38220         var el = this.wrap.firstChild;
38221         this.elRow = el;
38222         this.elNode = el.firstChild;
38223         this.ranchor = el.childNodes[1];
38224         this.ctNode = this.wrap.childNodes[1];
38225         var cs = el.firstChild.childNodes;
38226         this.indentNode = cs[0];
38227         this.ecNode = cs[1];
38228         this.iconNode = cs[2];
38229         var index = 3;
38230         if(cb){
38231             this.checkbox = cs[3];
38232             index++;
38233         }
38234         this.anchor = cs[index];
38235         
38236         this.textNode = cs[index].firstChild;
38237         
38238         //el.on("click", this.onClick, this);
38239         //el.on("dblclick", this.onDblClick, this);
38240         
38241         
38242        // console.log(this);
38243     },
38244     initEvents : function(){
38245         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38246         
38247             
38248         var a = this.ranchor;
38249
38250         var el = Roo.get(a);
38251
38252         if(Roo.isOpera){ // opera render bug ignores the CSS
38253             el.setStyle("text-decoration", "none");
38254         }
38255
38256         el.on("click", this.onClick, this);
38257         el.on("dblclick", this.onDblClick, this);
38258         el.on("contextmenu", this.onContextMenu, this);
38259         
38260     },
38261     
38262     /*onSelectedChange : function(state){
38263         if(state){
38264             this.focus();
38265             this.addClass("x-tree-selected");
38266         }else{
38267             //this.blur();
38268             this.removeClass("x-tree-selected");
38269         }
38270     },*/
38271     addClass : function(cls){
38272         if(this.elRow){
38273             Roo.fly(this.elRow).addClass(cls);
38274         }
38275         
38276     },
38277     
38278     
38279     removeClass : function(cls){
38280         if(this.elRow){
38281             Roo.fly(this.elRow).removeClass(cls);
38282         }
38283     }
38284
38285     
38286     
38287 });//<Script type="text/javascript">
38288
38289 /*
38290  * Based on:
38291  * Ext JS Library 1.1.1
38292  * Copyright(c) 2006-2007, Ext JS, LLC.
38293  *
38294  * Originally Released Under LGPL - original licence link has changed is not relivant.
38295  *
38296  * Fork - LGPL
38297  * <script type="text/javascript">
38298  */
38299  
38300
38301 /**
38302  * @class Roo.tree.ColumnTree
38303  * @extends Roo.tree.TreePanel
38304  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38305  * @cfg {int} borderWidth  compined right/left border allowance
38306  * @constructor
38307  * @param {String/HTMLElement/Element} el The container element
38308  * @param {Object} config
38309  */
38310 Roo.tree.ColumnTree =  function(el, config)
38311 {
38312    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38313    this.addEvents({
38314         /**
38315         * @event resize
38316         * Fire this event on a container when it resizes
38317         * @param {int} w Width
38318         * @param {int} h Height
38319         */
38320        "resize" : true
38321     });
38322     this.on('resize', this.onResize, this);
38323 };
38324
38325 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38326     //lines:false,
38327     
38328     
38329     borderWidth: Roo.isBorderBox ? 0 : 2, 
38330     headEls : false,
38331     
38332     render : function(){
38333         // add the header.....
38334        
38335         Roo.tree.ColumnTree.superclass.render.apply(this);
38336         
38337         this.el.addClass('x-column-tree');
38338         
38339         this.headers = this.el.createChild(
38340             {cls:'x-tree-headers'},this.innerCt.dom);
38341    
38342         var cols = this.columns, c;
38343         var totalWidth = 0;
38344         this.headEls = [];
38345         var  len = cols.length;
38346         for(var i = 0; i < len; i++){
38347              c = cols[i];
38348              totalWidth += c.width;
38349             this.headEls.push(this.headers.createChild({
38350                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38351                  cn: {
38352                      cls:'x-tree-hd-text',
38353                      html: c.header
38354                  },
38355                  style:'width:'+(c.width-this.borderWidth)+'px;'
38356              }));
38357         }
38358         this.headers.createChild({cls:'x-clear'});
38359         // prevent floats from wrapping when clipped
38360         this.headers.setWidth(totalWidth);
38361         //this.innerCt.setWidth(totalWidth);
38362         this.innerCt.setStyle({ overflow: 'auto' });
38363         this.onResize(this.width, this.height);
38364              
38365         
38366     },
38367     onResize : function(w,h)
38368     {
38369         this.height = h;
38370         this.width = w;
38371         // resize cols..
38372         this.innerCt.setWidth(this.width);
38373         this.innerCt.setHeight(this.height-20);
38374         
38375         // headers...
38376         var cols = this.columns, c;
38377         var totalWidth = 0;
38378         var expEl = false;
38379         var len = cols.length;
38380         for(var i = 0; i < len; i++){
38381             c = cols[i];
38382             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38383                 // it's the expander..
38384                 expEl  = this.headEls[i];
38385                 continue;
38386             }
38387             totalWidth += c.width;
38388             
38389         }
38390         if (expEl) {
38391             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38392         }
38393         this.headers.setWidth(w-20);
38394
38395         
38396         
38397         
38398     }
38399 });
38400 /*
38401  * Based on:
38402  * Ext JS Library 1.1.1
38403  * Copyright(c) 2006-2007, Ext JS, LLC.
38404  *
38405  * Originally Released Under LGPL - original licence link has changed is not relivant.
38406  *
38407  * Fork - LGPL
38408  * <script type="text/javascript">
38409  */
38410  
38411 /**
38412  * @class Roo.menu.Menu
38413  * @extends Roo.util.Observable
38414  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38415  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38416  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38417  * @constructor
38418  * Creates a new Menu
38419  * @param {Object} config Configuration options
38420  */
38421 Roo.menu.Menu = function(config){
38422     
38423     Roo.menu.Menu.superclass.constructor.call(this, config);
38424     
38425     this.id = this.id || Roo.id();
38426     this.addEvents({
38427         /**
38428          * @event beforeshow
38429          * Fires before this menu is displayed
38430          * @param {Roo.menu.Menu} this
38431          */
38432         beforeshow : true,
38433         /**
38434          * @event beforehide
38435          * Fires before this menu is hidden
38436          * @param {Roo.menu.Menu} this
38437          */
38438         beforehide : true,
38439         /**
38440          * @event show
38441          * Fires after this menu is displayed
38442          * @param {Roo.menu.Menu} this
38443          */
38444         show : true,
38445         /**
38446          * @event hide
38447          * Fires after this menu is hidden
38448          * @param {Roo.menu.Menu} this
38449          */
38450         hide : true,
38451         /**
38452          * @event click
38453          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38454          * @param {Roo.menu.Menu} this
38455          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38456          * @param {Roo.EventObject} e
38457          */
38458         click : true,
38459         /**
38460          * @event mouseover
38461          * Fires when the mouse is hovering over this menu
38462          * @param {Roo.menu.Menu} this
38463          * @param {Roo.EventObject} e
38464          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38465          */
38466         mouseover : true,
38467         /**
38468          * @event mouseout
38469          * Fires when the mouse exits this menu
38470          * @param {Roo.menu.Menu} this
38471          * @param {Roo.EventObject} e
38472          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38473          */
38474         mouseout : true,
38475         /**
38476          * @event itemclick
38477          * Fires when a menu item contained in this menu is clicked
38478          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38479          * @param {Roo.EventObject} e
38480          */
38481         itemclick: true
38482     });
38483     if (this.registerMenu) {
38484         Roo.menu.MenuMgr.register(this);
38485     }
38486     
38487     var mis = this.items;
38488     this.items = new Roo.util.MixedCollection();
38489     if(mis){
38490         this.add.apply(this, mis);
38491     }
38492 };
38493
38494 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38495     /**
38496      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38497      */
38498     minWidth : 120,
38499     /**
38500      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38501      * for bottom-right shadow (defaults to "sides")
38502      */
38503     shadow : "sides",
38504     /**
38505      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38506      * this menu (defaults to "tl-tr?")
38507      */
38508     subMenuAlign : "tl-tr?",
38509     /**
38510      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38511      * relative to its element of origin (defaults to "tl-bl?")
38512      */
38513     defaultAlign : "tl-bl?",
38514     /**
38515      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38516      */
38517     allowOtherMenus : false,
38518     /**
38519      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38520      */
38521     registerMenu : true,
38522
38523     hidden:true,
38524
38525     // private
38526     render : function(){
38527         if(this.el){
38528             return;
38529         }
38530         var el = this.el = new Roo.Layer({
38531             cls: "x-menu",
38532             shadow:this.shadow,
38533             constrain: false,
38534             parentEl: this.parentEl || document.body,
38535             zindex:15000
38536         });
38537
38538         this.keyNav = new Roo.menu.MenuNav(this);
38539
38540         if(this.plain){
38541             el.addClass("x-menu-plain");
38542         }
38543         if(this.cls){
38544             el.addClass(this.cls);
38545         }
38546         // generic focus element
38547         this.focusEl = el.createChild({
38548             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38549         });
38550         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38551         //disabling touch- as it's causing issues ..
38552         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38553         ul.on('click'   , this.onClick, this);
38554         
38555         
38556         ul.on("mouseover", this.onMouseOver, this);
38557         ul.on("mouseout", this.onMouseOut, this);
38558         this.items.each(function(item){
38559             if (item.hidden) {
38560                 return;
38561             }
38562             
38563             var li = document.createElement("li");
38564             li.className = "x-menu-list-item";
38565             ul.dom.appendChild(li);
38566             item.render(li, this);
38567         }, this);
38568         this.ul = ul;
38569         this.autoWidth();
38570     },
38571
38572     // private
38573     autoWidth : function(){
38574         var el = this.el, ul = this.ul;
38575         if(!el){
38576             return;
38577         }
38578         var w = this.width;
38579         if(w){
38580             el.setWidth(w);
38581         }else if(Roo.isIE){
38582             el.setWidth(this.minWidth);
38583             var t = el.dom.offsetWidth; // force recalc
38584             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38585         }
38586     },
38587
38588     // private
38589     delayAutoWidth : function(){
38590         if(this.rendered){
38591             if(!this.awTask){
38592                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38593             }
38594             this.awTask.delay(20);
38595         }
38596     },
38597
38598     // private
38599     findTargetItem : function(e){
38600         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38601         if(t && t.menuItemId){
38602             return this.items.get(t.menuItemId);
38603         }
38604     },
38605
38606     // private
38607     onClick : function(e){
38608         Roo.log("menu.onClick");
38609         var t = this.findTargetItem(e);
38610         if(!t){
38611             return;
38612         }
38613         Roo.log(e);
38614         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38615             if(t == this.activeItem && t.shouldDeactivate(e)){
38616                 this.activeItem.deactivate();
38617                 delete this.activeItem;
38618                 return;
38619             }
38620             if(t.canActivate){
38621                 this.setActiveItem(t, true);
38622             }
38623             return;
38624             
38625             
38626         }
38627         
38628         t.onClick(e);
38629         this.fireEvent("click", this, t, e);
38630     },
38631
38632     // private
38633     setActiveItem : function(item, autoExpand){
38634         if(item != this.activeItem){
38635             if(this.activeItem){
38636                 this.activeItem.deactivate();
38637             }
38638             this.activeItem = item;
38639             item.activate(autoExpand);
38640         }else if(autoExpand){
38641             item.expandMenu();
38642         }
38643     },
38644
38645     // private
38646     tryActivate : function(start, step){
38647         var items = this.items;
38648         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38649             var item = items.get(i);
38650             if(!item.disabled && item.canActivate){
38651                 this.setActiveItem(item, false);
38652                 return item;
38653             }
38654         }
38655         return false;
38656     },
38657
38658     // private
38659     onMouseOver : function(e){
38660         var t;
38661         if(t = this.findTargetItem(e)){
38662             if(t.canActivate && !t.disabled){
38663                 this.setActiveItem(t, true);
38664             }
38665         }
38666         this.fireEvent("mouseover", this, e, t);
38667     },
38668
38669     // private
38670     onMouseOut : function(e){
38671         var t;
38672         if(t = this.findTargetItem(e)){
38673             if(t == this.activeItem && t.shouldDeactivate(e)){
38674                 this.activeItem.deactivate();
38675                 delete this.activeItem;
38676             }
38677         }
38678         this.fireEvent("mouseout", this, e, t);
38679     },
38680
38681     /**
38682      * Read-only.  Returns true if the menu is currently displayed, else false.
38683      * @type Boolean
38684      */
38685     isVisible : function(){
38686         return this.el && !this.hidden;
38687     },
38688
38689     /**
38690      * Displays this menu relative to another element
38691      * @param {String/HTMLElement/Roo.Element} element The element to align to
38692      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38693      * the element (defaults to this.defaultAlign)
38694      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38695      */
38696     show : function(el, pos, parentMenu){
38697         this.parentMenu = parentMenu;
38698         if(!this.el){
38699             this.render();
38700         }
38701         this.fireEvent("beforeshow", this);
38702         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38703     },
38704
38705     /**
38706      * Displays this menu at a specific xy position
38707      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38708      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38709      */
38710     showAt : function(xy, parentMenu, /* private: */_e){
38711         this.parentMenu = parentMenu;
38712         if(!this.el){
38713             this.render();
38714         }
38715         if(_e !== false){
38716             this.fireEvent("beforeshow", this);
38717             xy = this.el.adjustForConstraints(xy);
38718         }
38719         this.el.setXY(xy);
38720         this.el.show();
38721         this.hidden = false;
38722         this.focus();
38723         this.fireEvent("show", this);
38724     },
38725
38726     focus : function(){
38727         if(!this.hidden){
38728             this.doFocus.defer(50, this);
38729         }
38730     },
38731
38732     doFocus : function(){
38733         if(!this.hidden){
38734             this.focusEl.focus();
38735         }
38736     },
38737
38738     /**
38739      * Hides this menu and optionally all parent menus
38740      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38741      */
38742     hide : function(deep){
38743         if(this.el && this.isVisible()){
38744             this.fireEvent("beforehide", this);
38745             if(this.activeItem){
38746                 this.activeItem.deactivate();
38747                 this.activeItem = null;
38748             }
38749             this.el.hide();
38750             this.hidden = true;
38751             this.fireEvent("hide", this);
38752         }
38753         if(deep === true && this.parentMenu){
38754             this.parentMenu.hide(true);
38755         }
38756     },
38757
38758     /**
38759      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38760      * Any of the following are valid:
38761      * <ul>
38762      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38763      * <li>An HTMLElement object which will be converted to a menu item</li>
38764      * <li>A menu item config object that will be created as a new menu item</li>
38765      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38766      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38767      * </ul>
38768      * Usage:
38769      * <pre><code>
38770 // Create the menu
38771 var menu = new Roo.menu.Menu();
38772
38773 // Create a menu item to add by reference
38774 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38775
38776 // Add a bunch of items at once using different methods.
38777 // Only the last item added will be returned.
38778 var item = menu.add(
38779     menuItem,                // add existing item by ref
38780     'Dynamic Item',          // new TextItem
38781     '-',                     // new separator
38782     { text: 'Config Item' }  // new item by config
38783 );
38784 </code></pre>
38785      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38786      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38787      */
38788     add : function(){
38789         var a = arguments, l = a.length, item;
38790         for(var i = 0; i < l; i++){
38791             var el = a[i];
38792             if ((typeof(el) == "object") && el.xtype && el.xns) {
38793                 el = Roo.factory(el, Roo.menu);
38794             }
38795             
38796             if(el.render){ // some kind of Item
38797                 item = this.addItem(el);
38798             }else if(typeof el == "string"){ // string
38799                 if(el == "separator" || el == "-"){
38800                     item = this.addSeparator();
38801                 }else{
38802                     item = this.addText(el);
38803                 }
38804             }else if(el.tagName || el.el){ // element
38805                 item = this.addElement(el);
38806             }else if(typeof el == "object"){ // must be menu item config?
38807                 item = this.addMenuItem(el);
38808             }
38809         }
38810         return item;
38811     },
38812
38813     /**
38814      * Returns this menu's underlying {@link Roo.Element} object
38815      * @return {Roo.Element} The element
38816      */
38817     getEl : function(){
38818         if(!this.el){
38819             this.render();
38820         }
38821         return this.el;
38822     },
38823
38824     /**
38825      * Adds a separator bar to the menu
38826      * @return {Roo.menu.Item} The menu item that was added
38827      */
38828     addSeparator : function(){
38829         return this.addItem(new Roo.menu.Separator());
38830     },
38831
38832     /**
38833      * Adds an {@link Roo.Element} object to the menu
38834      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38835      * @return {Roo.menu.Item} The menu item that was added
38836      */
38837     addElement : function(el){
38838         return this.addItem(new Roo.menu.BaseItem(el));
38839     },
38840
38841     /**
38842      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38843      * @param {Roo.menu.Item} item The menu item to add
38844      * @return {Roo.menu.Item} The menu item that was added
38845      */
38846     addItem : function(item){
38847         this.items.add(item);
38848         if(this.ul){
38849             var li = document.createElement("li");
38850             li.className = "x-menu-list-item";
38851             this.ul.dom.appendChild(li);
38852             item.render(li, this);
38853             this.delayAutoWidth();
38854         }
38855         return item;
38856     },
38857
38858     /**
38859      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38860      * @param {Object} config A MenuItem config object
38861      * @return {Roo.menu.Item} The menu item that was added
38862      */
38863     addMenuItem : function(config){
38864         if(!(config instanceof Roo.menu.Item)){
38865             if(typeof config.checked == "boolean"){ // must be check menu item config?
38866                 config = new Roo.menu.CheckItem(config);
38867             }else{
38868                 config = new Roo.menu.Item(config);
38869             }
38870         }
38871         return this.addItem(config);
38872     },
38873
38874     /**
38875      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38876      * @param {String} text The text to display in the menu item
38877      * @return {Roo.menu.Item} The menu item that was added
38878      */
38879     addText : function(text){
38880         return this.addItem(new Roo.menu.TextItem({ text : text }));
38881     },
38882
38883     /**
38884      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38885      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38886      * @param {Roo.menu.Item} item The menu item to add
38887      * @return {Roo.menu.Item} The menu item that was added
38888      */
38889     insert : function(index, item){
38890         this.items.insert(index, item);
38891         if(this.ul){
38892             var li = document.createElement("li");
38893             li.className = "x-menu-list-item";
38894             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38895             item.render(li, this);
38896             this.delayAutoWidth();
38897         }
38898         return item;
38899     },
38900
38901     /**
38902      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38903      * @param {Roo.menu.Item} item The menu item to remove
38904      */
38905     remove : function(item){
38906         this.items.removeKey(item.id);
38907         item.destroy();
38908     },
38909
38910     /**
38911      * Removes and destroys all items in the menu
38912      */
38913     removeAll : function(){
38914         var f;
38915         while(f = this.items.first()){
38916             this.remove(f);
38917         }
38918     }
38919 });
38920
38921 // MenuNav is a private utility class used internally by the Menu
38922 Roo.menu.MenuNav = function(menu){
38923     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38924     this.scope = this.menu = menu;
38925 };
38926
38927 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38928     doRelay : function(e, h){
38929         var k = e.getKey();
38930         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38931             this.menu.tryActivate(0, 1);
38932             return false;
38933         }
38934         return h.call(this.scope || this, e, this.menu);
38935     },
38936
38937     up : function(e, m){
38938         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38939             m.tryActivate(m.items.length-1, -1);
38940         }
38941     },
38942
38943     down : function(e, m){
38944         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38945             m.tryActivate(0, 1);
38946         }
38947     },
38948
38949     right : function(e, m){
38950         if(m.activeItem){
38951             m.activeItem.expandMenu(true);
38952         }
38953     },
38954
38955     left : function(e, m){
38956         m.hide();
38957         if(m.parentMenu && m.parentMenu.activeItem){
38958             m.parentMenu.activeItem.activate();
38959         }
38960     },
38961
38962     enter : function(e, m){
38963         if(m.activeItem){
38964             e.stopPropagation();
38965             m.activeItem.onClick(e);
38966             m.fireEvent("click", this, m.activeItem);
38967             return true;
38968         }
38969     }
38970 });/*
38971  * Based on:
38972  * Ext JS Library 1.1.1
38973  * Copyright(c) 2006-2007, Ext JS, LLC.
38974  *
38975  * Originally Released Under LGPL - original licence link has changed is not relivant.
38976  *
38977  * Fork - LGPL
38978  * <script type="text/javascript">
38979  */
38980  
38981 /**
38982  * @class Roo.menu.MenuMgr
38983  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38984  * @static
38985  */
38986 Roo.menu.MenuMgr = function(){
38987    var menus, active, groups = {}, attached = false, lastShow = new Date();
38988
38989    // private - called when first menu is created
38990    function init(){
38991        menus = {};
38992        active = new Roo.util.MixedCollection();
38993        Roo.get(document).addKeyListener(27, function(){
38994            if(active.length > 0){
38995                hideAll();
38996            }
38997        });
38998    }
38999
39000    // private
39001    function hideAll(){
39002        if(active && active.length > 0){
39003            var c = active.clone();
39004            c.each(function(m){
39005                m.hide();
39006            });
39007        }
39008    }
39009
39010    // private
39011    function onHide(m){
39012        active.remove(m);
39013        if(active.length < 1){
39014            Roo.get(document).un("mousedown", onMouseDown);
39015            attached = false;
39016        }
39017    }
39018
39019    // private
39020    function onShow(m){
39021        var last = active.last();
39022        lastShow = new Date();
39023        active.add(m);
39024        if(!attached){
39025            Roo.get(document).on("mousedown", onMouseDown);
39026            attached = true;
39027        }
39028        if(m.parentMenu){
39029           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39030           m.parentMenu.activeChild = m;
39031        }else if(last && last.isVisible()){
39032           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39033        }
39034    }
39035
39036    // private
39037    function onBeforeHide(m){
39038        if(m.activeChild){
39039            m.activeChild.hide();
39040        }
39041        if(m.autoHideTimer){
39042            clearTimeout(m.autoHideTimer);
39043            delete m.autoHideTimer;
39044        }
39045    }
39046
39047    // private
39048    function onBeforeShow(m){
39049        var pm = m.parentMenu;
39050        if(!pm && !m.allowOtherMenus){
39051            hideAll();
39052        }else if(pm && pm.activeChild && active != m){
39053            pm.activeChild.hide();
39054        }
39055    }
39056
39057    // private
39058    function onMouseDown(e){
39059        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39060            hideAll();
39061        }
39062    }
39063
39064    // private
39065    function onBeforeCheck(mi, state){
39066        if(state){
39067            var g = groups[mi.group];
39068            for(var i = 0, l = g.length; i < l; i++){
39069                if(g[i] != mi){
39070                    g[i].setChecked(false);
39071                }
39072            }
39073        }
39074    }
39075
39076    return {
39077
39078        /**
39079         * Hides all menus that are currently visible
39080         */
39081        hideAll : function(){
39082             hideAll();  
39083        },
39084
39085        // private
39086        register : function(menu){
39087            if(!menus){
39088                init();
39089            }
39090            menus[menu.id] = menu;
39091            menu.on("beforehide", onBeforeHide);
39092            menu.on("hide", onHide);
39093            menu.on("beforeshow", onBeforeShow);
39094            menu.on("show", onShow);
39095            var g = menu.group;
39096            if(g && menu.events["checkchange"]){
39097                if(!groups[g]){
39098                    groups[g] = [];
39099                }
39100                groups[g].push(menu);
39101                menu.on("checkchange", onCheck);
39102            }
39103        },
39104
39105         /**
39106          * Returns a {@link Roo.menu.Menu} object
39107          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39108          * be used to generate and return a new Menu instance.
39109          */
39110        get : function(menu){
39111            if(typeof menu == "string"){ // menu id
39112                return menus[menu];
39113            }else if(menu.events){  // menu instance
39114                return menu;
39115            }else if(typeof menu.length == 'number'){ // array of menu items?
39116                return new Roo.menu.Menu({items:menu});
39117            }else{ // otherwise, must be a config
39118                return new Roo.menu.Menu(menu);
39119            }
39120        },
39121
39122        // private
39123        unregister : function(menu){
39124            delete menus[menu.id];
39125            menu.un("beforehide", onBeforeHide);
39126            menu.un("hide", onHide);
39127            menu.un("beforeshow", onBeforeShow);
39128            menu.un("show", onShow);
39129            var g = menu.group;
39130            if(g && menu.events["checkchange"]){
39131                groups[g].remove(menu);
39132                menu.un("checkchange", onCheck);
39133            }
39134        },
39135
39136        // private
39137        registerCheckable : function(menuItem){
39138            var g = menuItem.group;
39139            if(g){
39140                if(!groups[g]){
39141                    groups[g] = [];
39142                }
39143                groups[g].push(menuItem);
39144                menuItem.on("beforecheckchange", onBeforeCheck);
39145            }
39146        },
39147
39148        // private
39149        unregisterCheckable : function(menuItem){
39150            var g = menuItem.group;
39151            if(g){
39152                groups[g].remove(menuItem);
39153                menuItem.un("beforecheckchange", onBeforeCheck);
39154            }
39155        }
39156    };
39157 }();/*
39158  * Based on:
39159  * Ext JS Library 1.1.1
39160  * Copyright(c) 2006-2007, Ext JS, LLC.
39161  *
39162  * Originally Released Under LGPL - original licence link has changed is not relivant.
39163  *
39164  * Fork - LGPL
39165  * <script type="text/javascript">
39166  */
39167  
39168
39169 /**
39170  * @class Roo.menu.BaseItem
39171  * @extends Roo.Component
39172  * @abstract
39173  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39174  * management and base configuration options shared by all menu components.
39175  * @constructor
39176  * Creates a new BaseItem
39177  * @param {Object} config Configuration options
39178  */
39179 Roo.menu.BaseItem = function(config){
39180     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39181
39182     this.addEvents({
39183         /**
39184          * @event click
39185          * Fires when this item is clicked
39186          * @param {Roo.menu.BaseItem} this
39187          * @param {Roo.EventObject} e
39188          */
39189         click: true,
39190         /**
39191          * @event activate
39192          * Fires when this item is activated
39193          * @param {Roo.menu.BaseItem} this
39194          */
39195         activate : true,
39196         /**
39197          * @event deactivate
39198          * Fires when this item is deactivated
39199          * @param {Roo.menu.BaseItem} this
39200          */
39201         deactivate : true
39202     });
39203
39204     if(this.handler){
39205         this.on("click", this.handler, this.scope, true);
39206     }
39207 };
39208
39209 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39210     /**
39211      * @cfg {Function} handler
39212      * A function that will handle the click event of this menu item (defaults to undefined)
39213      */
39214     /**
39215      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39216      */
39217     canActivate : false,
39218     
39219      /**
39220      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39221      */
39222     hidden: false,
39223     
39224     /**
39225      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39226      */
39227     activeClass : "x-menu-item-active",
39228     /**
39229      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39230      */
39231     hideOnClick : true,
39232     /**
39233      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39234      */
39235     hideDelay : 100,
39236
39237     // private
39238     ctype: "Roo.menu.BaseItem",
39239
39240     // private
39241     actionMode : "container",
39242
39243     // private
39244     render : function(container, parentMenu){
39245         this.parentMenu = parentMenu;
39246         Roo.menu.BaseItem.superclass.render.call(this, container);
39247         this.container.menuItemId = this.id;
39248     },
39249
39250     // private
39251     onRender : function(container, position){
39252         this.el = Roo.get(this.el);
39253         container.dom.appendChild(this.el.dom);
39254     },
39255
39256     // private
39257     onClick : function(e){
39258         if(!this.disabled && this.fireEvent("click", this, e) !== false
39259                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39260             this.handleClick(e);
39261         }else{
39262             e.stopEvent();
39263         }
39264     },
39265
39266     // private
39267     activate : function(){
39268         if(this.disabled){
39269             return false;
39270         }
39271         var li = this.container;
39272         li.addClass(this.activeClass);
39273         this.region = li.getRegion().adjust(2, 2, -2, -2);
39274         this.fireEvent("activate", this);
39275         return true;
39276     },
39277
39278     // private
39279     deactivate : function(){
39280         this.container.removeClass(this.activeClass);
39281         this.fireEvent("deactivate", this);
39282     },
39283
39284     // private
39285     shouldDeactivate : function(e){
39286         return !this.region || !this.region.contains(e.getPoint());
39287     },
39288
39289     // private
39290     handleClick : function(e){
39291         if(this.hideOnClick){
39292             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39293         }
39294     },
39295
39296     // private
39297     expandMenu : function(autoActivate){
39298         // do nothing
39299     },
39300
39301     // private
39302     hideMenu : function(){
39303         // do nothing
39304     }
39305 });/*
39306  * Based on:
39307  * Ext JS Library 1.1.1
39308  * Copyright(c) 2006-2007, Ext JS, LLC.
39309  *
39310  * Originally Released Under LGPL - original licence link has changed is not relivant.
39311  *
39312  * Fork - LGPL
39313  * <script type="text/javascript">
39314  */
39315  
39316 /**
39317  * @class Roo.menu.Adapter
39318  * @extends Roo.menu.BaseItem
39319  * @abstract
39320  * 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.
39321  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39322  * @constructor
39323  * Creates a new Adapter
39324  * @param {Object} config Configuration options
39325  */
39326 Roo.menu.Adapter = function(component, config){
39327     Roo.menu.Adapter.superclass.constructor.call(this, config);
39328     this.component = component;
39329 };
39330 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39331     // private
39332     canActivate : true,
39333
39334     // private
39335     onRender : function(container, position){
39336         this.component.render(container);
39337         this.el = this.component.getEl();
39338     },
39339
39340     // private
39341     activate : function(){
39342         if(this.disabled){
39343             return false;
39344         }
39345         this.component.focus();
39346         this.fireEvent("activate", this);
39347         return true;
39348     },
39349
39350     // private
39351     deactivate : function(){
39352         this.fireEvent("deactivate", this);
39353     },
39354
39355     // private
39356     disable : function(){
39357         this.component.disable();
39358         Roo.menu.Adapter.superclass.disable.call(this);
39359     },
39360
39361     // private
39362     enable : function(){
39363         this.component.enable();
39364         Roo.menu.Adapter.superclass.enable.call(this);
39365     }
39366 });/*
39367  * Based on:
39368  * Ext JS Library 1.1.1
39369  * Copyright(c) 2006-2007, Ext JS, LLC.
39370  *
39371  * Originally Released Under LGPL - original licence link has changed is not relivant.
39372  *
39373  * Fork - LGPL
39374  * <script type="text/javascript">
39375  */
39376
39377 /**
39378  * @class Roo.menu.TextItem
39379  * @extends Roo.menu.BaseItem
39380  * Adds a static text string to a menu, usually used as either a heading or group separator.
39381  * Note: old style constructor with text is still supported.
39382  * 
39383  * @constructor
39384  * Creates a new TextItem
39385  * @param {Object} cfg Configuration
39386  */
39387 Roo.menu.TextItem = function(cfg){
39388     if (typeof(cfg) == 'string') {
39389         this.text = cfg;
39390     } else {
39391         Roo.apply(this,cfg);
39392     }
39393     
39394     Roo.menu.TextItem.superclass.constructor.call(this);
39395 };
39396
39397 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39398     /**
39399      * @cfg {String} text Text to show on item.
39400      */
39401     text : '',
39402     
39403     /**
39404      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39405      */
39406     hideOnClick : false,
39407     /**
39408      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39409      */
39410     itemCls : "x-menu-text",
39411
39412     // private
39413     onRender : function(){
39414         var s = document.createElement("span");
39415         s.className = this.itemCls;
39416         s.innerHTML = this.text;
39417         this.el = s;
39418         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39419     }
39420 });/*
39421  * Based on:
39422  * Ext JS Library 1.1.1
39423  * Copyright(c) 2006-2007, Ext JS, LLC.
39424  *
39425  * Originally Released Under LGPL - original licence link has changed is not relivant.
39426  *
39427  * Fork - LGPL
39428  * <script type="text/javascript">
39429  */
39430
39431 /**
39432  * @class Roo.menu.Separator
39433  * @extends Roo.menu.BaseItem
39434  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39435  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39436  * @constructor
39437  * @param {Object} config Configuration options
39438  */
39439 Roo.menu.Separator = function(config){
39440     Roo.menu.Separator.superclass.constructor.call(this, config);
39441 };
39442
39443 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39444     /**
39445      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39446      */
39447     itemCls : "x-menu-sep",
39448     /**
39449      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39450      */
39451     hideOnClick : false,
39452
39453     // private
39454     onRender : function(li){
39455         var s = document.createElement("span");
39456         s.className = this.itemCls;
39457         s.innerHTML = "&#160;";
39458         this.el = s;
39459         li.addClass("x-menu-sep-li");
39460         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39461     }
39462 });/*
39463  * Based on:
39464  * Ext JS Library 1.1.1
39465  * Copyright(c) 2006-2007, Ext JS, LLC.
39466  *
39467  * Originally Released Under LGPL - original licence link has changed is not relivant.
39468  *
39469  * Fork - LGPL
39470  * <script type="text/javascript">
39471  */
39472 /**
39473  * @class Roo.menu.Item
39474  * @extends Roo.menu.BaseItem
39475  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39476  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39477  * activation and click handling.
39478  * @constructor
39479  * Creates a new Item
39480  * @param {Object} config Configuration options
39481  */
39482 Roo.menu.Item = function(config){
39483     Roo.menu.Item.superclass.constructor.call(this, config);
39484     if(this.menu){
39485         this.menu = Roo.menu.MenuMgr.get(this.menu);
39486     }
39487 };
39488 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39489     /**
39490      * @cfg {Roo.menu.Menu} menu
39491      * A Sub menu
39492      */
39493     /**
39494      * @cfg {String} text
39495      * The text to show on the menu item.
39496      */
39497     text: '',
39498      /**
39499      * @cfg {String} HTML to render in menu
39500      * The text to show on the menu item (HTML version).
39501      */
39502     html: '',
39503     /**
39504      * @cfg {String} icon
39505      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39506      */
39507     icon: undefined,
39508     /**
39509      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39510      */
39511     itemCls : "x-menu-item",
39512     /**
39513      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39514      */
39515     canActivate : true,
39516     /**
39517      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39518      */
39519     showDelay: 200,
39520     // doc'd in BaseItem
39521     hideDelay: 200,
39522
39523     // private
39524     ctype: "Roo.menu.Item",
39525     
39526     // private
39527     onRender : function(container, position){
39528         var el = document.createElement("a");
39529         el.hideFocus = true;
39530         el.unselectable = "on";
39531         el.href = this.href || "#";
39532         if(this.hrefTarget){
39533             el.target = this.hrefTarget;
39534         }
39535         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39536         
39537         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39538         
39539         el.innerHTML = String.format(
39540                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39541                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39542         this.el = el;
39543         Roo.menu.Item.superclass.onRender.call(this, container, position);
39544     },
39545
39546     /**
39547      * Sets the text to display in this menu item
39548      * @param {String} text The text to display
39549      * @param {Boolean} isHTML true to indicate text is pure html.
39550      */
39551     setText : function(text, isHTML){
39552         if (isHTML) {
39553             this.html = text;
39554         } else {
39555             this.text = text;
39556             this.html = '';
39557         }
39558         if(this.rendered){
39559             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39560      
39561             this.el.update(String.format(
39562                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39563                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39564             this.parentMenu.autoWidth();
39565         }
39566     },
39567
39568     // private
39569     handleClick : function(e){
39570         if(!this.href){ // if no link defined, stop the event automatically
39571             e.stopEvent();
39572         }
39573         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39574     },
39575
39576     // private
39577     activate : function(autoExpand){
39578         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39579             this.focus();
39580             if(autoExpand){
39581                 this.expandMenu();
39582             }
39583         }
39584         return true;
39585     },
39586
39587     // private
39588     shouldDeactivate : function(e){
39589         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39590             if(this.menu && this.menu.isVisible()){
39591                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39592             }
39593             return true;
39594         }
39595         return false;
39596     },
39597
39598     // private
39599     deactivate : function(){
39600         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39601         this.hideMenu();
39602     },
39603
39604     // private
39605     expandMenu : function(autoActivate){
39606         if(!this.disabled && this.menu){
39607             clearTimeout(this.hideTimer);
39608             delete this.hideTimer;
39609             if(!this.menu.isVisible() && !this.showTimer){
39610                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39611             }else if (this.menu.isVisible() && autoActivate){
39612                 this.menu.tryActivate(0, 1);
39613             }
39614         }
39615     },
39616
39617     // private
39618     deferExpand : function(autoActivate){
39619         delete this.showTimer;
39620         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39621         if(autoActivate){
39622             this.menu.tryActivate(0, 1);
39623         }
39624     },
39625
39626     // private
39627     hideMenu : function(){
39628         clearTimeout(this.showTimer);
39629         delete this.showTimer;
39630         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39631             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39632         }
39633     },
39634
39635     // private
39636     deferHide : function(){
39637         delete this.hideTimer;
39638         this.menu.hide();
39639     }
39640 });/*
39641  * Based on:
39642  * Ext JS Library 1.1.1
39643  * Copyright(c) 2006-2007, Ext JS, LLC.
39644  *
39645  * Originally Released Under LGPL - original licence link has changed is not relivant.
39646  *
39647  * Fork - LGPL
39648  * <script type="text/javascript">
39649  */
39650  
39651 /**
39652  * @class Roo.menu.CheckItem
39653  * @extends Roo.menu.Item
39654  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39655  * @constructor
39656  * Creates a new CheckItem
39657  * @param {Object} config Configuration options
39658  */
39659 Roo.menu.CheckItem = function(config){
39660     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39661     this.addEvents({
39662         /**
39663          * @event beforecheckchange
39664          * Fires before the checked value is set, providing an opportunity to cancel if needed
39665          * @param {Roo.menu.CheckItem} this
39666          * @param {Boolean} checked The new checked value that will be set
39667          */
39668         "beforecheckchange" : true,
39669         /**
39670          * @event checkchange
39671          * Fires after the checked value has been set
39672          * @param {Roo.menu.CheckItem} this
39673          * @param {Boolean} checked The checked value that was set
39674          */
39675         "checkchange" : true
39676     });
39677     if(this.checkHandler){
39678         this.on('checkchange', this.checkHandler, this.scope);
39679     }
39680 };
39681 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39682     /**
39683      * @cfg {String} group
39684      * All check items with the same group name will automatically be grouped into a single-select
39685      * radio button group (defaults to '')
39686      */
39687     /**
39688      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39689      */
39690     itemCls : "x-menu-item x-menu-check-item",
39691     /**
39692      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39693      */
39694     groupClass : "x-menu-group-item",
39695
39696     /**
39697      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39698      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39699      * initialized with checked = true will be rendered as checked.
39700      */
39701     checked: false,
39702
39703     // private
39704     ctype: "Roo.menu.CheckItem",
39705
39706     // private
39707     onRender : function(c){
39708         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39709         if(this.group){
39710             this.el.addClass(this.groupClass);
39711         }
39712         Roo.menu.MenuMgr.registerCheckable(this);
39713         if(this.checked){
39714             this.checked = false;
39715             this.setChecked(true, true);
39716         }
39717     },
39718
39719     // private
39720     destroy : function(){
39721         if(this.rendered){
39722             Roo.menu.MenuMgr.unregisterCheckable(this);
39723         }
39724         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39725     },
39726
39727     /**
39728      * Set the checked state of this item
39729      * @param {Boolean} checked The new checked value
39730      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39731      */
39732     setChecked : function(state, suppressEvent){
39733         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39734             if(this.container){
39735                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39736             }
39737             this.checked = state;
39738             if(suppressEvent !== true){
39739                 this.fireEvent("checkchange", this, state);
39740             }
39741         }
39742     },
39743
39744     // private
39745     handleClick : function(e){
39746        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39747            this.setChecked(!this.checked);
39748        }
39749        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39750     }
39751 });/*
39752  * Based on:
39753  * Ext JS Library 1.1.1
39754  * Copyright(c) 2006-2007, Ext JS, LLC.
39755  *
39756  * Originally Released Under LGPL - original licence link has changed is not relivant.
39757  *
39758  * Fork - LGPL
39759  * <script type="text/javascript">
39760  */
39761  
39762 /**
39763  * @class Roo.menu.DateItem
39764  * @extends Roo.menu.Adapter
39765  * A menu item that wraps the {@link Roo.DatPicker} component.
39766  * @constructor
39767  * Creates a new DateItem
39768  * @param {Object} config Configuration options
39769  */
39770 Roo.menu.DateItem = function(config){
39771     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39772     /** The Roo.DatePicker object @type Roo.DatePicker */
39773     this.picker = this.component;
39774     this.addEvents({select: true});
39775     
39776     this.picker.on("render", function(picker){
39777         picker.getEl().swallowEvent("click");
39778         picker.container.addClass("x-menu-date-item");
39779     });
39780
39781     this.picker.on("select", this.onSelect, this);
39782 };
39783
39784 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39785     // private
39786     onSelect : function(picker, date){
39787         this.fireEvent("select", this, date, picker);
39788         Roo.menu.DateItem.superclass.handleClick.call(this);
39789     }
39790 });/*
39791  * Based on:
39792  * Ext JS Library 1.1.1
39793  * Copyright(c) 2006-2007, Ext JS, LLC.
39794  *
39795  * Originally Released Under LGPL - original licence link has changed is not relivant.
39796  *
39797  * Fork - LGPL
39798  * <script type="text/javascript">
39799  */
39800  
39801 /**
39802  * @class Roo.menu.ColorItem
39803  * @extends Roo.menu.Adapter
39804  * A menu item that wraps the {@link Roo.ColorPalette} component.
39805  * @constructor
39806  * Creates a new ColorItem
39807  * @param {Object} config Configuration options
39808  */
39809 Roo.menu.ColorItem = function(config){
39810     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39811     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39812     this.palette = this.component;
39813     this.relayEvents(this.palette, ["select"]);
39814     if(this.selectHandler){
39815         this.on('select', this.selectHandler, this.scope);
39816     }
39817 };
39818 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39819  * Based on:
39820  * Ext JS Library 1.1.1
39821  * Copyright(c) 2006-2007, Ext JS, LLC.
39822  *
39823  * Originally Released Under LGPL - original licence link has changed is not relivant.
39824  *
39825  * Fork - LGPL
39826  * <script type="text/javascript">
39827  */
39828  
39829
39830 /**
39831  * @class Roo.menu.DateMenu
39832  * @extends Roo.menu.Menu
39833  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39834  * @constructor
39835  * Creates a new DateMenu
39836  * @param {Object} config Configuration options
39837  */
39838 Roo.menu.DateMenu = function(config){
39839     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39840     this.plain = true;
39841     var di = new Roo.menu.DateItem(config);
39842     this.add(di);
39843     /**
39844      * The {@link Roo.DatePicker} instance for this DateMenu
39845      * @type DatePicker
39846      */
39847     this.picker = di.picker;
39848     /**
39849      * @event select
39850      * @param {DatePicker} picker
39851      * @param {Date} date
39852      */
39853     this.relayEvents(di, ["select"]);
39854     this.on('beforeshow', function(){
39855         if(this.picker){
39856             this.picker.hideMonthPicker(false);
39857         }
39858     }, this);
39859 };
39860 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39861     cls:'x-date-menu'
39862 });/*
39863  * Based on:
39864  * Ext JS Library 1.1.1
39865  * Copyright(c) 2006-2007, Ext JS, LLC.
39866  *
39867  * Originally Released Under LGPL - original licence link has changed is not relivant.
39868  *
39869  * Fork - LGPL
39870  * <script type="text/javascript">
39871  */
39872  
39873
39874 /**
39875  * @class Roo.menu.ColorMenu
39876  * @extends Roo.menu.Menu
39877  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39878  * @constructor
39879  * Creates a new ColorMenu
39880  * @param {Object} config Configuration options
39881  */
39882 Roo.menu.ColorMenu = function(config){
39883     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39884     this.plain = true;
39885     var ci = new Roo.menu.ColorItem(config);
39886     this.add(ci);
39887     /**
39888      * The {@link Roo.ColorPalette} instance for this ColorMenu
39889      * @type ColorPalette
39890      */
39891     this.palette = ci.palette;
39892     /**
39893      * @event select
39894      * @param {ColorPalette} palette
39895      * @param {String} color
39896      */
39897     this.relayEvents(ci, ["select"]);
39898 };
39899 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39900  * Based on:
39901  * Ext JS Library 1.1.1
39902  * Copyright(c) 2006-2007, Ext JS, LLC.
39903  *
39904  * Originally Released Under LGPL - original licence link has changed is not relivant.
39905  *
39906  * Fork - LGPL
39907  * <script type="text/javascript">
39908  */
39909  
39910 /**
39911  * @class Roo.form.TextItem
39912  * @extends Roo.BoxComponent
39913  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39914  * @constructor
39915  * Creates a new TextItem
39916  * @param {Object} config Configuration options
39917  */
39918 Roo.form.TextItem = function(config){
39919     Roo.form.TextItem.superclass.constructor.call(this, config);
39920 };
39921
39922 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39923     
39924     /**
39925      * @cfg {String} tag the tag for this item (default div)
39926      */
39927     tag : 'div',
39928     /**
39929      * @cfg {String} html the content for this item
39930      */
39931     html : '',
39932     
39933     getAutoCreate : function()
39934     {
39935         var cfg = {
39936             id: this.id,
39937             tag: this.tag,
39938             html: this.html,
39939             cls: 'x-form-item'
39940         };
39941         
39942         return cfg;
39943         
39944     },
39945     
39946     onRender : function(ct, position)
39947     {
39948         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39949         
39950         if(!this.el){
39951             var cfg = this.getAutoCreate();
39952             if(!cfg.name){
39953                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39954             }
39955             if (!cfg.name.length) {
39956                 delete cfg.name;
39957             }
39958             this.el = ct.createChild(cfg, position);
39959         }
39960     },
39961     /*
39962      * setHTML
39963      * @param {String} html update the Contents of the element.
39964      */
39965     setHTML : function(html)
39966     {
39967         this.fieldEl.dom.innerHTML = html;
39968     }
39969     
39970 });/*
39971  * Based on:
39972  * Ext JS Library 1.1.1
39973  * Copyright(c) 2006-2007, Ext JS, LLC.
39974  *
39975  * Originally Released Under LGPL - original licence link has changed is not relivant.
39976  *
39977  * Fork - LGPL
39978  * <script type="text/javascript">
39979  */
39980  
39981 /**
39982  * @class Roo.form.Field
39983  * @extends Roo.BoxComponent
39984  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39985  * @constructor
39986  * Creates a new Field
39987  * @param {Object} config Configuration options
39988  */
39989 Roo.form.Field = function(config){
39990     Roo.form.Field.superclass.constructor.call(this, config);
39991 };
39992
39993 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39994     /**
39995      * @cfg {String} fieldLabel Label to use when rendering a form.
39996      */
39997        /**
39998      * @cfg {String} qtip Mouse over tip
39999      */
40000      
40001     /**
40002      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40003      */
40004     invalidClass : "x-form-invalid",
40005     /**
40006      * @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")
40007      */
40008     invalidText : "The value in this field is invalid",
40009     /**
40010      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40011      */
40012     focusClass : "x-form-focus",
40013     /**
40014      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40015       automatic validation (defaults to "keyup").
40016      */
40017     validationEvent : "keyup",
40018     /**
40019      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40020      */
40021     validateOnBlur : true,
40022     /**
40023      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40024      */
40025     validationDelay : 250,
40026     /**
40027      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40028      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40029      */
40030     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40031     /**
40032      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40033      */
40034     fieldClass : "x-form-field",
40035     /**
40036      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40037      *<pre>
40038 Value         Description
40039 -----------   ----------------------------------------------------------------------
40040 qtip          Display a quick tip when the user hovers over the field
40041 title         Display a default browser title attribute popup
40042 under         Add a block div beneath the field containing the error text
40043 side          Add an error icon to the right of the field with a popup on hover
40044 [element id]  Add the error text directly to the innerHTML of the specified element
40045 </pre>
40046      */
40047     msgTarget : 'qtip',
40048     /**
40049      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40050      */
40051     msgFx : 'normal',
40052
40053     /**
40054      * @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.
40055      */
40056     readOnly : false,
40057
40058     /**
40059      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40060      */
40061     disabled : false,
40062
40063     /**
40064      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40065      */
40066     inputType : undefined,
40067     
40068     /**
40069      * @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).
40070          */
40071         tabIndex : undefined,
40072         
40073     // private
40074     isFormField : true,
40075
40076     // private
40077     hasFocus : false,
40078     /**
40079      * @property {Roo.Element} fieldEl
40080      * Element Containing the rendered Field (with label etc.)
40081      */
40082     /**
40083      * @cfg {Mixed} value A value to initialize this field with.
40084      */
40085     value : undefined,
40086
40087     /**
40088      * @cfg {String} name The field's HTML name attribute.
40089      */
40090     /**
40091      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40092      */
40093     // private
40094     loadedValue : false,
40095      
40096      
40097         // private ??
40098         initComponent : function(){
40099         Roo.form.Field.superclass.initComponent.call(this);
40100         this.addEvents({
40101             /**
40102              * @event focus
40103              * Fires when this field receives input focus.
40104              * @param {Roo.form.Field} this
40105              */
40106             focus : true,
40107             /**
40108              * @event blur
40109              * Fires when this field loses input focus.
40110              * @param {Roo.form.Field} this
40111              */
40112             blur : true,
40113             /**
40114              * @event specialkey
40115              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40116              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40117              * @param {Roo.form.Field} this
40118              * @param {Roo.EventObject} e The event object
40119              */
40120             specialkey : true,
40121             /**
40122              * @event change
40123              * Fires just before the field blurs if the field value has changed.
40124              * @param {Roo.form.Field} this
40125              * @param {Mixed} newValue The new value
40126              * @param {Mixed} oldValue The original value
40127              */
40128             change : true,
40129             /**
40130              * @event invalid
40131              * Fires after the field has been marked as invalid.
40132              * @param {Roo.form.Field} this
40133              * @param {String} msg The validation message
40134              */
40135             invalid : true,
40136             /**
40137              * @event valid
40138              * Fires after the field has been validated with no errors.
40139              * @param {Roo.form.Field} this
40140              */
40141             valid : true,
40142              /**
40143              * @event keyup
40144              * Fires after the key up
40145              * @param {Roo.form.Field} this
40146              * @param {Roo.EventObject}  e The event Object
40147              */
40148             keyup : true
40149         });
40150     },
40151
40152     /**
40153      * Returns the name attribute of the field if available
40154      * @return {String} name The field name
40155      */
40156     getName: function(){
40157          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40158     },
40159
40160     // private
40161     onRender : function(ct, position){
40162         Roo.form.Field.superclass.onRender.call(this, ct, position);
40163         if(!this.el){
40164             var cfg = this.getAutoCreate();
40165             if(!cfg.name){
40166                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40167             }
40168             if (!cfg.name.length) {
40169                 delete cfg.name;
40170             }
40171             if(this.inputType){
40172                 cfg.type = this.inputType;
40173             }
40174             this.el = ct.createChild(cfg, position);
40175         }
40176         var type = this.el.dom.type;
40177         if(type){
40178             if(type == 'password'){
40179                 type = 'text';
40180             }
40181             this.el.addClass('x-form-'+type);
40182         }
40183         if(this.readOnly){
40184             this.el.dom.readOnly = true;
40185         }
40186         if(this.tabIndex !== undefined){
40187             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40188         }
40189
40190         this.el.addClass([this.fieldClass, this.cls]);
40191         this.initValue();
40192     },
40193
40194     /**
40195      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40196      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40197      * @return {Roo.form.Field} this
40198      */
40199     applyTo : function(target){
40200         this.allowDomMove = false;
40201         this.el = Roo.get(target);
40202         this.render(this.el.dom.parentNode);
40203         return this;
40204     },
40205
40206     // private
40207     initValue : function(){
40208         if(this.value !== undefined){
40209             this.setValue(this.value);
40210         }else if(this.el.dom.value.length > 0){
40211             this.setValue(this.el.dom.value);
40212         }
40213     },
40214
40215     /**
40216      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40217      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40218      */
40219     isDirty : function() {
40220         if(this.disabled) {
40221             return false;
40222         }
40223         return String(this.getValue()) !== String(this.originalValue);
40224     },
40225
40226     /**
40227      * stores the current value in loadedValue
40228      */
40229     resetHasChanged : function()
40230     {
40231         this.loadedValue = String(this.getValue());
40232     },
40233     /**
40234      * checks the current value against the 'loaded' value.
40235      * Note - will return false if 'resetHasChanged' has not been called first.
40236      */
40237     hasChanged : function()
40238     {
40239         if(this.disabled || this.readOnly) {
40240             return false;
40241         }
40242         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40243     },
40244     
40245     
40246     
40247     // private
40248     afterRender : function(){
40249         Roo.form.Field.superclass.afterRender.call(this);
40250         this.initEvents();
40251     },
40252
40253     // private
40254     fireKey : function(e){
40255         //Roo.log('field ' + e.getKey());
40256         if(e.isNavKeyPress()){
40257             this.fireEvent("specialkey", this, e);
40258         }
40259     },
40260
40261     /**
40262      * Resets the current field value to the originally loaded value and clears any validation messages
40263      */
40264     reset : function(){
40265         this.setValue(this.resetValue);
40266         this.originalValue = this.getValue();
40267         this.clearInvalid();
40268     },
40269
40270     // private
40271     initEvents : function(){
40272         // safari killled keypress - so keydown is now used..
40273         this.el.on("keydown" , this.fireKey,  this);
40274         this.el.on("focus", this.onFocus,  this);
40275         this.el.on("blur", this.onBlur,  this);
40276         this.el.relayEvent('keyup', this);
40277
40278         // reference to original value for reset
40279         this.originalValue = this.getValue();
40280         this.resetValue =  this.getValue();
40281     },
40282
40283     // private
40284     onFocus : function(){
40285         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40286             this.el.addClass(this.focusClass);
40287         }
40288         if(!this.hasFocus){
40289             this.hasFocus = true;
40290             this.startValue = this.getValue();
40291             this.fireEvent("focus", this);
40292         }
40293     },
40294
40295     beforeBlur : Roo.emptyFn,
40296
40297     // private
40298     onBlur : function(){
40299         this.beforeBlur();
40300         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40301             this.el.removeClass(this.focusClass);
40302         }
40303         this.hasFocus = false;
40304         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40305             this.validate();
40306         }
40307         var v = this.getValue();
40308         if(String(v) !== String(this.startValue)){
40309             this.fireEvent('change', this, v, this.startValue);
40310         }
40311         this.fireEvent("blur", this);
40312     },
40313
40314     /**
40315      * Returns whether or not the field value is currently valid
40316      * @param {Boolean} preventMark True to disable marking the field invalid
40317      * @return {Boolean} True if the value is valid, else false
40318      */
40319     isValid : function(preventMark){
40320         if(this.disabled){
40321             return true;
40322         }
40323         var restore = this.preventMark;
40324         this.preventMark = preventMark === true;
40325         var v = this.validateValue(this.processValue(this.getRawValue()));
40326         this.preventMark = restore;
40327         return v;
40328     },
40329
40330     /**
40331      * Validates the field value
40332      * @return {Boolean} True if the value is valid, else false
40333      */
40334     validate : function(){
40335         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40336             this.clearInvalid();
40337             return true;
40338         }
40339         return false;
40340     },
40341
40342     processValue : function(value){
40343         return value;
40344     },
40345
40346     // private
40347     // Subclasses should provide the validation implementation by overriding this
40348     validateValue : function(value){
40349         return true;
40350     },
40351
40352     /**
40353      * Mark this field as invalid
40354      * @param {String} msg The validation message
40355      */
40356     markInvalid : function(msg){
40357         if(!this.rendered || this.preventMark){ // not rendered
40358             return;
40359         }
40360         
40361         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40362         
40363         obj.el.addClass(this.invalidClass);
40364         msg = msg || this.invalidText;
40365         switch(this.msgTarget){
40366             case 'qtip':
40367                 obj.el.dom.qtip = msg;
40368                 obj.el.dom.qclass = 'x-form-invalid-tip';
40369                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40370                     Roo.QuickTips.enable();
40371                 }
40372                 break;
40373             case 'title':
40374                 this.el.dom.title = msg;
40375                 break;
40376             case 'under':
40377                 if(!this.errorEl){
40378                     var elp = this.el.findParent('.x-form-element', 5, true);
40379                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40380                     this.errorEl.setWidth(elp.getWidth(true)-20);
40381                 }
40382                 this.errorEl.update(msg);
40383                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40384                 break;
40385             case 'side':
40386                 if(!this.errorIcon){
40387                     var elp = this.el.findParent('.x-form-element', 5, true);
40388                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40389                 }
40390                 this.alignErrorIcon();
40391                 this.errorIcon.dom.qtip = msg;
40392                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40393                 this.errorIcon.show();
40394                 this.on('resize', this.alignErrorIcon, this);
40395                 break;
40396             default:
40397                 var t = Roo.getDom(this.msgTarget);
40398                 t.innerHTML = msg;
40399                 t.style.display = this.msgDisplay;
40400                 break;
40401         }
40402         this.fireEvent('invalid', this, msg);
40403     },
40404
40405     // private
40406     alignErrorIcon : function(){
40407         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40408     },
40409
40410     /**
40411      * Clear any invalid styles/messages for this field
40412      */
40413     clearInvalid : function(){
40414         if(!this.rendered || this.preventMark){ // not rendered
40415             return;
40416         }
40417         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40418         
40419         obj.el.removeClass(this.invalidClass);
40420         switch(this.msgTarget){
40421             case 'qtip':
40422                 obj.el.dom.qtip = '';
40423                 break;
40424             case 'title':
40425                 this.el.dom.title = '';
40426                 break;
40427             case 'under':
40428                 if(this.errorEl){
40429                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40430                 }
40431                 break;
40432             case 'side':
40433                 if(this.errorIcon){
40434                     this.errorIcon.dom.qtip = '';
40435                     this.errorIcon.hide();
40436                     this.un('resize', this.alignErrorIcon, this);
40437                 }
40438                 break;
40439             default:
40440                 var t = Roo.getDom(this.msgTarget);
40441                 t.innerHTML = '';
40442                 t.style.display = 'none';
40443                 break;
40444         }
40445         this.fireEvent('valid', this);
40446     },
40447
40448     /**
40449      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40450      * @return {Mixed} value The field value
40451      */
40452     getRawValue : function(){
40453         var v = this.el.getValue();
40454         
40455         return v;
40456     },
40457
40458     /**
40459      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40460      * @return {Mixed} value The field value
40461      */
40462     getValue : function(){
40463         var v = this.el.getValue();
40464          
40465         return v;
40466     },
40467
40468     /**
40469      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40470      * @param {Mixed} value The value to set
40471      */
40472     setRawValue : function(v){
40473         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40474     },
40475
40476     /**
40477      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40478      * @param {Mixed} value The value to set
40479      */
40480     setValue : function(v){
40481         this.value = v;
40482         if(this.rendered){
40483             this.el.dom.value = (v === null || v === undefined ? '' : v);
40484              this.validate();
40485         }
40486     },
40487
40488     adjustSize : function(w, h){
40489         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40490         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40491         return s;
40492     },
40493
40494     adjustWidth : function(tag, w){
40495         tag = tag.toLowerCase();
40496         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40497             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40498                 if(tag == 'input'){
40499                     return w + 2;
40500                 }
40501                 if(tag == 'textarea'){
40502                     return w-2;
40503                 }
40504             }else if(Roo.isOpera){
40505                 if(tag == 'input'){
40506                     return w + 2;
40507                 }
40508                 if(tag == 'textarea'){
40509                     return w-2;
40510                 }
40511             }
40512         }
40513         return w;
40514     }
40515 });
40516
40517
40518 // anything other than normal should be considered experimental
40519 Roo.form.Field.msgFx = {
40520     normal : {
40521         show: function(msgEl, f){
40522             msgEl.setDisplayed('block');
40523         },
40524
40525         hide : function(msgEl, f){
40526             msgEl.setDisplayed(false).update('');
40527         }
40528     },
40529
40530     slide : {
40531         show: function(msgEl, f){
40532             msgEl.slideIn('t', {stopFx:true});
40533         },
40534
40535         hide : function(msgEl, f){
40536             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40537         }
40538     },
40539
40540     slideRight : {
40541         show: function(msgEl, f){
40542             msgEl.fixDisplay();
40543             msgEl.alignTo(f.el, 'tl-tr');
40544             msgEl.slideIn('l', {stopFx:true});
40545         },
40546
40547         hide : function(msgEl, f){
40548             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40549         }
40550     }
40551 };/*
40552  * Based on:
40553  * Ext JS Library 1.1.1
40554  * Copyright(c) 2006-2007, Ext JS, LLC.
40555  *
40556  * Originally Released Under LGPL - original licence link has changed is not relivant.
40557  *
40558  * Fork - LGPL
40559  * <script type="text/javascript">
40560  */
40561  
40562
40563 /**
40564  * @class Roo.form.TextField
40565  * @extends Roo.form.Field
40566  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40567  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40568  * @constructor
40569  * Creates a new TextField
40570  * @param {Object} config Configuration options
40571  */
40572 Roo.form.TextField = function(config){
40573     Roo.form.TextField.superclass.constructor.call(this, config);
40574     this.addEvents({
40575         /**
40576          * @event autosize
40577          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40578          * according to the default logic, but this event provides a hook for the developer to apply additional
40579          * logic at runtime to resize the field if needed.
40580              * @param {Roo.form.Field} this This text field
40581              * @param {Number} width The new field width
40582              */
40583         autosize : true
40584     });
40585 };
40586
40587 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40588     /**
40589      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40590      */
40591     grow : false,
40592     /**
40593      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40594      */
40595     growMin : 30,
40596     /**
40597      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40598      */
40599     growMax : 800,
40600     /**
40601      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40602      */
40603     vtype : null,
40604     /**
40605      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40606      */
40607     maskRe : null,
40608     /**
40609      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40610      */
40611     disableKeyFilter : false,
40612     /**
40613      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40614      */
40615     allowBlank : true,
40616     /**
40617      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40618      */
40619     minLength : 0,
40620     /**
40621      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40622      */
40623     maxLength : Number.MAX_VALUE,
40624     /**
40625      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40626      */
40627     minLengthText : "The minimum length for this field is {0}",
40628     /**
40629      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40630      */
40631     maxLengthText : "The maximum length for this field is {0}",
40632     /**
40633      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40634      */
40635     selectOnFocus : false,
40636     /**
40637      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40638      */    
40639     allowLeadingSpace : false,
40640     /**
40641      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40642      */
40643     blankText : "This field is required",
40644     /**
40645      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40646      * If available, this function will be called only after the basic validators all return true, and will be passed the
40647      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40648      */
40649     validator : null,
40650     /**
40651      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40652      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40653      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40654      */
40655     regex : null,
40656     /**
40657      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40658      */
40659     regexText : "",
40660     /**
40661      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40662      */
40663     emptyText : null,
40664    
40665
40666     // private
40667     initEvents : function()
40668     {
40669         if (this.emptyText) {
40670             this.el.attr('placeholder', this.emptyText);
40671         }
40672         
40673         Roo.form.TextField.superclass.initEvents.call(this);
40674         if(this.validationEvent == 'keyup'){
40675             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40676             this.el.on('keyup', this.filterValidation, this);
40677         }
40678         else if(this.validationEvent !== false){
40679             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40680         }
40681         
40682         if(this.selectOnFocus){
40683             this.on("focus", this.preFocus, this);
40684         }
40685         if (!this.allowLeadingSpace) {
40686             this.on('blur', this.cleanLeadingSpace, this);
40687         }
40688         
40689         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40690             this.el.on("keypress", this.filterKeys, this);
40691         }
40692         if(this.grow){
40693             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40694             this.el.on("click", this.autoSize,  this);
40695         }
40696         if(this.el.is('input[type=password]') && Roo.isSafari){
40697             this.el.on('keydown', this.SafariOnKeyDown, this);
40698         }
40699     },
40700
40701     processValue : function(value){
40702         if(this.stripCharsRe){
40703             var newValue = value.replace(this.stripCharsRe, '');
40704             if(newValue !== value){
40705                 this.setRawValue(newValue);
40706                 return newValue;
40707             }
40708         }
40709         return value;
40710     },
40711
40712     filterValidation : function(e){
40713         if(!e.isNavKeyPress()){
40714             this.validationTask.delay(this.validationDelay);
40715         }
40716     },
40717
40718     // private
40719     onKeyUp : function(e){
40720         if(!e.isNavKeyPress()){
40721             this.autoSize();
40722         }
40723     },
40724     // private - clean the leading white space
40725     cleanLeadingSpace : function(e)
40726     {
40727         if ( this.inputType == 'file') {
40728             return;
40729         }
40730         
40731         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40732     },
40733     /**
40734      * Resets the current field value to the originally-loaded value and clears any validation messages.
40735      *  
40736      */
40737     reset : function(){
40738         Roo.form.TextField.superclass.reset.call(this);
40739        
40740     }, 
40741     // private
40742     preFocus : function(){
40743         
40744         if(this.selectOnFocus){
40745             this.el.dom.select();
40746         }
40747     },
40748
40749     
40750     // private
40751     filterKeys : function(e){
40752         var k = e.getKey();
40753         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40754             return;
40755         }
40756         var c = e.getCharCode(), cc = String.fromCharCode(c);
40757         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40758             return;
40759         }
40760         if(!this.maskRe.test(cc)){
40761             e.stopEvent();
40762         }
40763     },
40764
40765     setValue : function(v){
40766         
40767         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40768         
40769         this.autoSize();
40770     },
40771
40772     /**
40773      * Validates a value according to the field's validation rules and marks the field as invalid
40774      * if the validation fails
40775      * @param {Mixed} value The value to validate
40776      * @return {Boolean} True if the value is valid, else false
40777      */
40778     validateValue : function(value){
40779         if(value.length < 1)  { // if it's blank
40780              if(this.allowBlank){
40781                 this.clearInvalid();
40782                 return true;
40783              }else{
40784                 this.markInvalid(this.blankText);
40785                 return false;
40786              }
40787         }
40788         if(value.length < this.minLength){
40789             this.markInvalid(String.format(this.minLengthText, this.minLength));
40790             return false;
40791         }
40792         if(value.length > this.maxLength){
40793             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40794             return false;
40795         }
40796         if(this.vtype){
40797             var vt = Roo.form.VTypes;
40798             if(!vt[this.vtype](value, this)){
40799                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40800                 return false;
40801             }
40802         }
40803         if(typeof this.validator == "function"){
40804             var msg = this.validator(value);
40805             if(msg !== true){
40806                 this.markInvalid(msg);
40807                 return false;
40808             }
40809         }
40810         if(this.regex && !this.regex.test(value)){
40811             this.markInvalid(this.regexText);
40812             return false;
40813         }
40814         return true;
40815     },
40816
40817     /**
40818      * Selects text in this field
40819      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40820      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40821      */
40822     selectText : function(start, end){
40823         var v = this.getRawValue();
40824         if(v.length > 0){
40825             start = start === undefined ? 0 : start;
40826             end = end === undefined ? v.length : end;
40827             var d = this.el.dom;
40828             if(d.setSelectionRange){
40829                 d.setSelectionRange(start, end);
40830             }else if(d.createTextRange){
40831                 var range = d.createTextRange();
40832                 range.moveStart("character", start);
40833                 range.moveEnd("character", v.length-end);
40834                 range.select();
40835             }
40836         }
40837     },
40838
40839     /**
40840      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40841      * This only takes effect if grow = true, and fires the autosize event.
40842      */
40843     autoSize : function(){
40844         if(!this.grow || !this.rendered){
40845             return;
40846         }
40847         if(!this.metrics){
40848             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40849         }
40850         var el = this.el;
40851         var v = el.dom.value;
40852         var d = document.createElement('div');
40853         d.appendChild(document.createTextNode(v));
40854         v = d.innerHTML;
40855         d = null;
40856         v += "&#160;";
40857         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40858         this.el.setWidth(w);
40859         this.fireEvent("autosize", this, w);
40860     },
40861     
40862     // private
40863     SafariOnKeyDown : function(event)
40864     {
40865         // this is a workaround for a password hang bug on chrome/ webkit.
40866         
40867         var isSelectAll = false;
40868         
40869         if(this.el.dom.selectionEnd > 0){
40870             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40871         }
40872         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40873             event.preventDefault();
40874             this.setValue('');
40875             return;
40876         }
40877         
40878         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40879             
40880             event.preventDefault();
40881             // this is very hacky as keydown always get's upper case.
40882             
40883             var cc = String.fromCharCode(event.getCharCode());
40884             
40885             
40886             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40887             
40888         }
40889         
40890         
40891     }
40892 });/*
40893  * Based on:
40894  * Ext JS Library 1.1.1
40895  * Copyright(c) 2006-2007, Ext JS, LLC.
40896  *
40897  * Originally Released Under LGPL - original licence link has changed is not relivant.
40898  *
40899  * Fork - LGPL
40900  * <script type="text/javascript">
40901  */
40902  
40903 /**
40904  * @class Roo.form.Hidden
40905  * @extends Roo.form.TextField
40906  * Simple Hidden element used on forms 
40907  * 
40908  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40909  * 
40910  * @constructor
40911  * Creates a new Hidden form element.
40912  * @param {Object} config Configuration options
40913  */
40914
40915
40916
40917 // easy hidden field...
40918 Roo.form.Hidden = function(config){
40919     Roo.form.Hidden.superclass.constructor.call(this, config);
40920 };
40921   
40922 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40923     fieldLabel:      '',
40924     inputType:      'hidden',
40925     width:          50,
40926     allowBlank:     true,
40927     labelSeparator: '',
40928     hidden:         true,
40929     itemCls :       'x-form-item-display-none'
40930
40931
40932 });
40933
40934
40935 /*
40936  * Based on:
40937  * Ext JS Library 1.1.1
40938  * Copyright(c) 2006-2007, Ext JS, LLC.
40939  *
40940  * Originally Released Under LGPL - original licence link has changed is not relivant.
40941  *
40942  * Fork - LGPL
40943  * <script type="text/javascript">
40944  */
40945  
40946 /**
40947  * @class Roo.form.TriggerField
40948  * @extends Roo.form.TextField
40949  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40950  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40951  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40952  * for which you can provide a custom implementation.  For example:
40953  * <pre><code>
40954 var trigger = new Roo.form.TriggerField();
40955 trigger.onTriggerClick = myTriggerFn;
40956 trigger.applyTo('my-field');
40957 </code></pre>
40958  *
40959  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40960  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40961  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40962  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40963  * @constructor
40964  * Create a new TriggerField.
40965  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40966  * to the base TextField)
40967  */
40968 Roo.form.TriggerField = function(config){
40969     this.mimicing = false;
40970     Roo.form.TriggerField.superclass.constructor.call(this, config);
40971 };
40972
40973 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40974     /**
40975      * @cfg {String} triggerClass A CSS class to apply to the trigger
40976      */
40977     /**
40978      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40979      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40980      */
40981     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40982     /**
40983      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40984      */
40985     hideTrigger:false,
40986
40987     /** @cfg {Boolean} grow @hide */
40988     /** @cfg {Number} growMin @hide */
40989     /** @cfg {Number} growMax @hide */
40990
40991     /**
40992      * @hide 
40993      * @method
40994      */
40995     autoSize: Roo.emptyFn,
40996     // private
40997     monitorTab : true,
40998     // private
40999     deferHeight : true,
41000
41001     
41002     actionMode : 'wrap',
41003     // private
41004     onResize : function(w, h){
41005         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41006         if(typeof w == 'number'){
41007             var x = w - this.trigger.getWidth();
41008             this.el.setWidth(this.adjustWidth('input', x));
41009             this.trigger.setStyle('left', x+'px');
41010         }
41011     },
41012
41013     // private
41014     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41015
41016     // private
41017     getResizeEl : function(){
41018         return this.wrap;
41019     },
41020
41021     // private
41022     getPositionEl : function(){
41023         return this.wrap;
41024     },
41025
41026     // private
41027     alignErrorIcon : function(){
41028         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41029     },
41030
41031     // private
41032     onRender : function(ct, position){
41033         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41034         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41035         this.trigger = this.wrap.createChild(this.triggerConfig ||
41036                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41037         if(this.hideTrigger){
41038             this.trigger.setDisplayed(false);
41039         }
41040         this.initTrigger();
41041         if(!this.width){
41042             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41043         }
41044     },
41045
41046     // private
41047     initTrigger : function(){
41048         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41049         this.trigger.addClassOnOver('x-form-trigger-over');
41050         this.trigger.addClassOnClick('x-form-trigger-click');
41051     },
41052
41053     // private
41054     onDestroy : function(){
41055         if(this.trigger){
41056             this.trigger.removeAllListeners();
41057             this.trigger.remove();
41058         }
41059         if(this.wrap){
41060             this.wrap.remove();
41061         }
41062         Roo.form.TriggerField.superclass.onDestroy.call(this);
41063     },
41064
41065     // private
41066     onFocus : function(){
41067         Roo.form.TriggerField.superclass.onFocus.call(this);
41068         if(!this.mimicing){
41069             this.wrap.addClass('x-trigger-wrap-focus');
41070             this.mimicing = true;
41071             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41072             if(this.monitorTab){
41073                 this.el.on("keydown", this.checkTab, this);
41074             }
41075         }
41076     },
41077
41078     // private
41079     checkTab : function(e){
41080         if(e.getKey() == e.TAB){
41081             this.triggerBlur();
41082         }
41083     },
41084
41085     // private
41086     onBlur : function(){
41087         // do nothing
41088     },
41089
41090     // private
41091     mimicBlur : function(e, t){
41092         if(!this.wrap.contains(t) && this.validateBlur()){
41093             this.triggerBlur();
41094         }
41095     },
41096
41097     // private
41098     triggerBlur : function(){
41099         this.mimicing = false;
41100         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41101         if(this.monitorTab){
41102             this.el.un("keydown", this.checkTab, this);
41103         }
41104         this.wrap.removeClass('x-trigger-wrap-focus');
41105         Roo.form.TriggerField.superclass.onBlur.call(this);
41106     },
41107
41108     // private
41109     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41110     validateBlur : function(e, t){
41111         return true;
41112     },
41113
41114     // private
41115     onDisable : function(){
41116         Roo.form.TriggerField.superclass.onDisable.call(this);
41117         if(this.wrap){
41118             this.wrap.addClass('x-item-disabled');
41119         }
41120     },
41121
41122     // private
41123     onEnable : function(){
41124         Roo.form.TriggerField.superclass.onEnable.call(this);
41125         if(this.wrap){
41126             this.wrap.removeClass('x-item-disabled');
41127         }
41128     },
41129
41130     // private
41131     onShow : function(){
41132         var ae = this.getActionEl();
41133         
41134         if(ae){
41135             ae.dom.style.display = '';
41136             ae.dom.style.visibility = 'visible';
41137         }
41138     },
41139
41140     // private
41141     
41142     onHide : function(){
41143         var ae = this.getActionEl();
41144         ae.dom.style.display = 'none';
41145     },
41146
41147     /**
41148      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41149      * by an implementing function.
41150      * @method
41151      * @param {EventObject} e
41152      */
41153     onTriggerClick : Roo.emptyFn
41154 });
41155
41156 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41157 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41158 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41159 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41160     initComponent : function(){
41161         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41162
41163         this.triggerConfig = {
41164             tag:'span', cls:'x-form-twin-triggers', cn:[
41165             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41166             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41167         ]};
41168     },
41169
41170     getTrigger : function(index){
41171         return this.triggers[index];
41172     },
41173
41174     initTrigger : function(){
41175         var ts = this.trigger.select('.x-form-trigger', true);
41176         this.wrap.setStyle('overflow', 'hidden');
41177         var triggerField = this;
41178         ts.each(function(t, all, index){
41179             t.hide = function(){
41180                 var w = triggerField.wrap.getWidth();
41181                 this.dom.style.display = 'none';
41182                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41183             };
41184             t.show = function(){
41185                 var w = triggerField.wrap.getWidth();
41186                 this.dom.style.display = '';
41187                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41188             };
41189             var triggerIndex = 'Trigger'+(index+1);
41190
41191             if(this['hide'+triggerIndex]){
41192                 t.dom.style.display = 'none';
41193             }
41194             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41195             t.addClassOnOver('x-form-trigger-over');
41196             t.addClassOnClick('x-form-trigger-click');
41197         }, this);
41198         this.triggers = ts.elements;
41199     },
41200
41201     onTrigger1Click : Roo.emptyFn,
41202     onTrigger2Click : Roo.emptyFn
41203 });/*
41204  * Based on:
41205  * Ext JS Library 1.1.1
41206  * Copyright(c) 2006-2007, Ext JS, LLC.
41207  *
41208  * Originally Released Under LGPL - original licence link has changed is not relivant.
41209  *
41210  * Fork - LGPL
41211  * <script type="text/javascript">
41212  */
41213  
41214 /**
41215  * @class Roo.form.TextArea
41216  * @extends Roo.form.TextField
41217  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41218  * support for auto-sizing.
41219  * @constructor
41220  * Creates a new TextArea
41221  * @param {Object} config Configuration options
41222  */
41223 Roo.form.TextArea = function(config){
41224     Roo.form.TextArea.superclass.constructor.call(this, config);
41225     // these are provided exchanges for backwards compat
41226     // minHeight/maxHeight were replaced by growMin/growMax to be
41227     // compatible with TextField growing config values
41228     if(this.minHeight !== undefined){
41229         this.growMin = this.minHeight;
41230     }
41231     if(this.maxHeight !== undefined){
41232         this.growMax = this.maxHeight;
41233     }
41234 };
41235
41236 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41237     /**
41238      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41239      */
41240     growMin : 60,
41241     /**
41242      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41243      */
41244     growMax: 1000,
41245     /**
41246      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41247      * in the field (equivalent to setting overflow: hidden, defaults to false)
41248      */
41249     preventScrollbars: false,
41250     /**
41251      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41252      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41253      */
41254
41255     // private
41256     onRender : function(ct, position){
41257         if(!this.el){
41258             this.defaultAutoCreate = {
41259                 tag: "textarea",
41260                 style:"width:300px;height:60px;",
41261                 autocomplete: "new-password"
41262             };
41263         }
41264         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41265         if(this.grow){
41266             this.textSizeEl = Roo.DomHelper.append(document.body, {
41267                 tag: "pre", cls: "x-form-grow-sizer"
41268             });
41269             if(this.preventScrollbars){
41270                 this.el.setStyle("overflow", "hidden");
41271             }
41272             this.el.setHeight(this.growMin);
41273         }
41274     },
41275
41276     onDestroy : function(){
41277         if(this.textSizeEl){
41278             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41279         }
41280         Roo.form.TextArea.superclass.onDestroy.call(this);
41281     },
41282
41283     // private
41284     onKeyUp : function(e){
41285         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41286             this.autoSize();
41287         }
41288     },
41289
41290     /**
41291      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41292      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41293      */
41294     autoSize : function(){
41295         if(!this.grow || !this.textSizeEl){
41296             return;
41297         }
41298         var el = this.el;
41299         var v = el.dom.value;
41300         var ts = this.textSizeEl;
41301
41302         ts.innerHTML = '';
41303         ts.appendChild(document.createTextNode(v));
41304         v = ts.innerHTML;
41305
41306         Roo.fly(ts).setWidth(this.el.getWidth());
41307         if(v.length < 1){
41308             v = "&#160;&#160;";
41309         }else{
41310             if(Roo.isIE){
41311                 v = v.replace(/\n/g, '<p>&#160;</p>');
41312             }
41313             v += "&#160;\n&#160;";
41314         }
41315         ts.innerHTML = v;
41316         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41317         if(h != this.lastHeight){
41318             this.lastHeight = h;
41319             this.el.setHeight(h);
41320             this.fireEvent("autosize", this, h);
41321         }
41322     }
41323 });/*
41324  * Based on:
41325  * Ext JS Library 1.1.1
41326  * Copyright(c) 2006-2007, Ext JS, LLC.
41327  *
41328  * Originally Released Under LGPL - original licence link has changed is not relivant.
41329  *
41330  * Fork - LGPL
41331  * <script type="text/javascript">
41332  */
41333  
41334
41335 /**
41336  * @class Roo.form.NumberField
41337  * @extends Roo.form.TextField
41338  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41339  * @constructor
41340  * Creates a new NumberField
41341  * @param {Object} config Configuration options
41342  */
41343 Roo.form.NumberField = function(config){
41344     Roo.form.NumberField.superclass.constructor.call(this, config);
41345 };
41346
41347 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41348     /**
41349      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41350      */
41351     fieldClass: "x-form-field x-form-num-field",
41352     /**
41353      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41354      */
41355     allowDecimals : true,
41356     /**
41357      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41358      */
41359     decimalSeparator : ".",
41360     /**
41361      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41362      */
41363     decimalPrecision : 2,
41364     /**
41365      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41366      */
41367     allowNegative : true,
41368     /**
41369      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41370      */
41371     minValue : Number.NEGATIVE_INFINITY,
41372     /**
41373      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41374      */
41375     maxValue : Number.MAX_VALUE,
41376     /**
41377      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41378      */
41379     minText : "The minimum value for this field is {0}",
41380     /**
41381      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41382      */
41383     maxText : "The maximum value for this field is {0}",
41384     /**
41385      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41386      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41387      */
41388     nanText : "{0} is not a valid number",
41389
41390     // private
41391     initEvents : function(){
41392         Roo.form.NumberField.superclass.initEvents.call(this);
41393         var allowed = "0123456789";
41394         if(this.allowDecimals){
41395             allowed += this.decimalSeparator;
41396         }
41397         if(this.allowNegative){
41398             allowed += "-";
41399         }
41400         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41401         var keyPress = function(e){
41402             var k = e.getKey();
41403             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41404                 return;
41405             }
41406             var c = e.getCharCode();
41407             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41408                 e.stopEvent();
41409             }
41410         };
41411         this.el.on("keypress", keyPress, this);
41412     },
41413
41414     // private
41415     validateValue : function(value){
41416         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41417             return false;
41418         }
41419         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41420              return true;
41421         }
41422         var num = this.parseValue(value);
41423         if(isNaN(num)){
41424             this.markInvalid(String.format(this.nanText, value));
41425             return false;
41426         }
41427         if(num < this.minValue){
41428             this.markInvalid(String.format(this.minText, this.minValue));
41429             return false;
41430         }
41431         if(num > this.maxValue){
41432             this.markInvalid(String.format(this.maxText, this.maxValue));
41433             return false;
41434         }
41435         return true;
41436     },
41437
41438     getValue : function(){
41439         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41440     },
41441
41442     // private
41443     parseValue : function(value){
41444         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41445         return isNaN(value) ? '' : value;
41446     },
41447
41448     // private
41449     fixPrecision : function(value){
41450         var nan = isNaN(value);
41451         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41452             return nan ? '' : value;
41453         }
41454         return parseFloat(value).toFixed(this.decimalPrecision);
41455     },
41456
41457     setValue : function(v){
41458         v = this.fixPrecision(v);
41459         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41460     },
41461
41462     // private
41463     decimalPrecisionFcn : function(v){
41464         return Math.floor(v);
41465     },
41466
41467     beforeBlur : function(){
41468         var v = this.parseValue(this.getRawValue());
41469         if(v){
41470             this.setValue(v);
41471         }
41472     }
41473 });/*
41474  * Based on:
41475  * Ext JS Library 1.1.1
41476  * Copyright(c) 2006-2007, Ext JS, LLC.
41477  *
41478  * Originally Released Under LGPL - original licence link has changed is not relivant.
41479  *
41480  * Fork - LGPL
41481  * <script type="text/javascript">
41482  */
41483  
41484 /**
41485  * @class Roo.form.DateField
41486  * @extends Roo.form.TriggerField
41487  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41488 * @constructor
41489 * Create a new DateField
41490 * @param {Object} config
41491  */
41492 Roo.form.DateField = function(config)
41493 {
41494     Roo.form.DateField.superclass.constructor.call(this, config);
41495     
41496       this.addEvents({
41497          
41498         /**
41499          * @event select
41500          * Fires when a date is selected
41501              * @param {Roo.form.DateField} combo This combo box
41502              * @param {Date} date The date selected
41503              */
41504         'select' : true
41505          
41506     });
41507     
41508     
41509     if(typeof this.minValue == "string") {
41510         this.minValue = this.parseDate(this.minValue);
41511     }
41512     if(typeof this.maxValue == "string") {
41513         this.maxValue = this.parseDate(this.maxValue);
41514     }
41515     this.ddMatch = null;
41516     if(this.disabledDates){
41517         var dd = this.disabledDates;
41518         var re = "(?:";
41519         for(var i = 0; i < dd.length; i++){
41520             re += dd[i];
41521             if(i != dd.length-1) {
41522                 re += "|";
41523             }
41524         }
41525         this.ddMatch = new RegExp(re + ")");
41526     }
41527 };
41528
41529 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41530     /**
41531      * @cfg {String} format
41532      * The default date format string which can be overriden for localization support.  The format must be
41533      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41534      */
41535     format : "m/d/y",
41536     /**
41537      * @cfg {String} altFormats
41538      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41539      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41540      */
41541     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41542     /**
41543      * @cfg {Array} disabledDays
41544      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41545      */
41546     disabledDays : null,
41547     /**
41548      * @cfg {String} disabledDaysText
41549      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41550      */
41551     disabledDaysText : "Disabled",
41552     /**
41553      * @cfg {Array} disabledDates
41554      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41555      * expression so they are very powerful. Some examples:
41556      * <ul>
41557      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41558      * <li>["03/08", "09/16"] would disable those days for every year</li>
41559      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41560      * <li>["03/../2006"] would disable every day in March 2006</li>
41561      * <li>["^03"] would disable every day in every March</li>
41562      * </ul>
41563      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41564      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41565      */
41566     disabledDates : null,
41567     /**
41568      * @cfg {String} disabledDatesText
41569      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41570      */
41571     disabledDatesText : "Disabled",
41572     /**
41573      * @cfg {Date/String} minValue
41574      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41575      * valid format (defaults to null).
41576      */
41577     minValue : null,
41578     /**
41579      * @cfg {Date/String} maxValue
41580      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41581      * valid format (defaults to null).
41582      */
41583     maxValue : null,
41584     /**
41585      * @cfg {String} minText
41586      * The error text to display when the date in the cell is before minValue (defaults to
41587      * 'The date in this field must be after {minValue}').
41588      */
41589     minText : "The date in this field must be equal to or after {0}",
41590     /**
41591      * @cfg {String} maxText
41592      * The error text to display when the date in the cell is after maxValue (defaults to
41593      * 'The date in this field must be before {maxValue}').
41594      */
41595     maxText : "The date in this field must be equal to or before {0}",
41596     /**
41597      * @cfg {String} invalidText
41598      * The error text to display when the date in the field is invalid (defaults to
41599      * '{value} is not a valid date - it must be in the format {format}').
41600      */
41601     invalidText : "{0} is not a valid date - it must be in the format {1}",
41602     /**
41603      * @cfg {String} triggerClass
41604      * An additional CSS class used to style the trigger button.  The trigger will always get the
41605      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41606      * which displays a calendar icon).
41607      */
41608     triggerClass : 'x-form-date-trigger',
41609     
41610
41611     /**
41612      * @cfg {Boolean} useIso
41613      * if enabled, then the date field will use a hidden field to store the 
41614      * real value as iso formated date. default (false)
41615      */ 
41616     useIso : false,
41617     /**
41618      * @cfg {String/Object} autoCreate
41619      * A DomHelper element spec, or true for a default element spec (defaults to
41620      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41621      */ 
41622     // private
41623     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41624     
41625     // private
41626     hiddenField: false,
41627     
41628     onRender : function(ct, position)
41629     {
41630         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41631         if (this.useIso) {
41632             //this.el.dom.removeAttribute('name'); 
41633             Roo.log("Changing name?");
41634             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41635             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41636                     'before', true);
41637             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41638             // prevent input submission
41639             this.hiddenName = this.name;
41640         }
41641             
41642             
41643     },
41644     
41645     // private
41646     validateValue : function(value)
41647     {
41648         value = this.formatDate(value);
41649         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41650             Roo.log('super failed');
41651             return false;
41652         }
41653         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41654              return true;
41655         }
41656         var svalue = value;
41657         value = this.parseDate(value);
41658         if(!value){
41659             Roo.log('parse date failed' + svalue);
41660             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41661             return false;
41662         }
41663         var time = value.getTime();
41664         if(this.minValue && time < this.minValue.getTime()){
41665             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41666             return false;
41667         }
41668         if(this.maxValue && time > this.maxValue.getTime()){
41669             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41670             return false;
41671         }
41672         if(this.disabledDays){
41673             var day = value.getDay();
41674             for(var i = 0; i < this.disabledDays.length; i++) {
41675                 if(day === this.disabledDays[i]){
41676                     this.markInvalid(this.disabledDaysText);
41677                     return false;
41678                 }
41679             }
41680         }
41681         var fvalue = this.formatDate(value);
41682         if(this.ddMatch && this.ddMatch.test(fvalue)){
41683             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41684             return false;
41685         }
41686         return true;
41687     },
41688
41689     // private
41690     // Provides logic to override the default TriggerField.validateBlur which just returns true
41691     validateBlur : function(){
41692         return !this.menu || !this.menu.isVisible();
41693     },
41694     
41695     getName: function()
41696     {
41697         // returns hidden if it's set..
41698         if (!this.rendered) {return ''};
41699         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41700         
41701     },
41702
41703     /**
41704      * Returns the current date value of the date field.
41705      * @return {Date} The date value
41706      */
41707     getValue : function(){
41708         
41709         return  this.hiddenField ?
41710                 this.hiddenField.value :
41711                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41712     },
41713
41714     /**
41715      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41716      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41717      * (the default format used is "m/d/y").
41718      * <br />Usage:
41719      * <pre><code>
41720 //All of these calls set the same date value (May 4, 2006)
41721
41722 //Pass a date object:
41723 var dt = new Date('5/4/06');
41724 dateField.setValue(dt);
41725
41726 //Pass a date string (default format):
41727 dateField.setValue('5/4/06');
41728
41729 //Pass a date string (custom format):
41730 dateField.format = 'Y-m-d';
41731 dateField.setValue('2006-5-4');
41732 </code></pre>
41733      * @param {String/Date} date The date or valid date string
41734      */
41735     setValue : function(date){
41736         if (this.hiddenField) {
41737             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41738         }
41739         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41740         // make sure the value field is always stored as a date..
41741         this.value = this.parseDate(date);
41742         
41743         
41744     },
41745
41746     // private
41747     parseDate : function(value){
41748         if(!value || value instanceof Date){
41749             return value;
41750         }
41751         var v = Date.parseDate(value, this.format);
41752          if (!v && this.useIso) {
41753             v = Date.parseDate(value, 'Y-m-d');
41754         }
41755         if(!v && this.altFormats){
41756             if(!this.altFormatsArray){
41757                 this.altFormatsArray = this.altFormats.split("|");
41758             }
41759             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41760                 v = Date.parseDate(value, this.altFormatsArray[i]);
41761             }
41762         }
41763         return v;
41764     },
41765
41766     // private
41767     formatDate : function(date, fmt){
41768         return (!date || !(date instanceof Date)) ?
41769                date : date.dateFormat(fmt || this.format);
41770     },
41771
41772     // private
41773     menuListeners : {
41774         select: function(m, d){
41775             
41776             this.setValue(d);
41777             this.fireEvent('select', this, d);
41778         },
41779         show : function(){ // retain focus styling
41780             this.onFocus();
41781         },
41782         hide : function(){
41783             this.focus.defer(10, this);
41784             var ml = this.menuListeners;
41785             this.menu.un("select", ml.select,  this);
41786             this.menu.un("show", ml.show,  this);
41787             this.menu.un("hide", ml.hide,  this);
41788         }
41789     },
41790
41791     // private
41792     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41793     onTriggerClick : function(){
41794         if(this.disabled){
41795             return;
41796         }
41797         if(this.menu == null){
41798             this.menu = new Roo.menu.DateMenu();
41799         }
41800         Roo.apply(this.menu.picker,  {
41801             showClear: this.allowBlank,
41802             minDate : this.minValue,
41803             maxDate : this.maxValue,
41804             disabledDatesRE : this.ddMatch,
41805             disabledDatesText : this.disabledDatesText,
41806             disabledDays : this.disabledDays,
41807             disabledDaysText : this.disabledDaysText,
41808             format : this.useIso ? 'Y-m-d' : this.format,
41809             minText : String.format(this.minText, this.formatDate(this.minValue)),
41810             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41811         });
41812         this.menu.on(Roo.apply({}, this.menuListeners, {
41813             scope:this
41814         }));
41815         this.menu.picker.setValue(this.getValue() || new Date());
41816         this.menu.show(this.el, "tl-bl?");
41817     },
41818
41819     beforeBlur : function(){
41820         var v = this.parseDate(this.getRawValue());
41821         if(v){
41822             this.setValue(v);
41823         }
41824     },
41825
41826     /*@
41827      * overide
41828      * 
41829      */
41830     isDirty : function() {
41831         if(this.disabled) {
41832             return false;
41833         }
41834         
41835         if(typeof(this.startValue) === 'undefined'){
41836             return false;
41837         }
41838         
41839         return String(this.getValue()) !== String(this.startValue);
41840         
41841     },
41842     // @overide
41843     cleanLeadingSpace : function(e)
41844     {
41845        return;
41846     }
41847     
41848 });/*
41849  * Based on:
41850  * Ext JS Library 1.1.1
41851  * Copyright(c) 2006-2007, Ext JS, LLC.
41852  *
41853  * Originally Released Under LGPL - original licence link has changed is not relivant.
41854  *
41855  * Fork - LGPL
41856  * <script type="text/javascript">
41857  */
41858  
41859 /**
41860  * @class Roo.form.MonthField
41861  * @extends Roo.form.TriggerField
41862  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41863 * @constructor
41864 * Create a new MonthField
41865 * @param {Object} config
41866  */
41867 Roo.form.MonthField = function(config){
41868     
41869     Roo.form.MonthField.superclass.constructor.call(this, config);
41870     
41871       this.addEvents({
41872          
41873         /**
41874          * @event select
41875          * Fires when a date is selected
41876              * @param {Roo.form.MonthFieeld} combo This combo box
41877              * @param {Date} date The date selected
41878              */
41879         'select' : true
41880          
41881     });
41882     
41883     
41884     if(typeof this.minValue == "string") {
41885         this.minValue = this.parseDate(this.minValue);
41886     }
41887     if(typeof this.maxValue == "string") {
41888         this.maxValue = this.parseDate(this.maxValue);
41889     }
41890     this.ddMatch = null;
41891     if(this.disabledDates){
41892         var dd = this.disabledDates;
41893         var re = "(?:";
41894         for(var i = 0; i < dd.length; i++){
41895             re += dd[i];
41896             if(i != dd.length-1) {
41897                 re += "|";
41898             }
41899         }
41900         this.ddMatch = new RegExp(re + ")");
41901     }
41902 };
41903
41904 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41905     /**
41906      * @cfg {String} format
41907      * The default date format string which can be overriden for localization support.  The format must be
41908      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41909      */
41910     format : "M Y",
41911     /**
41912      * @cfg {String} altFormats
41913      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41914      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41915      */
41916     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41917     /**
41918      * @cfg {Array} disabledDays
41919      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41920      */
41921     disabledDays : [0,1,2,3,4,5,6],
41922     /**
41923      * @cfg {String} disabledDaysText
41924      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41925      */
41926     disabledDaysText : "Disabled",
41927     /**
41928      * @cfg {Array} disabledDates
41929      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41930      * expression so they are very powerful. Some examples:
41931      * <ul>
41932      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41933      * <li>["03/08", "09/16"] would disable those days for every year</li>
41934      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41935      * <li>["03/../2006"] would disable every day in March 2006</li>
41936      * <li>["^03"] would disable every day in every March</li>
41937      * </ul>
41938      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41939      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41940      */
41941     disabledDates : null,
41942     /**
41943      * @cfg {String} disabledDatesText
41944      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41945      */
41946     disabledDatesText : "Disabled",
41947     /**
41948      * @cfg {Date/String} minValue
41949      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41950      * valid format (defaults to null).
41951      */
41952     minValue : null,
41953     /**
41954      * @cfg {Date/String} maxValue
41955      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41956      * valid format (defaults to null).
41957      */
41958     maxValue : null,
41959     /**
41960      * @cfg {String} minText
41961      * The error text to display when the date in the cell is before minValue (defaults to
41962      * 'The date in this field must be after {minValue}').
41963      */
41964     minText : "The date in this field must be equal to or after {0}",
41965     /**
41966      * @cfg {String} maxTextf
41967      * The error text to display when the date in the cell is after maxValue (defaults to
41968      * 'The date in this field must be before {maxValue}').
41969      */
41970     maxText : "The date in this field must be equal to or before {0}",
41971     /**
41972      * @cfg {String} invalidText
41973      * The error text to display when the date in the field is invalid (defaults to
41974      * '{value} is not a valid date - it must be in the format {format}').
41975      */
41976     invalidText : "{0} is not a valid date - it must be in the format {1}",
41977     /**
41978      * @cfg {String} triggerClass
41979      * An additional CSS class used to style the trigger button.  The trigger will always get the
41980      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41981      * which displays a calendar icon).
41982      */
41983     triggerClass : 'x-form-date-trigger',
41984     
41985
41986     /**
41987      * @cfg {Boolean} useIso
41988      * if enabled, then the date field will use a hidden field to store the 
41989      * real value as iso formated date. default (true)
41990      */ 
41991     useIso : true,
41992     /**
41993      * @cfg {String/Object} autoCreate
41994      * A DomHelper element spec, or true for a default element spec (defaults to
41995      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41996      */ 
41997     // private
41998     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41999     
42000     // private
42001     hiddenField: false,
42002     
42003     hideMonthPicker : false,
42004     
42005     onRender : function(ct, position)
42006     {
42007         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42008         if (this.useIso) {
42009             this.el.dom.removeAttribute('name'); 
42010             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42011                     'before', true);
42012             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42013             // prevent input submission
42014             this.hiddenName = this.name;
42015         }
42016             
42017             
42018     },
42019     
42020     // private
42021     validateValue : function(value)
42022     {
42023         value = this.formatDate(value);
42024         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42025             return false;
42026         }
42027         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42028              return true;
42029         }
42030         var svalue = value;
42031         value = this.parseDate(value);
42032         if(!value){
42033             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42034             return false;
42035         }
42036         var time = value.getTime();
42037         if(this.minValue && time < this.minValue.getTime()){
42038             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42039             return false;
42040         }
42041         if(this.maxValue && time > this.maxValue.getTime()){
42042             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42043             return false;
42044         }
42045         /*if(this.disabledDays){
42046             var day = value.getDay();
42047             for(var i = 0; i < this.disabledDays.length; i++) {
42048                 if(day === this.disabledDays[i]){
42049                     this.markInvalid(this.disabledDaysText);
42050                     return false;
42051                 }
42052             }
42053         }
42054         */
42055         var fvalue = this.formatDate(value);
42056         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42057             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42058             return false;
42059         }
42060         */
42061         return true;
42062     },
42063
42064     // private
42065     // Provides logic to override the default TriggerField.validateBlur which just returns true
42066     validateBlur : function(){
42067         return !this.menu || !this.menu.isVisible();
42068     },
42069
42070     /**
42071      * Returns the current date value of the date field.
42072      * @return {Date} The date value
42073      */
42074     getValue : function(){
42075         
42076         
42077         
42078         return  this.hiddenField ?
42079                 this.hiddenField.value :
42080                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42081     },
42082
42083     /**
42084      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42085      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42086      * (the default format used is "m/d/y").
42087      * <br />Usage:
42088      * <pre><code>
42089 //All of these calls set the same date value (May 4, 2006)
42090
42091 //Pass a date object:
42092 var dt = new Date('5/4/06');
42093 monthField.setValue(dt);
42094
42095 //Pass a date string (default format):
42096 monthField.setValue('5/4/06');
42097
42098 //Pass a date string (custom format):
42099 monthField.format = 'Y-m-d';
42100 monthField.setValue('2006-5-4');
42101 </code></pre>
42102      * @param {String/Date} date The date or valid date string
42103      */
42104     setValue : function(date){
42105         Roo.log('month setValue' + date);
42106         // can only be first of month..
42107         
42108         var val = this.parseDate(date);
42109         
42110         if (this.hiddenField) {
42111             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42112         }
42113         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42114         this.value = this.parseDate(date);
42115     },
42116
42117     // private
42118     parseDate : function(value){
42119         if(!value || value instanceof Date){
42120             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42121             return value;
42122         }
42123         var v = Date.parseDate(value, this.format);
42124         if (!v && this.useIso) {
42125             v = Date.parseDate(value, 'Y-m-d');
42126         }
42127         if (v) {
42128             // 
42129             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42130         }
42131         
42132         
42133         if(!v && this.altFormats){
42134             if(!this.altFormatsArray){
42135                 this.altFormatsArray = this.altFormats.split("|");
42136             }
42137             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42138                 v = Date.parseDate(value, this.altFormatsArray[i]);
42139             }
42140         }
42141         return v;
42142     },
42143
42144     // private
42145     formatDate : function(date, fmt){
42146         return (!date || !(date instanceof Date)) ?
42147                date : date.dateFormat(fmt || this.format);
42148     },
42149
42150     // private
42151     menuListeners : {
42152         select: function(m, d){
42153             this.setValue(d);
42154             this.fireEvent('select', this, d);
42155         },
42156         show : function(){ // retain focus styling
42157             this.onFocus();
42158         },
42159         hide : function(){
42160             this.focus.defer(10, this);
42161             var ml = this.menuListeners;
42162             this.menu.un("select", ml.select,  this);
42163             this.menu.un("show", ml.show,  this);
42164             this.menu.un("hide", ml.hide,  this);
42165         }
42166     },
42167     // private
42168     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42169     onTriggerClick : function(){
42170         if(this.disabled){
42171             return;
42172         }
42173         if(this.menu == null){
42174             this.menu = new Roo.menu.DateMenu();
42175            
42176         }
42177         
42178         Roo.apply(this.menu.picker,  {
42179             
42180             showClear: this.allowBlank,
42181             minDate : this.minValue,
42182             maxDate : this.maxValue,
42183             disabledDatesRE : this.ddMatch,
42184             disabledDatesText : this.disabledDatesText,
42185             
42186             format : this.useIso ? 'Y-m-d' : this.format,
42187             minText : String.format(this.minText, this.formatDate(this.minValue)),
42188             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42189             
42190         });
42191          this.menu.on(Roo.apply({}, this.menuListeners, {
42192             scope:this
42193         }));
42194        
42195         
42196         var m = this.menu;
42197         var p = m.picker;
42198         
42199         // hide month picker get's called when we called by 'before hide';
42200         
42201         var ignorehide = true;
42202         p.hideMonthPicker  = function(disableAnim){
42203             if (ignorehide) {
42204                 return;
42205             }
42206              if(this.monthPicker){
42207                 Roo.log("hideMonthPicker called");
42208                 if(disableAnim === true){
42209                     this.monthPicker.hide();
42210                 }else{
42211                     this.monthPicker.slideOut('t', {duration:.2});
42212                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42213                     p.fireEvent("select", this, this.value);
42214                     m.hide();
42215                 }
42216             }
42217         }
42218         
42219         Roo.log('picker set value');
42220         Roo.log(this.getValue());
42221         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42222         m.show(this.el, 'tl-bl?');
42223         ignorehide  = false;
42224         // this will trigger hideMonthPicker..
42225         
42226         
42227         // hidden the day picker
42228         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42229         
42230         
42231         
42232       
42233         
42234         p.showMonthPicker.defer(100, p);
42235     
42236         
42237        
42238     },
42239
42240     beforeBlur : function(){
42241         var v = this.parseDate(this.getRawValue());
42242         if(v){
42243             this.setValue(v);
42244         }
42245     }
42246
42247     /** @cfg {Boolean} grow @hide */
42248     /** @cfg {Number} growMin @hide */
42249     /** @cfg {Number} growMax @hide */
42250     /**
42251      * @hide
42252      * @method autoSize
42253      */
42254 });/*
42255  * Based on:
42256  * Ext JS Library 1.1.1
42257  * Copyright(c) 2006-2007, Ext JS, LLC.
42258  *
42259  * Originally Released Under LGPL - original licence link has changed is not relivant.
42260  *
42261  * Fork - LGPL
42262  * <script type="text/javascript">
42263  */
42264  
42265
42266 /**
42267  * @class Roo.form.ComboBox
42268  * @extends Roo.form.TriggerField
42269  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42270  * @constructor
42271  * Create a new ComboBox.
42272  * @param {Object} config Configuration options
42273  */
42274 Roo.form.ComboBox = function(config){
42275     Roo.form.ComboBox.superclass.constructor.call(this, config);
42276     this.addEvents({
42277         /**
42278          * @event expand
42279          * Fires when the dropdown list is expanded
42280              * @param {Roo.form.ComboBox} combo This combo box
42281              */
42282         'expand' : true,
42283         /**
42284          * @event collapse
42285          * Fires when the dropdown list is collapsed
42286              * @param {Roo.form.ComboBox} combo This combo box
42287              */
42288         'collapse' : true,
42289         /**
42290          * @event beforeselect
42291          * Fires before a list item is selected. Return false to cancel the selection.
42292              * @param {Roo.form.ComboBox} combo This combo box
42293              * @param {Roo.data.Record} record The data record returned from the underlying store
42294              * @param {Number} index The index of the selected item in the dropdown list
42295              */
42296         'beforeselect' : true,
42297         /**
42298          * @event select
42299          * Fires when a list item is selected
42300              * @param {Roo.form.ComboBox} combo This combo box
42301              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42302              * @param {Number} index The index of the selected item in the dropdown list
42303              */
42304         'select' : true,
42305         /**
42306          * @event beforequery
42307          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42308          * The event object passed has these properties:
42309              * @param {Roo.form.ComboBox} combo This combo box
42310              * @param {String} query The query
42311              * @param {Boolean} forceAll true to force "all" query
42312              * @param {Boolean} cancel true to cancel the query
42313              * @param {Object} e The query event object
42314              */
42315         'beforequery': true,
42316          /**
42317          * @event add
42318          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42319              * @param {Roo.form.ComboBox} combo This combo box
42320              */
42321         'add' : true,
42322         /**
42323          * @event edit
42324          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42325              * @param {Roo.form.ComboBox} combo This combo box
42326              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42327              */
42328         'edit' : true
42329         
42330         
42331     });
42332     if(this.transform){
42333         this.allowDomMove = false;
42334         var s = Roo.getDom(this.transform);
42335         if(!this.hiddenName){
42336             this.hiddenName = s.name;
42337         }
42338         if(!this.store){
42339             this.mode = 'local';
42340             var d = [], opts = s.options;
42341             for(var i = 0, len = opts.length;i < len; i++){
42342                 var o = opts[i];
42343                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42344                 if(o.selected) {
42345                     this.value = value;
42346                 }
42347                 d.push([value, o.text]);
42348             }
42349             this.store = new Roo.data.SimpleStore({
42350                 'id': 0,
42351                 fields: ['value', 'text'],
42352                 data : d
42353             });
42354             this.valueField = 'value';
42355             this.displayField = 'text';
42356         }
42357         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42358         if(!this.lazyRender){
42359             this.target = true;
42360             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42361             s.parentNode.removeChild(s); // remove it
42362             this.render(this.el.parentNode);
42363         }else{
42364             s.parentNode.removeChild(s); // remove it
42365         }
42366
42367     }
42368     if (this.store) {
42369         this.store = Roo.factory(this.store, Roo.data);
42370     }
42371     
42372     this.selectedIndex = -1;
42373     if(this.mode == 'local'){
42374         if(config.queryDelay === undefined){
42375             this.queryDelay = 10;
42376         }
42377         if(config.minChars === undefined){
42378             this.minChars = 0;
42379         }
42380     }
42381 };
42382
42383 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42384     /**
42385      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42386      */
42387     /**
42388      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42389      * rendering into an Roo.Editor, defaults to false)
42390      */
42391     /**
42392      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42393      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42394      */
42395     /**
42396      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42397      */
42398     /**
42399      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42400      * the dropdown list (defaults to undefined, with no header element)
42401      */
42402
42403      /**
42404      * @cfg {String/Roo.Template} tpl The template to use to render the output
42405      */
42406      
42407     // private
42408     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42409     /**
42410      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42411      */
42412     listWidth: undefined,
42413     /**
42414      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42415      * mode = 'remote' or 'text' if mode = 'local')
42416      */
42417     displayField: undefined,
42418     /**
42419      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42420      * mode = 'remote' or 'value' if mode = 'local'). 
42421      * Note: use of a valueField requires the user make a selection
42422      * in order for a value to be mapped.
42423      */
42424     valueField: undefined,
42425     
42426     
42427     /**
42428      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42429      * field's data value (defaults to the underlying DOM element's name)
42430      */
42431     hiddenName: undefined,
42432     /**
42433      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42434      */
42435     listClass: '',
42436     /**
42437      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42438      */
42439     selectedClass: 'x-combo-selected',
42440     /**
42441      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42442      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42443      * which displays a downward arrow icon).
42444      */
42445     triggerClass : 'x-form-arrow-trigger',
42446     /**
42447      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42448      */
42449     shadow:'sides',
42450     /**
42451      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42452      * anchor positions (defaults to 'tl-bl')
42453      */
42454     listAlign: 'tl-bl?',
42455     /**
42456      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42457      */
42458     maxHeight: 300,
42459     /**
42460      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42461      * query specified by the allQuery config option (defaults to 'query')
42462      */
42463     triggerAction: 'query',
42464     /**
42465      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42466      * (defaults to 4, does not apply if editable = false)
42467      */
42468     minChars : 4,
42469     /**
42470      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42471      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42472      */
42473     typeAhead: false,
42474     /**
42475      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42476      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42477      */
42478     queryDelay: 500,
42479     /**
42480      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42481      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42482      */
42483     pageSize: 0,
42484     /**
42485      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42486      * when editable = true (defaults to false)
42487      */
42488     selectOnFocus:false,
42489     /**
42490      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42491      */
42492     queryParam: 'query',
42493     /**
42494      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42495      * when mode = 'remote' (defaults to 'Loading...')
42496      */
42497     loadingText: 'Loading...',
42498     /**
42499      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42500      */
42501     resizable: false,
42502     /**
42503      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42504      */
42505     handleHeight : 8,
42506     /**
42507      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42508      * traditional select (defaults to true)
42509      */
42510     editable: true,
42511     /**
42512      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42513      */
42514     allQuery: '',
42515     /**
42516      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42517      */
42518     mode: 'remote',
42519     /**
42520      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42521      * listWidth has a higher value)
42522      */
42523     minListWidth : 70,
42524     /**
42525      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42526      * allow the user to set arbitrary text into the field (defaults to false)
42527      */
42528     forceSelection:false,
42529     /**
42530      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42531      * if typeAhead = true (defaults to 250)
42532      */
42533     typeAheadDelay : 250,
42534     /**
42535      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42536      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42537      */
42538     valueNotFoundText : undefined,
42539     /**
42540      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42541      */
42542     blockFocus : false,
42543     
42544     /**
42545      * @cfg {Boolean} disableClear Disable showing of clear button.
42546      */
42547     disableClear : false,
42548     /**
42549      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42550      */
42551     alwaysQuery : false,
42552     
42553     //private
42554     addicon : false,
42555     editicon: false,
42556     
42557     // element that contains real text value.. (when hidden is used..)
42558      
42559     // private
42560     onRender : function(ct, position)
42561     {
42562         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42563         
42564         if(this.hiddenName){
42565             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42566                     'before', true);
42567             this.hiddenField.value =
42568                 this.hiddenValue !== undefined ? this.hiddenValue :
42569                 this.value !== undefined ? this.value : '';
42570
42571             // prevent input submission
42572             this.el.dom.removeAttribute('name');
42573              
42574              
42575         }
42576         
42577         if(Roo.isGecko){
42578             this.el.dom.setAttribute('autocomplete', 'off');
42579         }
42580
42581         var cls = 'x-combo-list';
42582
42583         this.list = new Roo.Layer({
42584             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42585         });
42586
42587         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42588         this.list.setWidth(lw);
42589         this.list.swallowEvent('mousewheel');
42590         this.assetHeight = 0;
42591
42592         if(this.title){
42593             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42594             this.assetHeight += this.header.getHeight();
42595         }
42596
42597         this.innerList = this.list.createChild({cls:cls+'-inner'});
42598         this.innerList.on('mouseover', this.onViewOver, this);
42599         this.innerList.on('mousemove', this.onViewMove, this);
42600         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42601         
42602         if(this.allowBlank && !this.pageSize && !this.disableClear){
42603             this.footer = this.list.createChild({cls:cls+'-ft'});
42604             this.pageTb = new Roo.Toolbar(this.footer);
42605            
42606         }
42607         if(this.pageSize){
42608             this.footer = this.list.createChild({cls:cls+'-ft'});
42609             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42610                     {pageSize: this.pageSize});
42611             
42612         }
42613         
42614         if (this.pageTb && this.allowBlank && !this.disableClear) {
42615             var _this = this;
42616             this.pageTb.add(new Roo.Toolbar.Fill(), {
42617                 cls: 'x-btn-icon x-btn-clear',
42618                 text: '&#160;',
42619                 handler: function()
42620                 {
42621                     _this.collapse();
42622                     _this.clearValue();
42623                     _this.onSelect(false, -1);
42624                 }
42625             });
42626         }
42627         if (this.footer) {
42628             this.assetHeight += this.footer.getHeight();
42629         }
42630         
42631
42632         if(!this.tpl){
42633             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42634         }
42635
42636         this.view = new Roo.View(this.innerList, this.tpl, {
42637             singleSelect:true,
42638             store: this.store,
42639             selectedClass: this.selectedClass
42640         });
42641
42642         this.view.on('click', this.onViewClick, this);
42643
42644         this.store.on('beforeload', this.onBeforeLoad, this);
42645         this.store.on('load', this.onLoad, this);
42646         this.store.on('loadexception', this.onLoadException, this);
42647
42648         if(this.resizable){
42649             this.resizer = new Roo.Resizable(this.list,  {
42650                pinned:true, handles:'se'
42651             });
42652             this.resizer.on('resize', function(r, w, h){
42653                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42654                 this.listWidth = w;
42655                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42656                 this.restrictHeight();
42657             }, this);
42658             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42659         }
42660         if(!this.editable){
42661             this.editable = true;
42662             this.setEditable(false);
42663         }  
42664         
42665         
42666         if (typeof(this.events.add.listeners) != 'undefined') {
42667             
42668             this.addicon = this.wrap.createChild(
42669                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42670        
42671             this.addicon.on('click', function(e) {
42672                 this.fireEvent('add', this);
42673             }, this);
42674         }
42675         if (typeof(this.events.edit.listeners) != 'undefined') {
42676             
42677             this.editicon = this.wrap.createChild(
42678                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42679             if (this.addicon) {
42680                 this.editicon.setStyle('margin-left', '40px');
42681             }
42682             this.editicon.on('click', function(e) {
42683                 
42684                 // we fire even  if inothing is selected..
42685                 this.fireEvent('edit', this, this.lastData );
42686                 
42687             }, this);
42688         }
42689         
42690         
42691         
42692     },
42693
42694     // private
42695     initEvents : function(){
42696         Roo.form.ComboBox.superclass.initEvents.call(this);
42697
42698         this.keyNav = new Roo.KeyNav(this.el, {
42699             "up" : function(e){
42700                 this.inKeyMode = true;
42701                 this.selectPrev();
42702             },
42703
42704             "down" : function(e){
42705                 if(!this.isExpanded()){
42706                     this.onTriggerClick();
42707                 }else{
42708                     this.inKeyMode = true;
42709                     this.selectNext();
42710                 }
42711             },
42712
42713             "enter" : function(e){
42714                 this.onViewClick();
42715                 //return true;
42716             },
42717
42718             "esc" : function(e){
42719                 this.collapse();
42720             },
42721
42722             "tab" : function(e){
42723                 this.onViewClick(false);
42724                 this.fireEvent("specialkey", this, e);
42725                 return true;
42726             },
42727
42728             scope : this,
42729
42730             doRelay : function(foo, bar, hname){
42731                 if(hname == 'down' || this.scope.isExpanded()){
42732                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42733                 }
42734                 return true;
42735             },
42736
42737             forceKeyDown: true
42738         });
42739         this.queryDelay = Math.max(this.queryDelay || 10,
42740                 this.mode == 'local' ? 10 : 250);
42741         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42742         if(this.typeAhead){
42743             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42744         }
42745         if(this.editable !== false){
42746             this.el.on("keyup", this.onKeyUp, this);
42747         }
42748         if(this.forceSelection){
42749             this.on('blur', this.doForce, this);
42750         }
42751     },
42752
42753     onDestroy : function(){
42754         if(this.view){
42755             this.view.setStore(null);
42756             this.view.el.removeAllListeners();
42757             this.view.el.remove();
42758             this.view.purgeListeners();
42759         }
42760         if(this.list){
42761             this.list.destroy();
42762         }
42763         if(this.store){
42764             this.store.un('beforeload', this.onBeforeLoad, this);
42765             this.store.un('load', this.onLoad, this);
42766             this.store.un('loadexception', this.onLoadException, this);
42767         }
42768         Roo.form.ComboBox.superclass.onDestroy.call(this);
42769     },
42770
42771     // private
42772     fireKey : function(e){
42773         if(e.isNavKeyPress() && !this.list.isVisible()){
42774             this.fireEvent("specialkey", this, e);
42775         }
42776     },
42777
42778     // private
42779     onResize: function(w, h){
42780         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42781         
42782         if(typeof w != 'number'){
42783             // we do not handle it!?!?
42784             return;
42785         }
42786         var tw = this.trigger.getWidth();
42787         tw += this.addicon ? this.addicon.getWidth() : 0;
42788         tw += this.editicon ? this.editicon.getWidth() : 0;
42789         var x = w - tw;
42790         this.el.setWidth( this.adjustWidth('input', x));
42791             
42792         this.trigger.setStyle('left', x+'px');
42793         
42794         if(this.list && this.listWidth === undefined){
42795             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42796             this.list.setWidth(lw);
42797             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42798         }
42799         
42800     
42801         
42802     },
42803
42804     /**
42805      * Allow or prevent the user from directly editing the field text.  If false is passed,
42806      * the user will only be able to select from the items defined in the dropdown list.  This method
42807      * is the runtime equivalent of setting the 'editable' config option at config time.
42808      * @param {Boolean} value True to allow the user to directly edit the field text
42809      */
42810     setEditable : function(value){
42811         if(value == this.editable){
42812             return;
42813         }
42814         this.editable = value;
42815         if(!value){
42816             this.el.dom.setAttribute('readOnly', true);
42817             this.el.on('mousedown', this.onTriggerClick,  this);
42818             this.el.addClass('x-combo-noedit');
42819         }else{
42820             this.el.dom.setAttribute('readOnly', false);
42821             this.el.un('mousedown', this.onTriggerClick,  this);
42822             this.el.removeClass('x-combo-noedit');
42823         }
42824     },
42825
42826     // private
42827     onBeforeLoad : function(){
42828         if(!this.hasFocus){
42829             return;
42830         }
42831         this.innerList.update(this.loadingText ?
42832                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42833         this.restrictHeight();
42834         this.selectedIndex = -1;
42835     },
42836
42837     // private
42838     onLoad : function(){
42839         if(!this.hasFocus){
42840             return;
42841         }
42842         if(this.store.getCount() > 0){
42843             this.expand();
42844             this.restrictHeight();
42845             if(this.lastQuery == this.allQuery){
42846                 if(this.editable){
42847                     this.el.dom.select();
42848                 }
42849                 if(!this.selectByValue(this.value, true)){
42850                     this.select(0, true);
42851                 }
42852             }else{
42853                 this.selectNext();
42854                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42855                     this.taTask.delay(this.typeAheadDelay);
42856                 }
42857             }
42858         }else{
42859             this.onEmptyResults();
42860         }
42861         //this.el.focus();
42862     },
42863     // private
42864     onLoadException : function()
42865     {
42866         this.collapse();
42867         Roo.log(this.store.reader.jsonData);
42868         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42869             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42870         }
42871         
42872         
42873     },
42874     // private
42875     onTypeAhead : function(){
42876         if(this.store.getCount() > 0){
42877             var r = this.store.getAt(0);
42878             var newValue = r.data[this.displayField];
42879             var len = newValue.length;
42880             var selStart = this.getRawValue().length;
42881             if(selStart != len){
42882                 this.setRawValue(newValue);
42883                 this.selectText(selStart, newValue.length);
42884             }
42885         }
42886     },
42887
42888     // private
42889     onSelect : function(record, index){
42890         if(this.fireEvent('beforeselect', this, record, index) !== false){
42891             this.setFromData(index > -1 ? record.data : false);
42892             this.collapse();
42893             this.fireEvent('select', this, record, index);
42894         }
42895     },
42896
42897     /**
42898      * Returns the currently selected field value or empty string if no value is set.
42899      * @return {String} value The selected value
42900      */
42901     getValue : function(){
42902         if(this.valueField){
42903             return typeof this.value != 'undefined' ? this.value : '';
42904         }
42905         return Roo.form.ComboBox.superclass.getValue.call(this);
42906     },
42907
42908     /**
42909      * Clears any text/value currently set in the field
42910      */
42911     clearValue : function(){
42912         if(this.hiddenField){
42913             this.hiddenField.value = '';
42914         }
42915         this.value = '';
42916         this.setRawValue('');
42917         this.lastSelectionText = '';
42918         
42919     },
42920
42921     /**
42922      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42923      * will be displayed in the field.  If the value does not match the data value of an existing item,
42924      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42925      * Otherwise the field will be blank (although the value will still be set).
42926      * @param {String} value The value to match
42927      */
42928     setValue : function(v){
42929         var text = v;
42930         if(this.valueField){
42931             var r = this.findRecord(this.valueField, v);
42932             if(r){
42933                 text = r.data[this.displayField];
42934             }else if(this.valueNotFoundText !== undefined){
42935                 text = this.valueNotFoundText;
42936             }
42937         }
42938         this.lastSelectionText = text;
42939         if(this.hiddenField){
42940             this.hiddenField.value = v;
42941         }
42942         Roo.form.ComboBox.superclass.setValue.call(this, text);
42943         this.value = v;
42944     },
42945     /**
42946      * @property {Object} the last set data for the element
42947      */
42948     
42949     lastData : false,
42950     /**
42951      * Sets the value of the field based on a object which is related to the record format for the store.
42952      * @param {Object} value the value to set as. or false on reset?
42953      */
42954     setFromData : function(o){
42955         var dv = ''; // display value
42956         var vv = ''; // value value..
42957         this.lastData = o;
42958         if (this.displayField) {
42959             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42960         } else {
42961             // this is an error condition!!!
42962             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42963         }
42964         
42965         if(this.valueField){
42966             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42967         }
42968         if(this.hiddenField){
42969             this.hiddenField.value = vv;
42970             
42971             this.lastSelectionText = dv;
42972             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42973             this.value = vv;
42974             return;
42975         }
42976         // no hidden field.. - we store the value in 'value', but still display
42977         // display field!!!!
42978         this.lastSelectionText = dv;
42979         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42980         this.value = vv;
42981         
42982         
42983     },
42984     // private
42985     reset : function(){
42986         // overridden so that last data is reset..
42987         this.setValue(this.resetValue);
42988         this.originalValue = this.getValue();
42989         this.clearInvalid();
42990         this.lastData = false;
42991         if (this.view) {
42992             this.view.clearSelections();
42993         }
42994     },
42995     // private
42996     findRecord : function(prop, value){
42997         var record;
42998         if(this.store.getCount() > 0){
42999             this.store.each(function(r){
43000                 if(r.data[prop] == value){
43001                     record = r;
43002                     return false;
43003                 }
43004                 return true;
43005             });
43006         }
43007         return record;
43008     },
43009     
43010     getName: function()
43011     {
43012         // returns hidden if it's set..
43013         if (!this.rendered) {return ''};
43014         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43015         
43016     },
43017     // private
43018     onViewMove : function(e, t){
43019         this.inKeyMode = false;
43020     },
43021
43022     // private
43023     onViewOver : function(e, t){
43024         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43025             return;
43026         }
43027         var item = this.view.findItemFromChild(t);
43028         if(item){
43029             var index = this.view.indexOf(item);
43030             this.select(index, false);
43031         }
43032     },
43033
43034     // private
43035     onViewClick : function(doFocus)
43036     {
43037         var index = this.view.getSelectedIndexes()[0];
43038         var r = this.store.getAt(index);
43039         if(r){
43040             this.onSelect(r, index);
43041         }
43042         if(doFocus !== false && !this.blockFocus){
43043             this.el.focus();
43044         }
43045     },
43046
43047     // private
43048     restrictHeight : function(){
43049         this.innerList.dom.style.height = '';
43050         var inner = this.innerList.dom;
43051         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43052         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43053         this.list.beginUpdate();
43054         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43055         this.list.alignTo(this.el, this.listAlign);
43056         this.list.endUpdate();
43057     },
43058
43059     // private
43060     onEmptyResults : function(){
43061         this.collapse();
43062     },
43063
43064     /**
43065      * Returns true if the dropdown list is expanded, else false.
43066      */
43067     isExpanded : function(){
43068         return this.list.isVisible();
43069     },
43070
43071     /**
43072      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43073      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43074      * @param {String} value The data value of the item to select
43075      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43076      * selected item if it is not currently in view (defaults to true)
43077      * @return {Boolean} True if the value matched an item in the list, else false
43078      */
43079     selectByValue : function(v, scrollIntoView){
43080         if(v !== undefined && v !== null){
43081             var r = this.findRecord(this.valueField || this.displayField, v);
43082             if(r){
43083                 this.select(this.store.indexOf(r), scrollIntoView);
43084                 return true;
43085             }
43086         }
43087         return false;
43088     },
43089
43090     /**
43091      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43092      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43093      * @param {Number} index The zero-based index of the list item to select
43094      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43095      * selected item if it is not currently in view (defaults to true)
43096      */
43097     select : function(index, scrollIntoView){
43098         this.selectedIndex = index;
43099         this.view.select(index);
43100         if(scrollIntoView !== false){
43101             var el = this.view.getNode(index);
43102             if(el){
43103                 this.innerList.scrollChildIntoView(el, false);
43104             }
43105         }
43106     },
43107
43108     // private
43109     selectNext : function(){
43110         var ct = this.store.getCount();
43111         if(ct > 0){
43112             if(this.selectedIndex == -1){
43113                 this.select(0);
43114             }else if(this.selectedIndex < ct-1){
43115                 this.select(this.selectedIndex+1);
43116             }
43117         }
43118     },
43119
43120     // private
43121     selectPrev : function(){
43122         var ct = this.store.getCount();
43123         if(ct > 0){
43124             if(this.selectedIndex == -1){
43125                 this.select(0);
43126             }else if(this.selectedIndex != 0){
43127                 this.select(this.selectedIndex-1);
43128             }
43129         }
43130     },
43131
43132     // private
43133     onKeyUp : function(e){
43134         if(this.editable !== false && !e.isSpecialKey()){
43135             this.lastKey = e.getKey();
43136             this.dqTask.delay(this.queryDelay);
43137         }
43138     },
43139
43140     // private
43141     validateBlur : function(){
43142         return !this.list || !this.list.isVisible();   
43143     },
43144
43145     // private
43146     initQuery : function(){
43147         this.doQuery(this.getRawValue());
43148     },
43149
43150     // private
43151     doForce : function(){
43152         if(this.el.dom.value.length > 0){
43153             this.el.dom.value =
43154                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43155              
43156         }
43157     },
43158
43159     /**
43160      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43161      * query allowing the query action to be canceled if needed.
43162      * @param {String} query The SQL query to execute
43163      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43164      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43165      * saved in the current store (defaults to false)
43166      */
43167     doQuery : function(q, forceAll){
43168         if(q === undefined || q === null){
43169             q = '';
43170         }
43171         var qe = {
43172             query: q,
43173             forceAll: forceAll,
43174             combo: this,
43175             cancel:false
43176         };
43177         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43178             return false;
43179         }
43180         q = qe.query;
43181         forceAll = qe.forceAll;
43182         if(forceAll === true || (q.length >= this.minChars)){
43183             if(this.lastQuery != q || this.alwaysQuery){
43184                 this.lastQuery = q;
43185                 if(this.mode == 'local'){
43186                     this.selectedIndex = -1;
43187                     if(forceAll){
43188                         this.store.clearFilter();
43189                     }else{
43190                         this.store.filter(this.displayField, q);
43191                     }
43192                     this.onLoad();
43193                 }else{
43194                     this.store.baseParams[this.queryParam] = q;
43195                     this.store.load({
43196                         params: this.getParams(q)
43197                     });
43198                     this.expand();
43199                 }
43200             }else{
43201                 this.selectedIndex = -1;
43202                 this.onLoad();   
43203             }
43204         }
43205     },
43206
43207     // private
43208     getParams : function(q){
43209         var p = {};
43210         //p[this.queryParam] = q;
43211         if(this.pageSize){
43212             p.start = 0;
43213             p.limit = this.pageSize;
43214         }
43215         return p;
43216     },
43217
43218     /**
43219      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43220      */
43221     collapse : function(){
43222         if(!this.isExpanded()){
43223             return;
43224         }
43225         this.list.hide();
43226         Roo.get(document).un('mousedown', this.collapseIf, this);
43227         Roo.get(document).un('mousewheel', this.collapseIf, this);
43228         if (!this.editable) {
43229             Roo.get(document).un('keydown', this.listKeyPress, this);
43230         }
43231         this.fireEvent('collapse', this);
43232     },
43233
43234     // private
43235     collapseIf : function(e){
43236         if(!e.within(this.wrap) && !e.within(this.list)){
43237             this.collapse();
43238         }
43239     },
43240
43241     /**
43242      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43243      */
43244     expand : function(){
43245         if(this.isExpanded() || !this.hasFocus){
43246             return;
43247         }
43248         this.list.alignTo(this.el, this.listAlign);
43249         this.list.show();
43250         Roo.get(document).on('mousedown', this.collapseIf, this);
43251         Roo.get(document).on('mousewheel', this.collapseIf, this);
43252         if (!this.editable) {
43253             Roo.get(document).on('keydown', this.listKeyPress, this);
43254         }
43255         
43256         this.fireEvent('expand', this);
43257     },
43258
43259     // private
43260     // Implements the default empty TriggerField.onTriggerClick function
43261     onTriggerClick : function(){
43262         if(this.disabled){
43263             return;
43264         }
43265         if(this.isExpanded()){
43266             this.collapse();
43267             if (!this.blockFocus) {
43268                 this.el.focus();
43269             }
43270             
43271         }else {
43272             this.hasFocus = true;
43273             if(this.triggerAction == 'all') {
43274                 this.doQuery(this.allQuery, true);
43275             } else {
43276                 this.doQuery(this.getRawValue());
43277             }
43278             if (!this.blockFocus) {
43279                 this.el.focus();
43280             }
43281         }
43282     },
43283     listKeyPress : function(e)
43284     {
43285         //Roo.log('listkeypress');
43286         // scroll to first matching element based on key pres..
43287         if (e.isSpecialKey()) {
43288             return false;
43289         }
43290         var k = String.fromCharCode(e.getKey()).toUpperCase();
43291         //Roo.log(k);
43292         var match  = false;
43293         var csel = this.view.getSelectedNodes();
43294         var cselitem = false;
43295         if (csel.length) {
43296             var ix = this.view.indexOf(csel[0]);
43297             cselitem  = this.store.getAt(ix);
43298             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43299                 cselitem = false;
43300             }
43301             
43302         }
43303         
43304         this.store.each(function(v) { 
43305             if (cselitem) {
43306                 // start at existing selection.
43307                 if (cselitem.id == v.id) {
43308                     cselitem = false;
43309                 }
43310                 return;
43311             }
43312                 
43313             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43314                 match = this.store.indexOf(v);
43315                 return false;
43316             }
43317         }, this);
43318         
43319         if (match === false) {
43320             return true; // no more action?
43321         }
43322         // scroll to?
43323         this.view.select(match);
43324         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43325         sn.scrollIntoView(sn.dom.parentNode, false);
43326     } 
43327
43328     /** 
43329     * @cfg {Boolean} grow 
43330     * @hide 
43331     */
43332     /** 
43333     * @cfg {Number} growMin 
43334     * @hide 
43335     */
43336     /** 
43337     * @cfg {Number} growMax 
43338     * @hide 
43339     */
43340     /**
43341      * @hide
43342      * @method autoSize
43343      */
43344 });/*
43345  * Copyright(c) 2010-2012, Roo J Solutions Limited
43346  *
43347  * Licence LGPL
43348  *
43349  */
43350
43351 /**
43352  * @class Roo.form.ComboBoxArray
43353  * @extends Roo.form.TextField
43354  * A facebook style adder... for lists of email / people / countries  etc...
43355  * pick multiple items from a combo box, and shows each one.
43356  *
43357  *  Fred [x]  Brian [x]  [Pick another |v]
43358  *
43359  *
43360  *  For this to work: it needs various extra information
43361  *    - normal combo problay has
43362  *      name, hiddenName
43363  *    + displayField, valueField
43364  *
43365  *    For our purpose...
43366  *
43367  *
43368  *   If we change from 'extends' to wrapping...
43369  *   
43370  *  
43371  *
43372  
43373  
43374  * @constructor
43375  * Create a new ComboBoxArray.
43376  * @param {Object} config Configuration options
43377  */
43378  
43379
43380 Roo.form.ComboBoxArray = function(config)
43381 {
43382     this.addEvents({
43383         /**
43384          * @event beforeremove
43385          * Fires before remove the value from the list
43386              * @param {Roo.form.ComboBoxArray} _self This combo box array
43387              * @param {Roo.form.ComboBoxArray.Item} item removed item
43388              */
43389         'beforeremove' : true,
43390         /**
43391          * @event remove
43392          * Fires when remove the value from the list
43393              * @param {Roo.form.ComboBoxArray} _self This combo box array
43394              * @param {Roo.form.ComboBoxArray.Item} item removed item
43395              */
43396         'remove' : true
43397         
43398         
43399     });
43400     
43401     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43402     
43403     this.items = new Roo.util.MixedCollection(false);
43404     
43405     // construct the child combo...
43406     
43407     
43408     
43409     
43410    
43411     
43412 }
43413
43414  
43415 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43416
43417     /**
43418      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43419      */
43420     
43421     lastData : false,
43422     
43423     // behavies liek a hiddne field
43424     inputType:      'hidden',
43425     /**
43426      * @cfg {Number} width The width of the box that displays the selected element
43427      */ 
43428     width:          300,
43429
43430     
43431     
43432     /**
43433      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43434      */
43435     name : false,
43436     /**
43437      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43438      */
43439     hiddenName : false,
43440       /**
43441      * @cfg {String} seperator    The value seperator normally ',' 
43442      */
43443     seperator : ',',
43444     
43445     // private the array of items that are displayed..
43446     items  : false,
43447     // private - the hidden field el.
43448     hiddenEl : false,
43449     // private - the filed el..
43450     el : false,
43451     
43452     //validateValue : function() { return true; }, // all values are ok!
43453     //onAddClick: function() { },
43454     
43455     onRender : function(ct, position) 
43456     {
43457         
43458         // create the standard hidden element
43459         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43460         
43461         
43462         // give fake names to child combo;
43463         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43464         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43465         
43466         this.combo = Roo.factory(this.combo, Roo.form);
43467         this.combo.onRender(ct, position);
43468         if (typeof(this.combo.width) != 'undefined') {
43469             this.combo.onResize(this.combo.width,0);
43470         }
43471         
43472         this.combo.initEvents();
43473         
43474         // assigned so form know we need to do this..
43475         this.store          = this.combo.store;
43476         this.valueField     = this.combo.valueField;
43477         this.displayField   = this.combo.displayField ;
43478         
43479         
43480         this.combo.wrap.addClass('x-cbarray-grp');
43481         
43482         var cbwrap = this.combo.wrap.createChild(
43483             {tag: 'div', cls: 'x-cbarray-cb'},
43484             this.combo.el.dom
43485         );
43486         
43487              
43488         this.hiddenEl = this.combo.wrap.createChild({
43489             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43490         });
43491         this.el = this.combo.wrap.createChild({
43492             tag: 'input',  type:'hidden' , name: this.name, value : ''
43493         });
43494          //   this.el.dom.removeAttribute("name");
43495         
43496         
43497         this.outerWrap = this.combo.wrap;
43498         this.wrap = cbwrap;
43499         
43500         this.outerWrap.setWidth(this.width);
43501         this.outerWrap.dom.removeChild(this.el.dom);
43502         
43503         this.wrap.dom.appendChild(this.el.dom);
43504         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43505         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43506         
43507         this.combo.trigger.setStyle('position','relative');
43508         this.combo.trigger.setStyle('left', '0px');
43509         this.combo.trigger.setStyle('top', '2px');
43510         
43511         this.combo.el.setStyle('vertical-align', 'text-bottom');
43512         
43513         //this.trigger.setStyle('vertical-align', 'top');
43514         
43515         // this should use the code from combo really... on('add' ....)
43516         if (this.adder) {
43517             
43518         
43519             this.adder = this.outerWrap.createChild(
43520                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43521             var _t = this;
43522             this.adder.on('click', function(e) {
43523                 _t.fireEvent('adderclick', this, e);
43524             }, _t);
43525         }
43526         //var _t = this;
43527         //this.adder.on('click', this.onAddClick, _t);
43528         
43529         
43530         this.combo.on('select', function(cb, rec, ix) {
43531             this.addItem(rec.data);
43532             
43533             cb.setValue('');
43534             cb.el.dom.value = '';
43535             //cb.lastData = rec.data;
43536             // add to list
43537             
43538         }, this);
43539         
43540         
43541     },
43542     
43543     
43544     getName: function()
43545     {
43546         // returns hidden if it's set..
43547         if (!this.rendered) {return ''};
43548         return  this.hiddenName ? this.hiddenName : this.name;
43549         
43550     },
43551     
43552     
43553     onResize: function(w, h){
43554         
43555         return;
43556         // not sure if this is needed..
43557         //this.combo.onResize(w,h);
43558         
43559         if(typeof w != 'number'){
43560             // we do not handle it!?!?
43561             return;
43562         }
43563         var tw = this.combo.trigger.getWidth();
43564         tw += this.addicon ? this.addicon.getWidth() : 0;
43565         tw += this.editicon ? this.editicon.getWidth() : 0;
43566         var x = w - tw;
43567         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43568             
43569         this.combo.trigger.setStyle('left', '0px');
43570         
43571         if(this.list && this.listWidth === undefined){
43572             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43573             this.list.setWidth(lw);
43574             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43575         }
43576         
43577     
43578         
43579     },
43580     
43581     addItem: function(rec)
43582     {
43583         var valueField = this.combo.valueField;
43584         var displayField = this.combo.displayField;
43585         
43586         if (this.items.indexOfKey(rec[valueField]) > -1) {
43587             //console.log("GOT " + rec.data.id);
43588             return;
43589         }
43590         
43591         var x = new Roo.form.ComboBoxArray.Item({
43592             //id : rec[this.idField],
43593             data : rec,
43594             displayField : displayField ,
43595             tipField : displayField ,
43596             cb : this
43597         });
43598         // use the 
43599         this.items.add(rec[valueField],x);
43600         // add it before the element..
43601         this.updateHiddenEl();
43602         x.render(this.outerWrap, this.wrap.dom);
43603         // add the image handler..
43604     },
43605     
43606     updateHiddenEl : function()
43607     {
43608         this.validate();
43609         if (!this.hiddenEl) {
43610             return;
43611         }
43612         var ar = [];
43613         var idField = this.combo.valueField;
43614         
43615         this.items.each(function(f) {
43616             ar.push(f.data[idField]);
43617         });
43618         this.hiddenEl.dom.value = ar.join(this.seperator);
43619         this.validate();
43620     },
43621     
43622     reset : function()
43623     {
43624         this.items.clear();
43625         
43626         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43627            el.remove();
43628         });
43629         
43630         this.el.dom.value = '';
43631         if (this.hiddenEl) {
43632             this.hiddenEl.dom.value = '';
43633         }
43634         
43635     },
43636     getValue: function()
43637     {
43638         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43639     },
43640     setValue: function(v) // not a valid action - must use addItems..
43641     {
43642         
43643         this.reset();
43644          
43645         if (this.store.isLocal && (typeof(v) == 'string')) {
43646             // then we can use the store to find the values..
43647             // comma seperated at present.. this needs to allow JSON based encoding..
43648             this.hiddenEl.value  = v;
43649             var v_ar = [];
43650             Roo.each(v.split(this.seperator), function(k) {
43651                 Roo.log("CHECK " + this.valueField + ',' + k);
43652                 var li = this.store.query(this.valueField, k);
43653                 if (!li.length) {
43654                     return;
43655                 }
43656                 var add = {};
43657                 add[this.valueField] = k;
43658                 add[this.displayField] = li.item(0).data[this.displayField];
43659                 
43660                 this.addItem(add);
43661             }, this) 
43662              
43663         }
43664         if (typeof(v) == 'object' ) {
43665             // then let's assume it's an array of objects..
43666             Roo.each(v, function(l) {
43667                 var add = l;
43668                 if (typeof(l) == 'string') {
43669                     add = {};
43670                     add[this.valueField] = l;
43671                     add[this.displayField] = l
43672                 }
43673                 this.addItem(add);
43674             }, this);
43675              
43676         }
43677         
43678         
43679     },
43680     setFromData: function(v)
43681     {
43682         // this recieves an object, if setValues is called.
43683         this.reset();
43684         this.el.dom.value = v[this.displayField];
43685         this.hiddenEl.dom.value = v[this.valueField];
43686         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43687             return;
43688         }
43689         var kv = v[this.valueField];
43690         var dv = v[this.displayField];
43691         kv = typeof(kv) != 'string' ? '' : kv;
43692         dv = typeof(dv) != 'string' ? '' : dv;
43693         
43694         
43695         var keys = kv.split(this.seperator);
43696         var display = dv.split(this.seperator);
43697         for (var i = 0 ; i < keys.length; i++) {
43698             add = {};
43699             add[this.valueField] = keys[i];
43700             add[this.displayField] = display[i];
43701             this.addItem(add);
43702         }
43703       
43704         
43705     },
43706     
43707     /**
43708      * Validates the combox array value
43709      * @return {Boolean} True if the value is valid, else false
43710      */
43711     validate : function(){
43712         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43713             this.clearInvalid();
43714             return true;
43715         }
43716         return false;
43717     },
43718     
43719     validateValue : function(value){
43720         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43721         
43722     },
43723     
43724     /*@
43725      * overide
43726      * 
43727      */
43728     isDirty : function() {
43729         if(this.disabled) {
43730             return false;
43731         }
43732         
43733         try {
43734             var d = Roo.decode(String(this.originalValue));
43735         } catch (e) {
43736             return String(this.getValue()) !== String(this.originalValue);
43737         }
43738         
43739         var originalValue = [];
43740         
43741         for (var i = 0; i < d.length; i++){
43742             originalValue.push(d[i][this.valueField]);
43743         }
43744         
43745         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43746         
43747     }
43748     
43749 });
43750
43751
43752
43753 /**
43754  * @class Roo.form.ComboBoxArray.Item
43755  * @extends Roo.BoxComponent
43756  * A selected item in the list
43757  *  Fred [x]  Brian [x]  [Pick another |v]
43758  * 
43759  * @constructor
43760  * Create a new item.
43761  * @param {Object} config Configuration options
43762  */
43763  
43764 Roo.form.ComboBoxArray.Item = function(config) {
43765     config.id = Roo.id();
43766     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43767 }
43768
43769 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43770     data : {},
43771     cb: false,
43772     displayField : false,
43773     tipField : false,
43774     
43775     
43776     defaultAutoCreate : {
43777         tag: 'div',
43778         cls: 'x-cbarray-item',
43779         cn : [ 
43780             { tag: 'div' },
43781             {
43782                 tag: 'img',
43783                 width:16,
43784                 height : 16,
43785                 src : Roo.BLANK_IMAGE_URL ,
43786                 align: 'center'
43787             }
43788         ]
43789         
43790     },
43791     
43792  
43793     onRender : function(ct, position)
43794     {
43795         Roo.form.Field.superclass.onRender.call(this, ct, position);
43796         
43797         if(!this.el){
43798             var cfg = this.getAutoCreate();
43799             this.el = ct.createChild(cfg, position);
43800         }
43801         
43802         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43803         
43804         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43805             this.cb.renderer(this.data) :
43806             String.format('{0}',this.data[this.displayField]);
43807         
43808             
43809         this.el.child('div').dom.setAttribute('qtip',
43810                         String.format('{0}',this.data[this.tipField])
43811         );
43812         
43813         this.el.child('img').on('click', this.remove, this);
43814         
43815     },
43816    
43817     remove : function()
43818     {
43819         if(this.cb.disabled){
43820             return;
43821         }
43822         
43823         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43824             this.cb.items.remove(this);
43825             this.el.child('img').un('click', this.remove, this);
43826             this.el.remove();
43827             this.cb.updateHiddenEl();
43828
43829             this.cb.fireEvent('remove', this.cb, this);
43830         }
43831         
43832     }
43833 });/*
43834  * RooJS Library 1.1.1
43835  * Copyright(c) 2008-2011  Alan Knowles
43836  *
43837  * License - LGPL
43838  */
43839  
43840
43841 /**
43842  * @class Roo.form.ComboNested
43843  * @extends Roo.form.ComboBox
43844  * A combobox for that allows selection of nested items in a list,
43845  * eg.
43846  *
43847  *  Book
43848  *    -> red
43849  *    -> green
43850  *  Table
43851  *    -> square
43852  *      ->red
43853  *      ->green
43854  *    -> rectangle
43855  *      ->green
43856  *      
43857  * 
43858  * @constructor
43859  * Create a new ComboNested
43860  * @param {Object} config Configuration options
43861  */
43862 Roo.form.ComboNested = function(config){
43863     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43864     // should verify some data...
43865     // like
43866     // hiddenName = required..
43867     // displayField = required
43868     // valudField == required
43869     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43870     var _t = this;
43871     Roo.each(req, function(e) {
43872         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43873             throw "Roo.form.ComboNested : missing value for: " + e;
43874         }
43875     });
43876      
43877     
43878 };
43879
43880 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43881    
43882     /*
43883      * @config {Number} max Number of columns to show
43884      */
43885     
43886     maxColumns : 3,
43887    
43888     list : null, // the outermost div..
43889     innerLists : null, // the
43890     views : null,
43891     stores : null,
43892     // private
43893     loadingChildren : false,
43894     
43895     onRender : function(ct, position)
43896     {
43897         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43898         
43899         if(this.hiddenName){
43900             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43901                     'before', true);
43902             this.hiddenField.value =
43903                 this.hiddenValue !== undefined ? this.hiddenValue :
43904                 this.value !== undefined ? this.value : '';
43905
43906             // prevent input submission
43907             this.el.dom.removeAttribute('name');
43908              
43909              
43910         }
43911         
43912         if(Roo.isGecko){
43913             this.el.dom.setAttribute('autocomplete', 'off');
43914         }
43915
43916         var cls = 'x-combo-list';
43917
43918         this.list = new Roo.Layer({
43919             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43920         });
43921
43922         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43923         this.list.setWidth(lw);
43924         this.list.swallowEvent('mousewheel');
43925         this.assetHeight = 0;
43926
43927         if(this.title){
43928             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43929             this.assetHeight += this.header.getHeight();
43930         }
43931         this.innerLists = [];
43932         this.views = [];
43933         this.stores = [];
43934         for (var i =0 ; i < this.maxColumns; i++) {
43935             this.onRenderList( cls, i);
43936         }
43937         
43938         // always needs footer, as we are going to have an 'OK' button.
43939         this.footer = this.list.createChild({cls:cls+'-ft'});
43940         this.pageTb = new Roo.Toolbar(this.footer);  
43941         var _this = this;
43942         this.pageTb.add(  {
43943             
43944             text: 'Done',
43945             handler: function()
43946             {
43947                 _this.collapse();
43948             }
43949         });
43950         
43951         if ( this.allowBlank && !this.disableClear) {
43952             
43953             this.pageTb.add(new Roo.Toolbar.Fill(), {
43954                 cls: 'x-btn-icon x-btn-clear',
43955                 text: '&#160;',
43956                 handler: function()
43957                 {
43958                     _this.collapse();
43959                     _this.clearValue();
43960                     _this.onSelect(false, -1);
43961                 }
43962             });
43963         }
43964         if (this.footer) {
43965             this.assetHeight += this.footer.getHeight();
43966         }
43967         
43968     },
43969     onRenderList : function (  cls, i)
43970     {
43971         
43972         var lw = Math.floor(
43973                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43974         );
43975         
43976         this.list.setWidth(lw); // default to '1'
43977
43978         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43979         //il.on('mouseover', this.onViewOver, this, { list:  i });
43980         //il.on('mousemove', this.onViewMove, this, { list:  i });
43981         il.setWidth(lw);
43982         il.setStyle({ 'overflow-x' : 'hidden'});
43983
43984         if(!this.tpl){
43985             this.tpl = new Roo.Template({
43986                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43987                 isEmpty: function (value, allValues) {
43988                     //Roo.log(value);
43989                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43990                     return dl ? 'has-children' : 'no-children'
43991                 }
43992             });
43993         }
43994         
43995         var store  = this.store;
43996         if (i > 0) {
43997             store  = new Roo.data.SimpleStore({
43998                 //fields : this.store.reader.meta.fields,
43999                 reader : this.store.reader,
44000                 data : [ ]
44001             });
44002         }
44003         this.stores[i]  = store;
44004                   
44005         var view = this.views[i] = new Roo.View(
44006             il,
44007             this.tpl,
44008             {
44009                 singleSelect:true,
44010                 store: store,
44011                 selectedClass: this.selectedClass
44012             }
44013         );
44014         view.getEl().setWidth(lw);
44015         view.getEl().setStyle({
44016             position: i < 1 ? 'relative' : 'absolute',
44017             top: 0,
44018             left: (i * lw ) + 'px',
44019             display : i > 0 ? 'none' : 'block'
44020         });
44021         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44022         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44023         //view.on('click', this.onViewClick, this, { list : i });
44024
44025         store.on('beforeload', this.onBeforeLoad, this);
44026         store.on('load',  this.onLoad, this, { list  : i});
44027         store.on('loadexception', this.onLoadException, this);
44028
44029         // hide the other vies..
44030         
44031         
44032         
44033     },
44034       
44035     restrictHeight : function()
44036     {
44037         var mh = 0;
44038         Roo.each(this.innerLists, function(il,i) {
44039             var el = this.views[i].getEl();
44040             el.dom.style.height = '';
44041             var inner = el.dom;
44042             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44043             // only adjust heights on other ones..
44044             mh = Math.max(h, mh);
44045             if (i < 1) {
44046                 
44047                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44048                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44049                
44050             }
44051             
44052             
44053         }, this);
44054         
44055         this.list.beginUpdate();
44056         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44057         this.list.alignTo(this.el, this.listAlign);
44058         this.list.endUpdate();
44059         
44060     },
44061      
44062     
44063     // -- store handlers..
44064     // private
44065     onBeforeLoad : function()
44066     {
44067         if(!this.hasFocus){
44068             return;
44069         }
44070         this.innerLists[0].update(this.loadingText ?
44071                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44072         this.restrictHeight();
44073         this.selectedIndex = -1;
44074     },
44075     // private
44076     onLoad : function(a,b,c,d)
44077     {
44078         if (!this.loadingChildren) {
44079             // then we are loading the top level. - hide the children
44080             for (var i = 1;i < this.views.length; i++) {
44081                 this.views[i].getEl().setStyle({ display : 'none' });
44082             }
44083             var lw = Math.floor(
44084                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44085             );
44086         
44087              this.list.setWidth(lw); // default to '1'
44088
44089             
44090         }
44091         if(!this.hasFocus){
44092             return;
44093         }
44094         
44095         if(this.store.getCount() > 0) {
44096             this.expand();
44097             this.restrictHeight();   
44098         } else {
44099             this.onEmptyResults();
44100         }
44101         
44102         if (!this.loadingChildren) {
44103             this.selectActive();
44104         }
44105         /*
44106         this.stores[1].loadData([]);
44107         this.stores[2].loadData([]);
44108         this.views
44109         */    
44110     
44111         //this.el.focus();
44112     },
44113     
44114     
44115     // private
44116     onLoadException : function()
44117     {
44118         this.collapse();
44119         Roo.log(this.store.reader.jsonData);
44120         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44121             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44122         }
44123         
44124         
44125     },
44126     // no cleaning of leading spaces on blur here.
44127     cleanLeadingSpace : function(e) { },
44128     
44129
44130     onSelectChange : function (view, sels, opts )
44131     {
44132         var ix = view.getSelectedIndexes();
44133          
44134         if (opts.list > this.maxColumns - 2) {
44135             if (view.store.getCount()<  1) {
44136                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44137
44138             } else  {
44139                 if (ix.length) {
44140                     // used to clear ?? but if we are loading unselected 
44141                     this.setFromData(view.store.getAt(ix[0]).data);
44142                 }
44143                 
44144             }
44145             
44146             return;
44147         }
44148         
44149         if (!ix.length) {
44150             // this get's fired when trigger opens..
44151            // this.setFromData({});
44152             var str = this.stores[opts.list+1];
44153             str.data.clear(); // removeall wihtout the fire events..
44154             return;
44155         }
44156         
44157         var rec = view.store.getAt(ix[0]);
44158          
44159         this.setFromData(rec.data);
44160         this.fireEvent('select', this, rec, ix[0]);
44161         
44162         var lw = Math.floor(
44163              (
44164                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44165              ) / this.maxColumns
44166         );
44167         this.loadingChildren = true;
44168         this.stores[opts.list+1].loadDataFromChildren( rec );
44169         this.loadingChildren = false;
44170         var dl = this.stores[opts.list+1]. getTotalCount();
44171         
44172         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44173         
44174         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44175         for (var i = opts.list+2; i < this.views.length;i++) {
44176             this.views[i].getEl().setStyle({ display : 'none' });
44177         }
44178         
44179         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44180         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44181         
44182         if (this.isLoading) {
44183            // this.selectActive(opts.list);
44184         }
44185          
44186     },
44187     
44188     
44189     
44190     
44191     onDoubleClick : function()
44192     {
44193         this.collapse(); //??
44194     },
44195     
44196      
44197     
44198     
44199     
44200     // private
44201     recordToStack : function(store, prop, value, stack)
44202     {
44203         var cstore = new Roo.data.SimpleStore({
44204             //fields : this.store.reader.meta.fields, // we need array reader.. for
44205             reader : this.store.reader,
44206             data : [ ]
44207         });
44208         var _this = this;
44209         var record  = false;
44210         var srec = false;
44211         if(store.getCount() < 1){
44212             return false;
44213         }
44214         store.each(function(r){
44215             if(r.data[prop] == value){
44216                 record = r;
44217             srec = r;
44218                 return false;
44219             }
44220             if (r.data.cn && r.data.cn.length) {
44221                 cstore.loadDataFromChildren( r);
44222                 var cret = _this.recordToStack(cstore, prop, value, stack);
44223                 if (cret !== false) {
44224                     record = cret;
44225                     srec = r;
44226                     return false;
44227                 }
44228             }
44229              
44230             return true;
44231         });
44232         if (record == false) {
44233             return false
44234         }
44235         stack.unshift(srec);
44236         return record;
44237     },
44238     
44239     /*
44240      * find the stack of stores that match our value.
44241      *
44242      * 
44243      */
44244     
44245     selectActive : function ()
44246     {
44247         // if store is not loaded, then we will need to wait for that to happen first.
44248         var stack = [];
44249         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44250         for (var i = 0; i < stack.length; i++ ) {
44251             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44252         }
44253         
44254     }
44255         
44256          
44257     
44258     
44259     
44260     
44261 });/*
44262  * Based on:
44263  * Ext JS Library 1.1.1
44264  * Copyright(c) 2006-2007, Ext JS, LLC.
44265  *
44266  * Originally Released Under LGPL - original licence link has changed is not relivant.
44267  *
44268  * Fork - LGPL
44269  * <script type="text/javascript">
44270  */
44271 /**
44272  * @class Roo.form.Checkbox
44273  * @extends Roo.form.Field
44274  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44275  * @constructor
44276  * Creates a new Checkbox
44277  * @param {Object} config Configuration options
44278  */
44279 Roo.form.Checkbox = function(config){
44280     Roo.form.Checkbox.superclass.constructor.call(this, config);
44281     this.addEvents({
44282         /**
44283          * @event check
44284          * Fires when the checkbox is checked or unchecked.
44285              * @param {Roo.form.Checkbox} this This checkbox
44286              * @param {Boolean} checked The new checked value
44287              */
44288         check : true
44289     });
44290 };
44291
44292 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44293     /**
44294      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44295      */
44296     focusClass : undefined,
44297     /**
44298      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44299      */
44300     fieldClass: "x-form-field",
44301     /**
44302      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44303      */
44304     checked: false,
44305     /**
44306      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44307      * {tag: "input", type: "checkbox", autocomplete: "off"})
44308      */
44309     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44310     /**
44311      * @cfg {String} boxLabel The text that appears beside the checkbox
44312      */
44313     boxLabel : "",
44314     /**
44315      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44316      */  
44317     inputValue : '1',
44318     /**
44319      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44320      */
44321      valueOff: '0', // value when not checked..
44322
44323     actionMode : 'viewEl', 
44324     //
44325     // private
44326     itemCls : 'x-menu-check-item x-form-item',
44327     groupClass : 'x-menu-group-item',
44328     inputType : 'hidden',
44329     
44330     
44331     inSetChecked: false, // check that we are not calling self...
44332     
44333     inputElement: false, // real input element?
44334     basedOn: false, // ????
44335     
44336     isFormField: true, // not sure where this is needed!!!!
44337
44338     onResize : function(){
44339         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44340         if(!this.boxLabel){
44341             this.el.alignTo(this.wrap, 'c-c');
44342         }
44343     },
44344
44345     initEvents : function(){
44346         Roo.form.Checkbox.superclass.initEvents.call(this);
44347         this.el.on("click", this.onClick,  this);
44348         this.el.on("change", this.onClick,  this);
44349     },
44350
44351
44352     getResizeEl : function(){
44353         return this.wrap;
44354     },
44355
44356     getPositionEl : function(){
44357         return this.wrap;
44358     },
44359
44360     // private
44361     onRender : function(ct, position){
44362         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44363         /*
44364         if(this.inputValue !== undefined){
44365             this.el.dom.value = this.inputValue;
44366         }
44367         */
44368         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44369         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44370         var viewEl = this.wrap.createChild({ 
44371             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44372         this.viewEl = viewEl;   
44373         this.wrap.on('click', this.onClick,  this); 
44374         
44375         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44376         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44377         
44378         
44379         
44380         if(this.boxLabel){
44381             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44382         //    viewEl.on('click', this.onClick,  this); 
44383         }
44384         //if(this.checked){
44385             this.setChecked(this.checked);
44386         //}else{
44387             //this.checked = this.el.dom;
44388         //}
44389
44390     },
44391
44392     // private
44393     initValue : Roo.emptyFn,
44394
44395     /**
44396      * Returns the checked state of the checkbox.
44397      * @return {Boolean} True if checked, else false
44398      */
44399     getValue : function(){
44400         if(this.el){
44401             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44402         }
44403         return this.valueOff;
44404         
44405     },
44406
44407         // private
44408     onClick : function(){ 
44409         if (this.disabled) {
44410             return;
44411         }
44412         this.setChecked(!this.checked);
44413
44414         //if(this.el.dom.checked != this.checked){
44415         //    this.setValue(this.el.dom.checked);
44416        // }
44417     },
44418
44419     /**
44420      * Sets the checked state of the checkbox.
44421      * On is always based on a string comparison between inputValue and the param.
44422      * @param {Boolean/String} value - the value to set 
44423      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44424      */
44425     setValue : function(v,suppressEvent){
44426         
44427         
44428         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44429         //if(this.el && this.el.dom){
44430         //    this.el.dom.checked = this.checked;
44431         //    this.el.dom.defaultChecked = this.checked;
44432         //}
44433         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44434         //this.fireEvent("check", this, this.checked);
44435     },
44436     // private..
44437     setChecked : function(state,suppressEvent)
44438     {
44439         if (this.inSetChecked) {
44440             this.checked = state;
44441             return;
44442         }
44443         
44444     
44445         if(this.wrap){
44446             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44447         }
44448         this.checked = state;
44449         if(suppressEvent !== true){
44450             this.fireEvent('check', this, state);
44451         }
44452         this.inSetChecked = true;
44453         this.el.dom.value = state ? this.inputValue : this.valueOff;
44454         this.inSetChecked = false;
44455         
44456     },
44457     // handle setting of hidden value by some other method!!?!?
44458     setFromHidden: function()
44459     {
44460         if(!this.el){
44461             return;
44462         }
44463         //console.log("SET FROM HIDDEN");
44464         //alert('setFrom hidden');
44465         this.setValue(this.el.dom.value);
44466     },
44467     
44468     onDestroy : function()
44469     {
44470         if(this.viewEl){
44471             Roo.get(this.viewEl).remove();
44472         }
44473          
44474         Roo.form.Checkbox.superclass.onDestroy.call(this);
44475     },
44476     
44477     setBoxLabel : function(str)
44478     {
44479         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44480     }
44481
44482 });/*
44483  * Based on:
44484  * Ext JS Library 1.1.1
44485  * Copyright(c) 2006-2007, Ext JS, LLC.
44486  *
44487  * Originally Released Under LGPL - original licence link has changed is not relivant.
44488  *
44489  * Fork - LGPL
44490  * <script type="text/javascript">
44491  */
44492  
44493 /**
44494  * @class Roo.form.Radio
44495  * @extends Roo.form.Checkbox
44496  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44497  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44498  * @constructor
44499  * Creates a new Radio
44500  * @param {Object} config Configuration options
44501  */
44502 Roo.form.Radio = function(){
44503     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44504 };
44505 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44506     inputType: 'radio',
44507
44508     /**
44509      * If this radio is part of a group, it will return the selected value
44510      * @return {String}
44511      */
44512     getGroupValue : function(){
44513         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44514     },
44515     
44516     
44517     onRender : function(ct, position){
44518         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44519         
44520         if(this.inputValue !== undefined){
44521             this.el.dom.value = this.inputValue;
44522         }
44523          
44524         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44525         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44526         //var viewEl = this.wrap.createChild({ 
44527         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44528         //this.viewEl = viewEl;   
44529         //this.wrap.on('click', this.onClick,  this); 
44530         
44531         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44532         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44533         
44534         
44535         
44536         if(this.boxLabel){
44537             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44538         //    viewEl.on('click', this.onClick,  this); 
44539         }
44540          if(this.checked){
44541             this.el.dom.checked =   'checked' ;
44542         }
44543          
44544     } 
44545     
44546     
44547 });Roo.rtf = {}; // namespace
44548 Roo.rtf.Hex = function(hex)
44549 {
44550     this.hexstr = hex;
44551 };
44552 Roo.rtf.Paragraph = function(opts)
44553 {
44554     this.content = []; ///??? is that used?
44555 };Roo.rtf.Span = function(opts)
44556 {
44557     this.value = opts.value;
44558 };
44559
44560 Roo.rtf.Group = function(parent)
44561 {
44562     // we dont want to acutally store parent - it will make debug a nightmare..
44563     this.content = [];
44564     this.cn  = [];
44565      
44566        
44567     
44568 };
44569
44570 Roo.rtf.Group.prototype = {
44571     ignorable : false,
44572     content: false,
44573     cn: false,
44574     addContent : function(node) {
44575         // could set styles...
44576         this.content.push(node);
44577     },
44578     addChild : function(cn)
44579     {
44580         this.cn.push(cn);
44581     },
44582     // only for images really...
44583     toDataURL : function()
44584     {
44585         var mimetype = false;
44586         switch(true) {
44587             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44588                 mimetype = "image/png";
44589                 break;
44590              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44591                 mimetype = "image/jpeg";
44592                 break;
44593             default :
44594                 return 'about:blank'; // ?? error?
44595         }
44596         
44597         
44598         var hexstring = this.content[this.content.length-1].value;
44599         
44600         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44601             return String.fromCharCode(parseInt(a, 16));
44602         }).join(""));
44603     }
44604     
44605 };
44606 // this looks like it's normally the {rtf{ .... }}
44607 Roo.rtf.Document = function()
44608 {
44609     // we dont want to acutally store parent - it will make debug a nightmare..
44610     this.rtlch  = [];
44611     this.content = [];
44612     this.cn = [];
44613     
44614 };
44615 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44616     addChild : function(cn)
44617     {
44618         this.cn.push(cn);
44619         switch(cn.type) {
44620             case 'rtlch': // most content seems to be inside this??
44621             case 'listtext':
44622             case 'shpinst':
44623                 this.rtlch.push(cn);
44624                 return;
44625             default:
44626                 this[cn.type] = cn;
44627         }
44628         
44629     },
44630     
44631     getElementsByType : function(type)
44632     {
44633         var ret =  [];
44634         this._getElementsByType(type, ret, this.cn, 'rtf');
44635         return ret;
44636     },
44637     _getElementsByType : function (type, ret, search_array, path)
44638     {
44639         search_array.forEach(function(n,i) {
44640             if (n.type == type) {
44641                 n.path = path + '/' + n.type + ':' + i;
44642                 ret.push(n);
44643             }
44644             if (n.cn.length > 0) {
44645                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44646             }
44647         },this);
44648     }
44649     
44650 });
44651  
44652 Roo.rtf.Ctrl = function(opts)
44653 {
44654     this.value = opts.value;
44655     this.param = opts.param;
44656 };
44657 /**
44658  *
44659  *
44660  * based on this https://github.com/iarna/rtf-parser
44661  * it's really only designed to extract pict from pasted RTF 
44662  *
44663  * usage:
44664  *
44665  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
44666  *  
44667  *
44668  */
44669
44670  
44671
44672
44673
44674 Roo.rtf.Parser = function(text) {
44675     //super({objectMode: true})
44676     this.text = '';
44677     this.parserState = this.parseText;
44678     
44679     // these are for interpeter...
44680     this.doc = {};
44681     ///this.parserState = this.parseTop
44682     this.groupStack = [];
44683     this.hexStore = [];
44684     this.doc = false;
44685     
44686     this.groups = []; // where we put the return.
44687     
44688     for (var ii = 0; ii < text.length; ++ii) {
44689         ++this.cpos;
44690         
44691         if (text[ii] === '\n') {
44692             ++this.row;
44693             this.col = 1;
44694         } else {
44695             ++this.col;
44696         }
44697         this.parserState(text[ii]);
44698     }
44699     
44700     
44701     
44702 };
44703 Roo.rtf.Parser.prototype = {
44704     text : '', // string being parsed..
44705     controlWord : '',
44706     controlWordParam :  '',
44707     hexChar : '',
44708     doc : false,
44709     group: false,
44710     groupStack : false,
44711     hexStore : false,
44712     
44713     
44714     cpos : 0, 
44715     row : 1, // reportin?
44716     col : 1, //
44717
44718      
44719     push : function (el)
44720     {
44721         var m = 'cmd'+ el.type;
44722         if (typeof(this[m]) == 'undefined') {
44723             Roo.log('invalid cmd:' + el.type);
44724             return;
44725         }
44726         this[m](el);
44727         //Roo.log(el);
44728     },
44729     flushHexStore : function()
44730     {
44731         if (this.hexStore.length < 1) {
44732             return;
44733         }
44734         var hexstr = this.hexStore.map(
44735             function(cmd) {
44736                 return cmd.value;
44737         }).join('');
44738         
44739         this.group.addContent( new Roo.rtf.Hex( hexstr ));
44740               
44741             
44742         this.hexStore.splice(0)
44743         
44744     },
44745     
44746     cmdgroupstart : function()
44747     {
44748         this.flushHexStore();
44749         if (this.group) {
44750             this.groupStack.push(this.group);
44751         }
44752          // parent..
44753         if (this.doc === false) {
44754             this.group = this.doc = new Roo.rtf.Document();
44755             return;
44756             
44757         }
44758         this.group = new Roo.rtf.Group(this.group);
44759     },
44760     cmdignorable : function()
44761     {
44762         this.flushHexStore();
44763         this.group.ignorable = true;
44764     },
44765     cmdendparagraph : function()
44766     {
44767         this.flushHexStore();
44768         this.group.addContent(new Roo.rtf.Paragraph());
44769     },
44770     cmdgroupend : function ()
44771     {
44772         this.flushHexStore();
44773         var endingGroup = this.group;
44774         
44775         
44776         this.group = this.groupStack.pop();
44777         if (this.group) {
44778             this.group.addChild(endingGroup);
44779         }
44780         
44781         
44782         
44783         var doc = this.group || this.doc;
44784         //if (endingGroup instanceof FontTable) {
44785         //  doc.fonts = endingGroup.table
44786         //} else if (endingGroup instanceof ColorTable) {
44787         //  doc.colors = endingGroup.table
44788         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
44789         if (endingGroup.ignorable === false) {
44790             //code
44791             this.groups.push(endingGroup);
44792            // Roo.log( endingGroup );
44793         }
44794             //Roo.each(endingGroup.content, function(item)) {
44795             //    doc.addContent(item);
44796             //}
44797             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
44798         //}
44799     },
44800     cmdtext : function (cmd)
44801     {
44802         this.flushHexStore();
44803         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
44804             //this.group = this.doc
44805         }
44806         this.group.addContent(new Roo.rtf.Span(cmd));
44807     },
44808     cmdcontrolword : function (cmd)
44809     {
44810         this.flushHexStore();
44811         if (!this.group.type) {
44812             this.group.type = cmd.value;
44813             return;
44814         }
44815         this.group.addContent(new Roo.rtf.Ctrl(cmd));
44816         // we actually don't care about ctrl words...
44817         return ;
44818         /*
44819         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
44820         if (this[method]) {
44821             this[method](cmd.param)
44822         } else {
44823             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
44824         }
44825         */
44826     },
44827     cmdhexchar : function(cmd) {
44828         this.hexStore.push(cmd);
44829     },
44830     cmderror : function(cmd) {
44831         throw new Exception (cmd.value);
44832     },
44833     
44834     /*
44835       _flush (done) {
44836         if (this.text !== '\u0000') this.emitText()
44837         done()
44838       }
44839       */
44840       
44841       
44842     parseText : function(c)
44843     {
44844         if (c === '\\') {
44845             this.parserState = this.parseEscapes;
44846         } else if (c === '{') {
44847             this.emitStartGroup();
44848         } else if (c === '}') {
44849             this.emitEndGroup();
44850         } else if (c === '\x0A' || c === '\x0D') {
44851             // cr/lf are noise chars
44852         } else {
44853             this.text += c;
44854         }
44855     },
44856     
44857     parseEscapes: function (c)
44858     {
44859         if (c === '\\' || c === '{' || c === '}') {
44860             this.text += c;
44861             this.parserState = this.parseText;
44862         } else {
44863             this.parserState = this.parseControlSymbol;
44864             this.parseControlSymbol(c);
44865         }
44866     },
44867     parseControlSymbol: function(c)
44868     {
44869         if (c === '~') {
44870             this.text += '\u00a0'; // nbsp
44871             this.parserState = this.parseText
44872         } else if (c === '-') {
44873              this.text += '\u00ad'; // soft hyphen
44874         } else if (c === '_') {
44875             this.text += '\u2011'; // non-breaking hyphen
44876         } else if (c === '*') {
44877             this.emitIgnorable();
44878             this.parserState = this.parseText;
44879         } else if (c === "'") {
44880             this.parserState = this.parseHexChar;
44881         } else if (c === '|') { // formula cacter
44882             this.emitFormula();
44883             this.parserState = this.parseText;
44884         } else if (c === ':') { // subentry in an index entry
44885             this.emitIndexSubEntry();
44886             this.parserState = this.parseText;
44887         } else if (c === '\x0a') {
44888             this.emitEndParagraph();
44889             this.parserState = this.parseText;
44890         } else if (c === '\x0d') {
44891             this.emitEndParagraph();
44892             this.parserState = this.parseText;
44893         } else {
44894             this.parserState = this.parseControlWord;
44895             this.parseControlWord(c);
44896         }
44897     },
44898     parseHexChar: function (c)
44899     {
44900         if (/^[A-Fa-f0-9]$/.test(c)) {
44901             this.hexChar += c;
44902             if (this.hexChar.length >= 2) {
44903               this.emitHexChar();
44904               this.parserState = this.parseText;
44905             }
44906             return;
44907         }
44908         this.emitError("Invalid character \"" + c + "\" in hex literal.");
44909         this.parserState = this.parseText;
44910         
44911     },
44912     parseControlWord : function(c)
44913     {
44914         if (c === ' ') {
44915             this.emitControlWord();
44916             this.parserState = this.parseText;
44917         } else if (/^[-\d]$/.test(c)) {
44918             this.parserState = this.parseControlWordParam;
44919             this.controlWordParam += c;
44920         } else if (/^[A-Za-z]$/.test(c)) {
44921           this.controlWord += c;
44922         } else {
44923           this.emitControlWord();
44924           this.parserState = this.parseText;
44925           this.parseText(c);
44926         }
44927     },
44928     parseControlWordParam : function (c) {
44929         if (/^\d$/.test(c)) {
44930           this.controlWordParam += c;
44931         } else if (c === ' ') {
44932           this.emitControlWord();
44933           this.parserState = this.parseText;
44934         } else {
44935           this.emitControlWord();
44936           this.parserState = this.parseText;
44937           this.parseText(c);
44938         }
44939     },
44940     
44941     
44942     
44943     
44944     emitText : function () {
44945         if (this.text === '') {
44946             return;
44947         }
44948         this.push({
44949             type: 'text',
44950             value: this.text,
44951             pos: this.cpos,
44952             row: this.row,
44953             col: this.col
44954         });
44955         this.text = ''
44956     },
44957     emitControlWord : function ()
44958     {
44959         this.emitText();
44960         if (this.controlWord === '') {
44961             this.emitError('empty control word');
44962         } else {
44963             this.push({
44964                   type: 'controlword',
44965                   value: this.controlWord,
44966                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
44967                   pos: this.cpos,
44968                   row: this.row,
44969                   col: this.col
44970             });
44971         }
44972         this.controlWord = '';
44973         this.controlWordParam = '';
44974     },
44975     emitStartGroup : function ()
44976     {
44977         this.emitText();
44978         this.push({
44979             type: 'groupstart',
44980             pos: this.cpos,
44981             row: this.row,
44982             col: this.col
44983         });
44984     },
44985     emitEndGroup : function ()
44986     {
44987         this.emitText();
44988         this.push({
44989             type: 'groupend',
44990             pos: this.cpos,
44991             row: this.row,
44992             col: this.col
44993         });
44994     },
44995     emitIgnorable : function ()
44996     {
44997         this.emitText();
44998         this.push({
44999             type: 'ignorable',
45000             pos: this.cpos,
45001             row: this.row,
45002             col: this.col
45003         });
45004     },
45005     emitHexChar : function ()
45006     {
45007         this.emitText();
45008         this.push({
45009             type: 'hexchar',
45010             value: this.hexChar,
45011             pos: this.cpos,
45012             row: this.row,
45013             col: this.col
45014         });
45015         this.hexChar = ''
45016     },
45017     emitError : function (message)
45018     {
45019       this.emitText();
45020       this.push({
45021             type: 'error',
45022             value: message,
45023             row: this.row,
45024             col: this.col,
45025             char: this.cpos //,
45026             //stack: new Error().stack
45027         });
45028     },
45029     emitEndParagraph : function () {
45030         this.emitText();
45031         this.push({
45032             type: 'endparagraph',
45033             pos: this.cpos,
45034             row: this.row,
45035             col: this.col
45036         });
45037     }
45038      
45039 } ;
45040 Roo.htmleditor = {};
45041  
45042 /**
45043  * @class Roo.htmleditor.Filter
45044  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45045  * @cfg {DomElement} node The node to iterate and filter
45046  * @cfg {boolean|String|Array} tag Tags to replace 
45047  * @constructor
45048  * Create a new Filter.
45049  * @param {Object} config Configuration options
45050  */
45051
45052
45053
45054 Roo.htmleditor.Filter = function(cfg) {
45055     Roo.apply(this.cfg);
45056     // this does not actually call walk as it's really just a abstract class
45057 }
45058
45059
45060 Roo.htmleditor.Filter.prototype = {
45061     
45062     node: false,
45063     
45064     tag: false,
45065
45066     // overrride to do replace comments.
45067     replaceComment : false,
45068     
45069     // overrride to do replace or do stuff with tags..
45070     replaceTag : false,
45071     
45072     walk : function(dom)
45073     {
45074         Roo.each( Array.from(dom.childNodes), function( e ) {
45075             switch(true) {
45076                 
45077                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45078                     this.replaceComment(e);
45079                     return;
45080                 
45081                 case e.nodeType != 1: //not a node.
45082                     return;
45083                 
45084                 case this.tag === true: // everything
45085                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45086                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45087                     if (this.replaceTag && false === this.replaceTag(e)) {
45088                         return;
45089                     }
45090                     if (e.hasChildNodes()) {
45091                         this.walk(e);
45092                     }
45093                     return;
45094                 
45095                 default:    // tags .. that do not match.
45096                     if (e.hasChildNodes()) {
45097                         this.walk(e);
45098                     }
45099             }
45100             
45101         }, this);
45102         
45103     }
45104 }; 
45105
45106 /**
45107  * @class Roo.htmleditor.FilterAttributes
45108  * clean attributes and  styles including http:// etc.. in attribute
45109  * @constructor
45110 * Run a new Attribute Filter
45111 * @param {Object} config Configuration options
45112  */
45113 Roo.htmleditor.FilterAttributes = function(cfg)
45114 {
45115     Roo.apply(this, cfg);
45116     this.attrib_black = this.attrib_black || [];
45117     this.attrib_white = this.attrib_white || [];
45118
45119     this.attrib_clean = this.attrib_clean || [];
45120     this.style_white = this.style_white || [];
45121     this.style_black = this.style_black || [];
45122     this.walk(cfg.node);
45123 }
45124
45125 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45126 {
45127     tag: true, // all tags
45128     
45129     attrib_black : false, // array
45130     attrib_clean : false,
45131     attrib_white : false,
45132
45133     style_white : false,
45134     style_black : false,
45135      
45136      
45137     replaceTag : function(node)
45138     {
45139         if (!node.attributes || !node.attributes.length) {
45140             return true;
45141         }
45142         
45143         for (var i = node.attributes.length-1; i > -1 ; i--) {
45144             var a = node.attributes[i];
45145             //console.log(a);
45146             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45147                 node.removeAttribute(a.name);
45148                 continue;
45149             }
45150             
45151             
45152             
45153             if (a.name.toLowerCase().substr(0,2)=='on')  {
45154                 node.removeAttribute(a.name);
45155                 continue;
45156             }
45157             
45158             
45159             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45160                 node.removeAttribute(a.name);
45161                 continue;
45162             }
45163             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45164                 this.cleanAttr(node,a.name,a.value); // fixme..
45165                 continue;
45166             }
45167             if (a.name == 'style') {
45168                 this.cleanStyle(node,a.name,a.value);
45169                 continue;
45170             }
45171             /// clean up MS crap..
45172             // tecnically this should be a list of valid class'es..
45173             
45174             
45175             if (a.name == 'class') {
45176                 if (a.value.match(/^Mso/)) {
45177                     node.removeAttribute('class');
45178                 }
45179                 
45180                 if (a.value.match(/^body$/)) {
45181                     node.removeAttribute('class');
45182                 }
45183                 continue;
45184             }
45185             
45186             
45187             // style cleanup!?
45188             // class cleanup?
45189             
45190         }
45191         return true; // clean children
45192     },
45193         
45194     cleanAttr: function(node, n,v)
45195     {
45196         
45197         if (v.match(/^\./) || v.match(/^\//)) {
45198             return;
45199         }
45200         if (v.match(/^(http|https):\/\//)
45201             || v.match(/^mailto:/) 
45202             || v.match(/^ftp:/)
45203             || v.match(/^data:/)
45204             ) {
45205             return;
45206         }
45207         if (v.match(/^#/)) {
45208             return;
45209         }
45210         if (v.match(/^\{/)) { // allow template editing.
45211             return;
45212         }
45213 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45214         node.removeAttribute(n);
45215         
45216     },
45217     cleanStyle : function(node,  n,v)
45218     {
45219         if (v.match(/expression/)) { //XSS?? should we even bother..
45220             node.removeAttribute(n);
45221             return;
45222         }
45223         
45224         var parts = v.split(/;/);
45225         var clean = [];
45226         
45227         Roo.each(parts, function(p) {
45228             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45229             if (!p.length) {
45230                 return true;
45231             }
45232             var l = p.split(':').shift().replace(/\s+/g,'');
45233             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45234             
45235             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45236                 return true;
45237             }
45238             //Roo.log()
45239             // only allow 'c whitelisted system attributes'
45240             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45241                 return true;
45242             }
45243             
45244             
45245             clean.push(p);
45246             return true;
45247         },this);
45248         if (clean.length) { 
45249             node.setAttribute(n, clean.join(';'));
45250         } else {
45251             node.removeAttribute(n);
45252         }
45253         
45254     }
45255         
45256         
45257         
45258     
45259 });/**
45260  * @class Roo.htmleditor.FilterBlack
45261  * remove blacklisted elements.
45262  * @constructor
45263  * Run a new Blacklisted Filter
45264  * @param {Object} config Configuration options
45265  */
45266
45267 Roo.htmleditor.FilterBlack = function(cfg)
45268 {
45269     Roo.apply(this, cfg);
45270     this.walk(cfg.node);
45271 }
45272
45273 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45274 {
45275     tag : true, // all elements.
45276    
45277     replace : function(n)
45278     {
45279         n.parentNode.removeChild(n);
45280     }
45281 });
45282 /**
45283  * @class Roo.htmleditor.FilterComment
45284  * remove comments.
45285  * @constructor
45286 * Run a new Comments Filter
45287 * @param {Object} config Configuration options
45288  */
45289 Roo.htmleditor.FilterComment = function(cfg)
45290 {
45291     this.walk(cfg.node);
45292 }
45293
45294 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45295 {
45296   
45297     replaceComment : function(n)
45298     {
45299         n.parentNode.removeChild(n);
45300     }
45301 });/**
45302  * @class Roo.htmleditor.FilterKeepChildren
45303  * remove tags but keep children
45304  * @constructor
45305  * Run a new Keep Children Filter
45306  * @param {Object} config Configuration options
45307  */
45308
45309 Roo.htmleditor.FilterKeepChildren = function(cfg)
45310 {
45311     Roo.apply(this, cfg);
45312     if (this.tag === false) {
45313         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45314     }
45315     this.walk(cfg.node);
45316 }
45317
45318 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45319 {
45320     
45321   
45322     replaceTag : function(node)
45323     {
45324         // walk children...
45325         //Roo.log(node);
45326         var ar = Array.from(node.childNodes);
45327         //remove first..
45328         for (var i = 0; i < ar.length; i++) {
45329             if (ar[i].nodeType == 1) {
45330                 if (
45331                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45332                     || // array and it matches
45333                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45334                 ) {
45335                     this.replaceTag(ar[i]); // child is blacklisted as well...
45336                     continue;
45337                 }
45338             }
45339         }  
45340         ar = Array.from(node.childNodes);
45341         for (var i = 0; i < ar.length; i++) {
45342          
45343             node.removeChild(ar[i]);
45344             // what if we need to walk these???
45345             node.parentNode.insertBefore(ar[i], node);
45346             if (this.tag !== false) {
45347                 this.walk(ar[i]);
45348                 
45349             }
45350         }
45351         node.parentNode.removeChild(node);
45352         return false; // don't walk children
45353         
45354         
45355     }
45356 });/**
45357  * @class Roo.htmleditor.FilterParagraph
45358  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45359  * like on 'push' to remove the <p> tags and replace them with line breaks.
45360  * @constructor
45361  * Run a new Paragraph Filter
45362  * @param {Object} config Configuration options
45363  */
45364
45365 Roo.htmleditor.FilterParagraph = function(cfg)
45366 {
45367     // no need to apply config.
45368     this.walk(cfg.node);
45369 }
45370
45371 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45372 {
45373     
45374      
45375     tag : 'P',
45376     
45377      
45378     replaceTag : function(node)
45379     {
45380         
45381         if (node.childNodes.length == 1 &&
45382             node.childNodes[0].nodeType == 3 &&
45383             node.childNodes[0].textContent.trim().length < 1
45384             ) {
45385             // remove and replace with '<BR>';
45386             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45387             return false; // no need to walk..
45388         }
45389         var ar = Array.from(node.childNodes);
45390         for (var i = 0; i < ar.length; i++) {
45391             node.removeChild(ar[i]);
45392             // what if we need to walk these???
45393             node.parentNode.insertBefore(ar[i], node);
45394         }
45395         // now what about this?
45396         // <p> &nbsp; </p>
45397         
45398         // double BR.
45399         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45400         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45401         node.parentNode.removeChild(node);
45402         
45403         return false;
45404
45405     }
45406     
45407 });/**
45408  * @class Roo.htmleditor.FilterSpan
45409  * filter span's with no attributes out..
45410  * @constructor
45411  * Run a new Span Filter
45412  * @param {Object} config Configuration options
45413  */
45414
45415 Roo.htmleditor.FilterSpan = function(cfg)
45416 {
45417     // no need to apply config.
45418     this.walk(cfg.node);
45419 }
45420
45421 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45422 {
45423      
45424     tag : 'SPAN',
45425      
45426  
45427     replaceTag : function(node)
45428     {
45429         if (node.attributes && node.attributes.length > 0) {
45430             return true; // walk if there are any.
45431         }
45432         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45433         return false;
45434      
45435     }
45436     
45437 });/**
45438  * @class Roo.htmleditor.FilterTableWidth
45439   try and remove table width data - as that frequently messes up other stuff.
45440  * 
45441  *      was cleanTableWidths.
45442  *
45443  * Quite often pasting from word etc.. results in tables with column and widths.
45444  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45445  *
45446  * @constructor
45447  * Run a new Table Filter
45448  * @param {Object} config Configuration options
45449  */
45450
45451 Roo.htmleditor.FilterTableWidth = function(cfg)
45452 {
45453     // no need to apply config.
45454     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45455     this.walk(cfg.node);
45456 }
45457
45458 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45459 {
45460      
45461      
45462     
45463     replaceTag: function(node) {
45464         
45465         
45466       
45467         if (node.hasAttribute('width')) {
45468             node.removeAttribute('width');
45469         }
45470         
45471          
45472         if (node.hasAttribute("style")) {
45473             // pretty basic...
45474             
45475             var styles = node.getAttribute("style").split(";");
45476             var nstyle = [];
45477             Roo.each(styles, function(s) {
45478                 if (!s.match(/:/)) {
45479                     return;
45480                 }
45481                 var kv = s.split(":");
45482                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45483                     return;
45484                 }
45485                 // what ever is left... we allow.
45486                 nstyle.push(s);
45487             });
45488             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45489             if (!nstyle.length) {
45490                 node.removeAttribute('style');
45491             }
45492         }
45493         
45494         return true; // continue doing children..
45495     }
45496 });/**
45497  * @class Roo.htmleditor.FilterWord
45498  * try and clean up all the mess that Word generates.
45499  * 
45500  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45501  
45502  * @constructor
45503  * Run a new Span Filter
45504  * @param {Object} config Configuration options
45505  */
45506
45507 Roo.htmleditor.FilterWord = function(cfg)
45508 {
45509     // no need to apply config.
45510     this.walk(cfg.node);
45511 }
45512
45513 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45514 {
45515     tag: true,
45516      
45517     
45518     /**
45519      * Clean up MS wordisms...
45520      */
45521     replaceTag : function(node)
45522     {
45523          
45524         // no idea what this does - span with text, replaceds with just text.
45525         if(
45526                 node.nodeName == 'SPAN' &&
45527                 !node.hasAttributes() &&
45528                 node.childNodes.length == 1 &&
45529                 node.firstChild.nodeName == "#text"  
45530         ) {
45531             var textNode = node.firstChild;
45532             node.removeChild(textNode);
45533             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45534                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45535             }
45536             node.parentNode.insertBefore(textNode, node);
45537             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45538                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45539             }
45540             
45541             node.parentNode.removeChild(node);
45542             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45543         }
45544         
45545    
45546         
45547         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45548             node.parentNode.removeChild(node);
45549             return false; // dont do chidlren
45550         }
45551         //Roo.log(node.tagName);
45552         // remove - but keep children..
45553         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45554             //Roo.log('-- removed');
45555             while (node.childNodes.length) {
45556                 var cn = node.childNodes[0];
45557                 node.removeChild(cn);
45558                 node.parentNode.insertBefore(cn, node);
45559                 // move node to parent - and clean it..
45560                 this.replaceTag(cn);
45561             }
45562             node.parentNode.removeChild(node);
45563             /// no need to iterate chidlren = it's got none..
45564             //this.iterateChildren(node, this.cleanWord);
45565             return false; // no need to iterate children.
45566         }
45567         // clean styles
45568         if (node.className.length) {
45569             
45570             var cn = node.className.split(/\W+/);
45571             var cna = [];
45572             Roo.each(cn, function(cls) {
45573                 if (cls.match(/Mso[a-zA-Z]+/)) {
45574                     return;
45575                 }
45576                 cna.push(cls);
45577             });
45578             node.className = cna.length ? cna.join(' ') : '';
45579             if (!cna.length) {
45580                 node.removeAttribute("class");
45581             }
45582         }
45583         
45584         if (node.hasAttribute("lang")) {
45585             node.removeAttribute("lang");
45586         }
45587         
45588         if (node.hasAttribute("style")) {
45589             
45590             var styles = node.getAttribute("style").split(";");
45591             var nstyle = [];
45592             Roo.each(styles, function(s) {
45593                 if (!s.match(/:/)) {
45594                     return;
45595                 }
45596                 var kv = s.split(":");
45597                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45598                     return;
45599                 }
45600                 // what ever is left... we allow.
45601                 nstyle.push(s);
45602             });
45603             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45604             if (!nstyle.length) {
45605                 node.removeAttribute('style');
45606             }
45607         }
45608         return true; // do children
45609         
45610         
45611         
45612     }
45613 });
45614 /**
45615  * @class Roo.htmleditor.FilterStyleToTag
45616  * part of the word stuff... - certain 'styles' should be converted to tags.
45617  * eg.
45618  *   font-weight: bold -> bold
45619  *   ?? super / subscrit etc..
45620  * 
45621  * @constructor
45622 * Run a new style to tag filter.
45623 * @param {Object} config Configuration options
45624  */
45625 Roo.htmleditor.FilterStyleToTag = function(cfg)
45626 {
45627     
45628     this.tags = {
45629         B  : [ 'fontWeight' , 'bold'],
45630         I :  [ 'fontStyle' , 'italic'],
45631         //pre :  [ 'font-style' , 'italic'],
45632         // h1.. h6 ?? font-size?
45633         SUP : [ 'verticalAlign' , 'super' ],
45634         SUB : [ 'verticalAlign' , 'sub' ]
45635         
45636         
45637     };
45638     
45639     Roo.apply(this, cfg);
45640      
45641     
45642     this.walk(cfg.node);
45643     
45644     
45645     
45646 }
45647
45648
45649 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45650 {
45651     tag: true, // all tags
45652     
45653     tags : false,
45654     
45655     
45656     replaceTag : function(node)
45657     {
45658         
45659         
45660         if (node.getAttribute("style") === null) {
45661             return true;
45662         }
45663         var inject = [];
45664         for (var k in this.tags) {
45665             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45666                 inject.push(k);
45667                 node.style.removeProperty(this.tags[k][0]);
45668             }
45669         }
45670         if (!inject.length) {
45671             return true; 
45672         }
45673         var cn = Array.from(node.childNodes);
45674         var nn = node;
45675         Roo.each(inject, function(t) {
45676             var nc = node.ownerDocument.createelement(t);
45677             nn.appendChild(nc);
45678             nn = nc;
45679         });
45680         for(var i = 0;i < cn.length;cn++) {
45681             node.removeChild(cn[i]);
45682             nn.appendChild(cn[i]);
45683         }
45684         return true /// iterate thru
45685     }
45686     
45687 })/**
45688  * @class Roo.htmleditor.FilterLongBr
45689  * BR/BR/BR - keep a maximum of 2...
45690  * @constructor
45691  * Run a new Long BR Filter
45692  * @param {Object} config Configuration options
45693  */
45694
45695 Roo.htmleditor.FilterLongBr = function(cfg)
45696 {
45697     // no need to apply config.
45698     this.walk(cfg.node);
45699 }
45700
45701 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
45702 {
45703     
45704      
45705     tag : 'BR',
45706     
45707      
45708     replaceTag : function(node)
45709     {
45710         
45711         var ps = node.nextSibling;
45712         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45713             ps = ps.nextSibling;
45714         }
45715         
45716         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
45717             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
45718             return false;
45719         }
45720         
45721         if (!ps || ps.nodeType != 1) {
45722             return false;
45723         }
45724         
45725         if (!ps || ps.tagName != 'BR') {
45726            
45727             return false;
45728         }
45729         
45730         
45731         
45732         
45733         
45734         if (!node.previousSibling) {
45735             return false;
45736         }
45737         var ps = node.previousSibling;
45738         
45739         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45740             ps = ps.previousSibling;
45741         }
45742         if (!ps || ps.nodeType != 1) {
45743             return false;
45744         }
45745         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
45746         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
45747             return false;
45748         }
45749         
45750         node.parentNode.removeChild(node); // remove me...
45751         
45752         return false; // no need to do children
45753
45754     }
45755     
45756 });
45757 /**
45758  * @class Roo.htmleditor.Tidy
45759  * Tidy HTML 
45760  * @cfg {Roo.HtmlEditorCore} core the editor.
45761  * @constructor
45762  * Create a new Filter.
45763  * @param {Object} config Configuration options
45764  */
45765
45766
45767 Roo.htmleditor.Tidy = function(cfg) {
45768     Roo.apply(this, cfg);
45769     
45770     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
45771      
45772 }
45773
45774 Roo.htmleditor.Tidy.toString = function(node)
45775 {
45776     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
45777 }
45778
45779 Roo.htmleditor.Tidy.prototype = {
45780     
45781     
45782     wrap : function(s) {
45783         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
45784     },
45785
45786     
45787     tidy : function(node, indent) {
45788      
45789         if  (node.nodeType == 3) {
45790             // text.
45791             
45792             
45793             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45794                 
45795             
45796         }
45797         
45798         if  (node.nodeType != 1) {
45799             return '';
45800         }
45801         
45802         
45803         
45804         if (node.tagName == 'BODY') {
45805             
45806             return this.cn(node, '');
45807         }
45808              
45809              // Prints the node tagName, such as <A>, <IMG>, etc
45810         var ret = "<" + node.tagName +  this.attr(node) ;
45811         
45812         // elements with no children..
45813         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45814                 return ret + '/>';
45815         }
45816         ret += '>';
45817         
45818         
45819         var cindent = indent === false ? '' : (indent + '  ');
45820         // tags where we will not pad the children.. (inline text tags etc..)
45821         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45822             cindent = false;
45823             
45824             
45825         }
45826         
45827         var cn = this.cn(node, cindent );
45828         
45829         return ret + cn  + '</' + node.tagName + '>';
45830         
45831     },
45832     cn: function(node, indent)
45833     {
45834         var ret = [];
45835         
45836         var ar = Array.from(node.childNodes);
45837         for (var i = 0 ; i < ar.length ; i++) {
45838             
45839             
45840             
45841             if (indent !== false   // indent==false preservies everything
45842                 && i > 0
45843                 && ar[i].nodeType == 3 
45844                 && ar[i].nodeValue.length > 0
45845                 && ar[i].nodeValue.match(/^\s+/)
45846             ) {
45847                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
45848                     ret.pop(); // remove line break from last?
45849                 }
45850                 
45851                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45852             }
45853             if (indent !== false
45854                 && ar[i].nodeType == 1 // element - and indent is not set... 
45855             ) {
45856                 ret.push("\n" + indent); 
45857             }
45858             
45859             ret.push(this.tidy(ar[i], indent));
45860             // text + trailing indent 
45861             if (indent !== false
45862                 && ar[i].nodeType == 3
45863                 && ar[i].nodeValue.length > 0
45864                 && ar[i].nodeValue.match(/\s+$/)
45865             ){
45866                 ret.push("\n" + indent); 
45867             }
45868             
45869             
45870             
45871             
45872         }
45873         // what if all text?
45874         
45875         
45876         return ret.join('');
45877     },
45878     
45879          
45880         
45881     attr : function(node)
45882     {
45883         var attr = [];
45884         for(i = 0; i < node.attributes.length;i++) {
45885             
45886             // skip empty values?
45887             if (!node.attributes.item(i).value.length) {
45888                 continue;
45889             }
45890             attr.push(  node.attributes.item(i).name + '="' +
45891                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45892             );
45893         }
45894         return attr.length ? (' ' + attr.join(' ') ) : '';
45895         
45896     }
45897     
45898     
45899     
45900 }
45901 /**
45902  * @class Roo.htmleditor.KeyEnter
45903  * Handle Enter press..
45904  * @cfg {Roo.HtmlEditorCore} core the editor.
45905  * @constructor
45906  * Create a new Filter.
45907  * @param {Object} config Configuration options
45908  */
45909
45910
45911
45912 Roo.htmleditor.KeyEnter = function(cfg) {
45913     Roo.apply(this, cfg);
45914     // this does not actually call walk as it's really just a abstract class
45915  
45916     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45917 }
45918
45919
45920 Roo.htmleditor.KeyEnter.prototype = {
45921     
45922     core : false,
45923     
45924     keypress : function(e) {
45925         if (e.charCode != 13) {
45926             return true;
45927         }
45928         e.preventDefault();
45929         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45930         var doc = this.core.doc;
45931         
45932         var docFragment = doc.createDocumentFragment();
45933     
45934         //add a new line
45935         var newEle = doc.createTextNode('\n');
45936         docFragment.appendChild(newEle);
45937     
45938     
45939         var range = this.core.win.getSelection().getRangeAt(0);
45940         var n = range.commonAncestorContainer ;
45941         while (n && n.nodeType != 1) {
45942             n  = n.parentNode;
45943         }
45944         var li = false;
45945         if (n && n.tagName == 'UL') {
45946             li = doc.createElement('LI');
45947             n.appendChild(li);
45948             
45949         }
45950         if (n && n.tagName == 'LI') {
45951             li = doc.createElement('LI');
45952             if (n.nextSibling) {
45953                 n.parentNode.insertBefore(li, n.firstSibling);
45954                 
45955             } else {
45956                 n.parentNode.appendChild(li);
45957             }
45958         }
45959         if (li) {   
45960             range = doc.createRange();
45961             range.setStartAfter(li);
45962             range.collapse(true);
45963         
45964             //make the cursor there
45965             var sel = this.core.win.getSelection();
45966             sel.removeAllRanges();
45967             sel.addRange(range);
45968             return false;
45969             
45970             
45971         }
45972         //add the br, or p, or something else
45973         newEle = doc.createElement('br');
45974         docFragment.appendChild(newEle);
45975     
45976         //make the br replace selection
45977         
45978         range.deleteContents();
45979         
45980         range.insertNode(docFragment);
45981     
45982         //create a new range
45983         range = doc.createRange();
45984         range.setStartAfter(newEle);
45985         range.collapse(true);
45986     
45987         //make the cursor there
45988         var sel = this.core.win.getSelection();
45989         sel.removeAllRanges();
45990         sel.addRange(range);
45991     
45992         return false;
45993          
45994     }
45995 };
45996      
45997 /**
45998  * @class Roo.htmleditor.Block
45999  * Base class for html editor blocks - do not use it directly .. extend it..
46000  * @cfg {DomElement} node The node to apply stuff to.
46001  * @cfg {String} friendly_name the name that appears in the context bar about this block
46002  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46003  
46004  * @constructor
46005  * Create a new Filter.
46006  * @param {Object} config Configuration options
46007  */
46008
46009 Roo.htmleditor.Block  = function(cfg)
46010 {
46011     // do nothing .. should not be called really.
46012 }
46013
46014 Roo.htmleditor.Block.factory = function(node)
46015 {
46016     
46017     var id = Roo.get(node).id;
46018     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46019         Roo.htmleditor.Block.cache[id].readElement();
46020         return Roo.htmleditor.Block.cache[id];
46021     }
46022     
46023     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
46024     if (typeof(cls) == 'undefined') {
46025         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
46026         return false;
46027     }
46028     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46029     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46030 };
46031 // question goes here... do we need to clear out this cache sometimes?
46032 // or show we make it relivant to the htmleditor.
46033 Roo.htmleditor.Block.cache = {};
46034
46035 Roo.htmleditor.Block.prototype = {
46036     
46037     node : false,
46038     
46039      // used by context menu
46040     friendly_name : 'Image with caption',
46041     
46042     context : false,
46043     /**
46044      * Update a node with values from this object
46045      * @param {DomElement} node
46046      */
46047     updateElement : function(node)
46048     {
46049         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46050     },
46051      /**
46052      * convert to plain HTML for calling insertAtCursor..
46053      */
46054     toHTML : function()
46055     {
46056         return Roo.DomHelper.markup(this.toObject());
46057     },
46058     /**
46059      * used by readEleemnt to extract data from a node
46060      * may need improving as it's pretty basic
46061      
46062      * @param {DomElement} node
46063      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46064      * @param {String} attribute (use html - for contents, or style for using next param as style)
46065      * @param {String} style the style property - eg. text-align
46066      */
46067     getVal : function(node, tag, attr, style)
46068     {
46069         var n = node;
46070         if (tag !== true && n.tagName != tag.toUpperCase()) {
46071             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46072             // but kiss for now.
46073             n = node.getElementsByTagName(tag).item(0);
46074         }
46075         if (attr == 'html') {
46076             return n.innerHTML;
46077         }
46078         if (attr == 'style') {
46079             return Roo.get(n).getStyle(style);
46080         }
46081         
46082         return Roo.get(n).attr(attr);
46083             
46084     },
46085     /**
46086      * create a DomHelper friendly object - for use with 
46087      * Roo.DomHelper.markup / overwrite / etc..
46088      * (override this)
46089      */
46090     toObject : function()
46091     {
46092         return {};
46093     },
46094       /**
46095      * Read a node that has a 'data-block' property - and extract the values from it.
46096      * @param {DomElement} node - the node
46097      */
46098     readElement : function(node)
46099     {
46100         
46101     } 
46102     
46103     
46104 };
46105
46106  
46107
46108 /**
46109  * @class Roo.htmleditor.BlockFigure
46110  * Block that has an image and a figcaption
46111  * @cfg {String} image_src the url for the image
46112  * @cfg {String} align (left|right) alignment for the block default left
46113  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46114  * @cfg {String} caption the text to appear below  (and in the alt tag)
46115  * @cfg {String|number} image_width the width of the image number or %?
46116  * @cfg {String|number} image_height the height of the image number or %?
46117  * 
46118  * @constructor
46119  * Create a new Filter.
46120  * @param {Object} config Configuration options
46121  */
46122
46123 Roo.htmleditor.BlockFigure = function(cfg)
46124 {
46125     if (cfg.node) {
46126         this.readElement(cfg.node);
46127         this.updateElement(cfg.node);
46128     }
46129     Roo.apply(this, cfg);
46130 }
46131 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46132  
46133     
46134     // setable values.
46135     image_src: '',
46136     
46137     align: 'left',
46138     caption : '',
46139     text_align: 'left',
46140     
46141     width : '46%',
46142     margin: '2%',
46143     
46144     // used by context menu
46145     friendly_name : 'Image with caption',
46146     
46147     context : { // ?? static really
46148         width : {
46149             title: "Width",
46150             width: 40
46151             // ?? number
46152         },
46153         margin : {
46154             title: "Margin",
46155             width: 40
46156             // ?? number
46157         },
46158         align: {
46159             title: "Align",
46160             opts : [[ "left"],[ "right"]],
46161             width : 80
46162             
46163         },
46164         text_align: {
46165             title: "Caption Align",
46166             opts : [ [ "left"],[ "right"],[ "center"]],
46167             width : 80
46168         },
46169         
46170        
46171         image_src : {
46172             title: "Src",
46173             width: 220
46174         }
46175     },
46176     /**
46177      * create a DomHelper friendly object - for use with
46178      * Roo.DomHelper.markup / overwrite / etc..
46179      */
46180     toObject : function()
46181     {
46182         var d = document.createElement('div');
46183         d.innerHTML = this.caption;
46184         
46185         return {
46186             tag: 'figure',
46187             'data-block' : 'Figure',
46188             contenteditable : 'false',
46189             style : {
46190                 display: 'table',
46191                 float :  this.align ,
46192                 width :  this.width,
46193                 margin:  this.margin
46194             },
46195             cn : [
46196                 {
46197                     tag : 'img',
46198                     src : this.image_src,
46199                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46200                     style: {
46201                         width: '100%'
46202                     }
46203                 },
46204                 {
46205                     tag: 'figcaption',
46206                     contenteditable : true,
46207                     style : {
46208                         'text-align': this.text_align
46209                     },
46210                     html : this.caption
46211                     
46212                 }
46213             ]
46214         };
46215     },
46216     
46217     readElement : function(node)
46218     {
46219         this.image_src = this.getVal(node, 'img', 'src');
46220         this.align = this.getVal(node, 'figure', 'style', 'float');
46221         this.caption = this.getVal(node, 'figcaption', 'html');
46222         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46223         this.width = this.getVal(node, 'figure', 'style', 'width');
46224         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46225         
46226     } 
46227     
46228   
46229    
46230      
46231     
46232     
46233     
46234     
46235 })
46236
46237 //<script type="text/javascript">
46238
46239 /*
46240  * Based  Ext JS Library 1.1.1
46241  * Copyright(c) 2006-2007, Ext JS, LLC.
46242  * LGPL
46243  *
46244  */
46245  
46246 /**
46247  * @class Roo.HtmlEditorCore
46248  * @extends Roo.Component
46249  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46250  *
46251  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46252  */
46253
46254 Roo.HtmlEditorCore = function(config){
46255     
46256     
46257     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46258     
46259     
46260     this.addEvents({
46261         /**
46262          * @event initialize
46263          * Fires when the editor is fully initialized (including the iframe)
46264          * @param {Roo.HtmlEditorCore} this
46265          */
46266         initialize: true,
46267         /**
46268          * @event activate
46269          * Fires when the editor is first receives the focus. Any insertion must wait
46270          * until after this event.
46271          * @param {Roo.HtmlEditorCore} this
46272          */
46273         activate: true,
46274          /**
46275          * @event beforesync
46276          * Fires before the textarea is updated with content from the editor iframe. Return false
46277          * to cancel the sync.
46278          * @param {Roo.HtmlEditorCore} this
46279          * @param {String} html
46280          */
46281         beforesync: true,
46282          /**
46283          * @event beforepush
46284          * Fires before the iframe editor is updated with content from the textarea. Return false
46285          * to cancel the push.
46286          * @param {Roo.HtmlEditorCore} this
46287          * @param {String} html
46288          */
46289         beforepush: true,
46290          /**
46291          * @event sync
46292          * Fires when the textarea is updated with content from the editor iframe.
46293          * @param {Roo.HtmlEditorCore} this
46294          * @param {String} html
46295          */
46296         sync: true,
46297          /**
46298          * @event push
46299          * Fires when the iframe editor is updated with content from the textarea.
46300          * @param {Roo.HtmlEditorCore} this
46301          * @param {String} html
46302          */
46303         push: true,
46304         
46305         /**
46306          * @event editorevent
46307          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46308          * @param {Roo.HtmlEditorCore} this
46309          */
46310         editorevent: true
46311         
46312     });
46313     
46314     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46315     
46316     // defaults : white / black...
46317     this.applyBlacklists();
46318     
46319     
46320     
46321 };
46322
46323
46324 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46325
46326
46327      /**
46328      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46329      */
46330     
46331     owner : false,
46332     
46333      /**
46334      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46335      *                        Roo.resizable.
46336      */
46337     resizable : false,
46338      /**
46339      * @cfg {Number} height (in pixels)
46340      */   
46341     height: 300,
46342    /**
46343      * @cfg {Number} width (in pixels)
46344      */   
46345     width: 500,
46346     
46347     /**
46348      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46349      * 
46350      */
46351     stylesheets: false,
46352     
46353     /**
46354      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46355      */
46356     allowComments: false,
46357     // id of frame..
46358     frameId: false,
46359     
46360     // private properties
46361     validationEvent : false,
46362     deferHeight: true,
46363     initialized : false,
46364     activated : false,
46365     sourceEditMode : false,
46366     onFocus : Roo.emptyFn,
46367     iframePad:3,
46368     hideMode:'offsets',
46369     
46370     clearUp: true,
46371     
46372     // blacklist + whitelisted elements..
46373     black: false,
46374     white: false,
46375      
46376     bodyCls : '',
46377
46378     /**
46379      * Protected method that will not generally be called directly. It
46380      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46381      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46382      */
46383     getDocMarkup : function(){
46384         // body styles..
46385         var st = '';
46386         
46387         // inherit styels from page...?? 
46388         if (this.stylesheets === false) {
46389             
46390             Roo.get(document.head).select('style').each(function(node) {
46391                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46392             });
46393             
46394             Roo.get(document.head).select('link').each(function(node) { 
46395                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46396             });
46397             
46398         } else if (!this.stylesheets.length) {
46399                 // simple..
46400                 st = '<style type="text/css">' +
46401                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46402                    '</style>';
46403         } else {
46404             for (var i in this.stylesheets) {
46405                 if (typeof(this.stylesheets[i]) != 'string') {
46406                     continue;
46407                 }
46408                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46409             }
46410             
46411         }
46412         
46413         st +=  '<style type="text/css">' +
46414             'IMG { cursor: pointer } ' +
46415         '</style>';
46416
46417         var cls = 'roo-htmleditor-body';
46418         
46419         if(this.bodyCls.length){
46420             cls += ' ' + this.bodyCls;
46421         }
46422         
46423         return '<html><head>' + st  +
46424             //<style type="text/css">' +
46425             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46426             //'</style>' +
46427             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46428     },
46429
46430     // private
46431     onRender : function(ct, position)
46432     {
46433         var _t = this;
46434         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46435         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46436         
46437         
46438         this.el.dom.style.border = '0 none';
46439         this.el.dom.setAttribute('tabIndex', -1);
46440         this.el.addClass('x-hidden hide');
46441         
46442         
46443         
46444         if(Roo.isIE){ // fix IE 1px bogus margin
46445             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46446         }
46447        
46448         
46449         this.frameId = Roo.id();
46450         
46451          
46452         
46453         var iframe = this.owner.wrap.createChild({
46454             tag: 'iframe',
46455             cls: 'form-control', // bootstrap..
46456             id: this.frameId,
46457             name: this.frameId,
46458             frameBorder : 'no',
46459             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46460         }, this.el
46461         );
46462         
46463         
46464         this.iframe = iframe.dom;
46465
46466         this.assignDocWin();
46467         
46468         this.doc.designMode = 'on';
46469        
46470         this.doc.open();
46471         this.doc.write(this.getDocMarkup());
46472         this.doc.close();
46473
46474         
46475         var task = { // must defer to wait for browser to be ready
46476             run : function(){
46477                 //console.log("run task?" + this.doc.readyState);
46478                 this.assignDocWin();
46479                 if(this.doc.body || this.doc.readyState == 'complete'){
46480                     try {
46481                         this.doc.designMode="on";
46482                     } catch (e) {
46483                         return;
46484                     }
46485                     Roo.TaskMgr.stop(task);
46486                     this.initEditor.defer(10, this);
46487                 }
46488             },
46489             interval : 10,
46490             duration: 10000,
46491             scope: this
46492         };
46493         Roo.TaskMgr.start(task);
46494
46495     },
46496
46497     // private
46498     onResize : function(w, h)
46499     {
46500          Roo.log('resize: ' +w + ',' + h );
46501         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46502         if(!this.iframe){
46503             return;
46504         }
46505         if(typeof w == 'number'){
46506             
46507             this.iframe.style.width = w + 'px';
46508         }
46509         if(typeof h == 'number'){
46510             
46511             this.iframe.style.height = h + 'px';
46512             if(this.doc){
46513                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46514             }
46515         }
46516         
46517     },
46518
46519     /**
46520      * Toggles the editor between standard and source edit mode.
46521      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46522      */
46523     toggleSourceEdit : function(sourceEditMode){
46524         
46525         this.sourceEditMode = sourceEditMode === true;
46526         
46527         if(this.sourceEditMode){
46528  
46529             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46530             
46531         }else{
46532             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46533             //this.iframe.className = '';
46534             this.deferFocus();
46535         }
46536         //this.setSize(this.owner.wrap.getSize());
46537         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46538     },
46539
46540     
46541   
46542
46543     /**
46544      * Protected method that will not generally be called directly. If you need/want
46545      * custom HTML cleanup, this is the method you should override.
46546      * @param {String} html The HTML to be cleaned
46547      * return {String} The cleaned HTML
46548      */
46549     cleanHtml : function(html){
46550         html = String(html);
46551         if(html.length > 5){
46552             if(Roo.isSafari){ // strip safari nonsense
46553                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46554             }
46555         }
46556         if(html == '&nbsp;'){
46557             html = '';
46558         }
46559         return html;
46560     },
46561
46562     /**
46563      * HTML Editor -> Textarea
46564      * Protected method that will not generally be called directly. Syncs the contents
46565      * of the editor iframe with the textarea.
46566      */
46567     syncValue : function()
46568     {
46569         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46570         if(this.initialized){
46571             var bd = (this.doc.body || this.doc.documentElement);
46572             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46573             
46574             // not sure if this is really the place for this
46575             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46576             // this has to update attributes that get duped.. like alt and caption..
46577             
46578             
46579             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46580             //     Roo.htmleditor.Block.factory(e);
46581             //},this);
46582             
46583             
46584             var div = document.createElement('div');
46585             div.innerHTML = bd.innerHTML;
46586             // remove content editable. (blocks)
46587             
46588            
46589             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46590             //?? tidy?
46591             var html = div.innerHTML;
46592             if(Roo.isSafari){
46593                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46594                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46595                 if(m && m[1]){
46596                     html = '<div style="'+m[0]+'">' + html + '</div>';
46597                 }
46598             }
46599             html = this.cleanHtml(html);
46600             // fix up the special chars.. normaly like back quotes in word...
46601             // however we do not want to do this with chinese..
46602             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46603                 
46604                 var cc = match.charCodeAt();
46605
46606                 // Get the character value, handling surrogate pairs
46607                 if (match.length == 2) {
46608                     // It's a surrogate pair, calculate the Unicode code point
46609                     var high = match.charCodeAt(0) - 0xD800;
46610                     var low  = match.charCodeAt(1) - 0xDC00;
46611                     cc = (high * 0x400) + low + 0x10000;
46612                 }  else if (
46613                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46614                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46615                     (cc >= 0xf900 && cc < 0xfb00 )
46616                 ) {
46617                         return match;
46618                 }  
46619          
46620                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46621                 return "&#" + cc + ";";
46622                 
46623                 
46624             });
46625             
46626             
46627              
46628             if(this.owner.fireEvent('beforesync', this, html) !== false){
46629                 this.el.dom.value = html;
46630                 this.owner.fireEvent('sync', this, html);
46631             }
46632         }
46633     },
46634
46635     /**
46636      * TEXTAREA -> EDITABLE
46637      * Protected method that will not generally be called directly. Pushes the value of the textarea
46638      * into the iframe editor.
46639      */
46640     pushValue : function()
46641     {
46642         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46643         if(this.initialized){
46644             var v = this.el.dom.value.trim();
46645             
46646             
46647             if(this.owner.fireEvent('beforepush', this, v) !== false){
46648                 var d = (this.doc.body || this.doc.documentElement);
46649                 d.innerHTML = v;
46650                  
46651                 this.el.dom.value = d.innerHTML;
46652                 this.owner.fireEvent('push', this, v);
46653             }
46654             
46655             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46656                 
46657                 Roo.htmleditor.Block.factory(e);
46658                 
46659             },this);
46660             var lc = this.doc.body.lastChild;
46661             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46662                 // add an extra line at the end.
46663                 this.doc.body.appendChild(this.doc.createElement('br'));
46664             }
46665             
46666             
46667         }
46668     },
46669
46670     // private
46671     deferFocus : function(){
46672         this.focus.defer(10, this);
46673     },
46674
46675     // doc'ed in Field
46676     focus : function(){
46677         if(this.win && !this.sourceEditMode){
46678             this.win.focus();
46679         }else{
46680             this.el.focus();
46681         }
46682     },
46683     
46684     assignDocWin: function()
46685     {
46686         var iframe = this.iframe;
46687         
46688          if(Roo.isIE){
46689             this.doc = iframe.contentWindow.document;
46690             this.win = iframe.contentWindow;
46691         } else {
46692 //            if (!Roo.get(this.frameId)) {
46693 //                return;
46694 //            }
46695 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46696 //            this.win = Roo.get(this.frameId).dom.contentWindow;
46697             
46698             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
46699                 return;
46700             }
46701             
46702             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46703             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
46704         }
46705     },
46706     
46707     // private
46708     initEditor : function(){
46709         //console.log("INIT EDITOR");
46710         this.assignDocWin();
46711         
46712         
46713         
46714         this.doc.designMode="on";
46715         this.doc.open();
46716         this.doc.write(this.getDocMarkup());
46717         this.doc.close();
46718         
46719         var dbody = (this.doc.body || this.doc.documentElement);
46720         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
46721         // this copies styles from the containing element into thsi one..
46722         // not sure why we need all of this..
46723         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
46724         
46725         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
46726         //ss['background-attachment'] = 'fixed'; // w3c
46727         dbody.bgProperties = 'fixed'; // ie
46728         //Roo.DomHelper.applyStyles(dbody, ss);
46729         Roo.EventManager.on(this.doc, {
46730             //'mousedown': this.onEditorEvent,
46731             'mouseup': this.onEditorEvent,
46732             'dblclick': this.onEditorEvent,
46733             'click': this.onEditorEvent,
46734             'keyup': this.onEditorEvent,
46735             
46736             buffer:100,
46737             scope: this
46738         });
46739         Roo.EventManager.on(this.doc, {
46740             'paste': this.onPasteEvent,
46741             scope : this
46742         });
46743         if(Roo.isGecko){
46744             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
46745         }
46746         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
46747             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
46748         }
46749         this.initialized = true;
46750
46751         
46752         // initialize special key events - enter
46753         new Roo.htmleditor.KeyEnter({core : this});
46754         
46755          
46756         
46757         this.owner.fireEvent('initialize', this);
46758         this.pushValue();
46759     },
46760     
46761     onPasteEvent : function(e,v)
46762     {
46763         // I think we better assume paste is going to be a dirty load of rubish from word..
46764         
46765         // even pasting into a 'email version' of this widget will have to clean up that mess.
46766         var cd = (e.browserEvent.clipboardData || window.clipboardData);
46767         
46768         // check what type of paste - if it's an image, then handle it differently.
46769         if (cd.files.length > 0) {
46770             // pasting images?
46771             var urlAPI = (window.createObjectURL && window) || 
46772                 (window.URL && URL.revokeObjectURL && URL) || 
46773                 (window.webkitURL && webkitURL);
46774     
46775             var url = urlAPI.createObjectURL( cd.files[0]);
46776             this.insertAtCursor('<img src=" + url + ">');
46777             return false;
46778         }
46779         
46780         var html = cd.getData('text/html'); // clipboard event
46781         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
46782         var images = parser.doc.getElementsByType('pict');
46783         Roo.log(images);
46784         //Roo.log(imgs);
46785         // fixme..
46786         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
46787                        .map(function(g) { return g.toDataURL(); });
46788         
46789         
46790         html = this.cleanWordChars(html);
46791         
46792         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
46793         
46794         if (images.length > 0) {
46795             Roo.each(d.getElementsByTagName('img'), function(img, i) {
46796                 img.setAttribute('src', images[i]);
46797             });
46798         }
46799         
46800       
46801         new Roo.htmleditor.FilterStyleToTag({ node : d });
46802         new Roo.htmleditor.FilterAttributes({
46803             node : d,
46804             attrib_white : ['href', 'src', 'name', 'align'],
46805             attrib_clean : ['href', 'src' ] 
46806         });
46807         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
46808         // should be fonts..
46809         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
46810         new Roo.htmleditor.FilterParagraph({ node : d });
46811         new Roo.htmleditor.FilterSpan({ node : d });
46812         new Roo.htmleditor.FilterLongBr({ node : d });
46813         
46814         
46815         
46816         this.insertAtCursor(d.innerHTML);
46817         
46818         e.preventDefault();
46819         return false;
46820         // default behaveiour should be our local cleanup paste? (optional?)
46821         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
46822         //this.owner.fireEvent('paste', e, v);
46823     },
46824     // private
46825     onDestroy : function(){
46826         
46827         
46828         
46829         if(this.rendered){
46830             
46831             //for (var i =0; i < this.toolbars.length;i++) {
46832             //    // fixme - ask toolbars for heights?
46833             //    this.toolbars[i].onDestroy();
46834            // }
46835             
46836             //this.wrap.dom.innerHTML = '';
46837             //this.wrap.remove();
46838         }
46839     },
46840
46841     // private
46842     onFirstFocus : function(){
46843         
46844         this.assignDocWin();
46845         
46846         
46847         this.activated = true;
46848          
46849     
46850         if(Roo.isGecko){ // prevent silly gecko errors
46851             this.win.focus();
46852             var s = this.win.getSelection();
46853             if(!s.focusNode || s.focusNode.nodeType != 3){
46854                 var r = s.getRangeAt(0);
46855                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
46856                 r.collapse(true);
46857                 this.deferFocus();
46858             }
46859             try{
46860                 this.execCmd('useCSS', true);
46861                 this.execCmd('styleWithCSS', false);
46862             }catch(e){}
46863         }
46864         this.owner.fireEvent('activate', this);
46865     },
46866
46867     // private
46868     adjustFont: function(btn){
46869         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
46870         //if(Roo.isSafari){ // safari
46871         //    adjust *= 2;
46872        // }
46873         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
46874         if(Roo.isSafari){ // safari
46875             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
46876             v =  (v < 10) ? 10 : v;
46877             v =  (v > 48) ? 48 : v;
46878             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
46879             
46880         }
46881         
46882         
46883         v = Math.max(1, v+adjust);
46884         
46885         this.execCmd('FontSize', v  );
46886     },
46887
46888     onEditorEvent : function(e)
46889     {
46890         this.owner.fireEvent('editorevent', this, e);
46891       //  this.updateToolbar();
46892         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
46893     },
46894
46895     insertTag : function(tg)
46896     {
46897         // could be a bit smarter... -> wrap the current selected tRoo..
46898         if (tg.toLowerCase() == 'span' ||
46899             tg.toLowerCase() == 'code' ||
46900             tg.toLowerCase() == 'sup' ||
46901             tg.toLowerCase() == 'sub' 
46902             ) {
46903             
46904             range = this.createRange(this.getSelection());
46905             var wrappingNode = this.doc.createElement(tg.toLowerCase());
46906             wrappingNode.appendChild(range.extractContents());
46907             range.insertNode(wrappingNode);
46908
46909             return;
46910             
46911             
46912             
46913         }
46914         this.execCmd("formatblock",   tg);
46915         
46916     },
46917     
46918     insertText : function(txt)
46919     {
46920         
46921         
46922         var range = this.createRange();
46923         range.deleteContents();
46924                //alert(Sender.getAttribute('label'));
46925                
46926         range.insertNode(this.doc.createTextNode(txt));
46927     } ,
46928     
46929      
46930
46931     /**
46932      * Executes a Midas editor command on the editor document and performs necessary focus and
46933      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
46934      * @param {String} cmd The Midas command
46935      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46936      */
46937     relayCmd : function(cmd, value){
46938         this.win.focus();
46939         this.execCmd(cmd, value);
46940         this.owner.fireEvent('editorevent', this);
46941         //this.updateToolbar();
46942         this.owner.deferFocus();
46943     },
46944
46945     /**
46946      * Executes a Midas editor command directly on the editor document.
46947      * For visual commands, you should use {@link #relayCmd} instead.
46948      * <b>This should only be called after the editor is initialized.</b>
46949      * @param {String} cmd The Midas command
46950      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46951      */
46952     execCmd : function(cmd, value){
46953         this.doc.execCommand(cmd, false, value === undefined ? null : value);
46954         this.syncValue();
46955     },
46956  
46957  
46958    
46959     /**
46960      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
46961      * to insert tRoo.
46962      * @param {String} text | dom node.. 
46963      */
46964     insertAtCursor : function(text)
46965     {
46966         
46967         if(!this.activated){
46968             return;
46969         }
46970          
46971         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
46972             this.win.focus();
46973             
46974             
46975             // from jquery ui (MIT licenced)
46976             var range, node;
46977             var win = this.win;
46978             
46979             if (win.getSelection && win.getSelection().getRangeAt) {
46980                 
46981                 // delete the existing?
46982                 
46983                 this.createRange(this.getSelection()).deleteContents();
46984                 range = win.getSelection().getRangeAt(0);
46985                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
46986                 range.insertNode(node);
46987             } else if (win.document.selection && win.document.selection.createRange) {
46988                 // no firefox support
46989                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46990                 win.document.selection.createRange().pasteHTML(txt);
46991             } else {
46992                 // no firefox support
46993                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46994                 this.execCmd('InsertHTML', txt);
46995             } 
46996             
46997             this.syncValue();
46998             
46999             this.deferFocus();
47000         }
47001     },
47002  // private
47003     mozKeyPress : function(e){
47004         if(e.ctrlKey){
47005             var c = e.getCharCode(), cmd;
47006           
47007             if(c > 0){
47008                 c = String.fromCharCode(c).toLowerCase();
47009                 switch(c){
47010                     case 'b':
47011                         cmd = 'bold';
47012                         break;
47013                     case 'i':
47014                         cmd = 'italic';
47015                         break;
47016                     
47017                     case 'u':
47018                         cmd = 'underline';
47019                         break;
47020                     
47021                     //case 'v':
47022                       //  this.cleanUpPaste.defer(100, this);
47023                       //  return;
47024                         
47025                 }
47026                 if(cmd){
47027                     this.win.focus();
47028                     this.execCmd(cmd);
47029                     this.deferFocus();
47030                     e.preventDefault();
47031                 }
47032                 
47033             }
47034         }
47035     },
47036
47037     // private
47038     fixKeys : function(){ // load time branching for fastest keydown performance
47039         if(Roo.isIE){
47040             return function(e){
47041                 var k = e.getKey(), r;
47042                 if(k == e.TAB){
47043                     e.stopEvent();
47044                     r = this.doc.selection.createRange();
47045                     if(r){
47046                         r.collapse(true);
47047                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47048                         this.deferFocus();
47049                     }
47050                     return;
47051                 }
47052                 
47053                 if(k == e.ENTER){
47054                     r = this.doc.selection.createRange();
47055                     if(r){
47056                         var target = r.parentElement();
47057                         if(!target || target.tagName.toLowerCase() != 'li'){
47058                             e.stopEvent();
47059                             r.pasteHTML('<br/>');
47060                             r.collapse(false);
47061                             r.select();
47062                         }
47063                     }
47064                 }
47065                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47066                 //    this.cleanUpPaste.defer(100, this);
47067                 //    return;
47068                 //}
47069                 
47070                 
47071             };
47072         }else if(Roo.isOpera){
47073             return function(e){
47074                 var k = e.getKey();
47075                 if(k == e.TAB){
47076                     e.stopEvent();
47077                     this.win.focus();
47078                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47079                     this.deferFocus();
47080                 }
47081                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47082                 //    this.cleanUpPaste.defer(100, this);
47083                  //   return;
47084                 //}
47085                 
47086             };
47087         }else if(Roo.isSafari){
47088             return function(e){
47089                 var k = e.getKey();
47090                 
47091                 if(k == e.TAB){
47092                     e.stopEvent();
47093                     this.execCmd('InsertText','\t');
47094                     this.deferFocus();
47095                     return;
47096                 }
47097                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47098                  //   this.cleanUpPaste.defer(100, this);
47099                  //   return;
47100                // }
47101                 
47102              };
47103         }
47104     }(),
47105     
47106     getAllAncestors: function()
47107     {
47108         var p = this.getSelectedNode();
47109         var a = [];
47110         if (!p) {
47111             a.push(p); // push blank onto stack..
47112             p = this.getParentElement();
47113         }
47114         
47115         
47116         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47117             a.push(p);
47118             p = p.parentNode;
47119         }
47120         a.push(this.doc.body);
47121         return a;
47122     },
47123     lastSel : false,
47124     lastSelNode : false,
47125     
47126     
47127     getSelection : function() 
47128     {
47129         this.assignDocWin();
47130         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47131     },
47132     /**
47133      * Select a dom node
47134      * @param {DomElement} node the node to select
47135      */
47136     selectNode : function(node)
47137     {
47138         
47139             var nodeRange = node.ownerDocument.createRange();
47140             try {
47141                 nodeRange.selectNode(node);
47142             } catch (e) {
47143                 nodeRange.selectNodeContents(node);
47144             }
47145             //nodeRange.collapse(true);
47146             var s = this.win.getSelection();
47147             s.removeAllRanges();
47148             s.addRange(nodeRange);
47149     },
47150     
47151     getSelectedNode: function() 
47152     {
47153         // this may only work on Gecko!!!
47154         
47155         // should we cache this!!!!
47156         
47157         
47158         
47159          
47160         var range = this.createRange(this.getSelection()).cloneRange();
47161         
47162         if (Roo.isIE) {
47163             var parent = range.parentElement();
47164             while (true) {
47165                 var testRange = range.duplicate();
47166                 testRange.moveToElementText(parent);
47167                 if (testRange.inRange(range)) {
47168                     break;
47169                 }
47170                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47171                     break;
47172                 }
47173                 parent = parent.parentElement;
47174             }
47175             return parent;
47176         }
47177         
47178         // is ancestor a text element.
47179         var ac =  range.commonAncestorContainer;
47180         if (ac.nodeType == 3) {
47181             ac = ac.parentNode;
47182         }
47183         
47184         var ar = ac.childNodes;
47185          
47186         var nodes = [];
47187         var other_nodes = [];
47188         var has_other_nodes = false;
47189         for (var i=0;i<ar.length;i++) {
47190             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47191                 continue;
47192             }
47193             // fullly contained node.
47194             
47195             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47196                 nodes.push(ar[i]);
47197                 continue;
47198             }
47199             
47200             // probably selected..
47201             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47202                 other_nodes.push(ar[i]);
47203                 continue;
47204             }
47205             // outer..
47206             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47207                 continue;
47208             }
47209             
47210             
47211             has_other_nodes = true;
47212         }
47213         if (!nodes.length && other_nodes.length) {
47214             nodes= other_nodes;
47215         }
47216         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47217             return false;
47218         }
47219         
47220         return nodes[0];
47221     },
47222     createRange: function(sel)
47223     {
47224         // this has strange effects when using with 
47225         // top toolbar - not sure if it's a great idea.
47226         //this.editor.contentWindow.focus();
47227         if (typeof sel != "undefined") {
47228             try {
47229                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47230             } catch(e) {
47231                 return this.doc.createRange();
47232             }
47233         } else {
47234             return this.doc.createRange();
47235         }
47236     },
47237     getParentElement: function()
47238     {
47239         
47240         this.assignDocWin();
47241         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47242         
47243         var range = this.createRange(sel);
47244          
47245         try {
47246             var p = range.commonAncestorContainer;
47247             while (p.nodeType == 3) { // text node
47248                 p = p.parentNode;
47249             }
47250             return p;
47251         } catch (e) {
47252             return null;
47253         }
47254     
47255     },
47256     /***
47257      *
47258      * Range intersection.. the hard stuff...
47259      *  '-1' = before
47260      *  '0' = hits..
47261      *  '1' = after.
47262      *         [ -- selected range --- ]
47263      *   [fail]                        [fail]
47264      *
47265      *    basically..
47266      *      if end is before start or  hits it. fail.
47267      *      if start is after end or hits it fail.
47268      *
47269      *   if either hits (but other is outside. - then it's not 
47270      *   
47271      *    
47272      **/
47273     
47274     
47275     // @see http://www.thismuchiknow.co.uk/?p=64.
47276     rangeIntersectsNode : function(range, node)
47277     {
47278         var nodeRange = node.ownerDocument.createRange();
47279         try {
47280             nodeRange.selectNode(node);
47281         } catch (e) {
47282             nodeRange.selectNodeContents(node);
47283         }
47284     
47285         var rangeStartRange = range.cloneRange();
47286         rangeStartRange.collapse(true);
47287     
47288         var rangeEndRange = range.cloneRange();
47289         rangeEndRange.collapse(false);
47290     
47291         var nodeStartRange = nodeRange.cloneRange();
47292         nodeStartRange.collapse(true);
47293     
47294         var nodeEndRange = nodeRange.cloneRange();
47295         nodeEndRange.collapse(false);
47296     
47297         return rangeStartRange.compareBoundaryPoints(
47298                  Range.START_TO_START, nodeEndRange) == -1 &&
47299                rangeEndRange.compareBoundaryPoints(
47300                  Range.START_TO_START, nodeStartRange) == 1;
47301         
47302          
47303     },
47304     rangeCompareNode : function(range, node)
47305     {
47306         var nodeRange = node.ownerDocument.createRange();
47307         try {
47308             nodeRange.selectNode(node);
47309         } catch (e) {
47310             nodeRange.selectNodeContents(node);
47311         }
47312         
47313         
47314         range.collapse(true);
47315     
47316         nodeRange.collapse(true);
47317      
47318         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47319         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47320          
47321         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47322         
47323         var nodeIsBefore   =  ss == 1;
47324         var nodeIsAfter    = ee == -1;
47325         
47326         if (nodeIsBefore && nodeIsAfter) {
47327             return 0; // outer
47328         }
47329         if (!nodeIsBefore && nodeIsAfter) {
47330             return 1; //right trailed.
47331         }
47332         
47333         if (nodeIsBefore && !nodeIsAfter) {
47334             return 2;  // left trailed.
47335         }
47336         // fully contined.
47337         return 3;
47338     },
47339  
47340     cleanWordChars : function(input) {// change the chars to hex code
47341         
47342        var swapCodes  = [ 
47343             [    8211, "&#8211;" ], 
47344             [    8212, "&#8212;" ], 
47345             [    8216,  "'" ],  
47346             [    8217, "'" ],  
47347             [    8220, '"' ],  
47348             [    8221, '"' ],  
47349             [    8226, "*" ],  
47350             [    8230, "..." ]
47351         ]; 
47352         var output = input;
47353         Roo.each(swapCodes, function(sw) { 
47354             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47355             
47356             output = output.replace(swapper, sw[1]);
47357         });
47358         
47359         return output;
47360     },
47361     
47362      
47363     
47364         
47365     
47366     cleanUpChild : function (node)
47367     {
47368         
47369         new Roo.htmleditor.FilterComment({node : node});
47370         new Roo.htmleditor.FilterAttributes({
47371                 node : node,
47372                 attrib_black : this.ablack,
47373                 attrib_clean : this.aclean,
47374                 style_white : this.cwhite,
47375                 style_black : this.cblack
47376         });
47377         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47378         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47379          
47380         
47381     },
47382     
47383     /**
47384      * Clean up MS wordisms...
47385      * @deprecated - use filter directly
47386      */
47387     cleanWord : function(node)
47388     {
47389         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47390         
47391     },
47392    
47393     
47394     /**
47395
47396      * @deprecated - use filters
47397      */
47398     cleanTableWidths : function(node)
47399     {
47400         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47401         
47402  
47403     },
47404     
47405      
47406         
47407     applyBlacklists : function()
47408     {
47409         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47410         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47411         
47412         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47413         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47414         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47415         
47416         this.white = [];
47417         this.black = [];
47418         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47419             if (b.indexOf(tag) > -1) {
47420                 return;
47421             }
47422             this.white.push(tag);
47423             
47424         }, this);
47425         
47426         Roo.each(w, function(tag) {
47427             if (b.indexOf(tag) > -1) {
47428                 return;
47429             }
47430             if (this.white.indexOf(tag) > -1) {
47431                 return;
47432             }
47433             this.white.push(tag);
47434             
47435         }, this);
47436         
47437         
47438         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47439             if (w.indexOf(tag) > -1) {
47440                 return;
47441             }
47442             this.black.push(tag);
47443             
47444         }, this);
47445         
47446         Roo.each(b, function(tag) {
47447             if (w.indexOf(tag) > -1) {
47448                 return;
47449             }
47450             if (this.black.indexOf(tag) > -1) {
47451                 return;
47452             }
47453             this.black.push(tag);
47454             
47455         }, this);
47456         
47457         
47458         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47459         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47460         
47461         this.cwhite = [];
47462         this.cblack = [];
47463         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47464             if (b.indexOf(tag) > -1) {
47465                 return;
47466             }
47467             this.cwhite.push(tag);
47468             
47469         }, this);
47470         
47471         Roo.each(w, function(tag) {
47472             if (b.indexOf(tag) > -1) {
47473                 return;
47474             }
47475             if (this.cwhite.indexOf(tag) > -1) {
47476                 return;
47477             }
47478             this.cwhite.push(tag);
47479             
47480         }, this);
47481         
47482         
47483         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47484             if (w.indexOf(tag) > -1) {
47485                 return;
47486             }
47487             this.cblack.push(tag);
47488             
47489         }, this);
47490         
47491         Roo.each(b, function(tag) {
47492             if (w.indexOf(tag) > -1) {
47493                 return;
47494             }
47495             if (this.cblack.indexOf(tag) > -1) {
47496                 return;
47497             }
47498             this.cblack.push(tag);
47499             
47500         }, this);
47501     },
47502     
47503     setStylesheets : function(stylesheets)
47504     {
47505         if(typeof(stylesheets) == 'string'){
47506             Roo.get(this.iframe.contentDocument.head).createChild({
47507                 tag : 'link',
47508                 rel : 'stylesheet',
47509                 type : 'text/css',
47510                 href : stylesheets
47511             });
47512             
47513             return;
47514         }
47515         var _this = this;
47516      
47517         Roo.each(stylesheets, function(s) {
47518             if(!s.length){
47519                 return;
47520             }
47521             
47522             Roo.get(_this.iframe.contentDocument.head).createChild({
47523                 tag : 'link',
47524                 rel : 'stylesheet',
47525                 type : 'text/css',
47526                 href : s
47527             });
47528         });
47529
47530         
47531     },
47532     
47533     removeStylesheets : function()
47534     {
47535         var _this = this;
47536         
47537         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47538             s.remove();
47539         });
47540     },
47541     
47542     setStyle : function(style)
47543     {
47544         Roo.get(this.iframe.contentDocument.head).createChild({
47545             tag : 'style',
47546             type : 'text/css',
47547             html : style
47548         });
47549
47550         return;
47551     }
47552     
47553     // hide stuff that is not compatible
47554     /**
47555      * @event blur
47556      * @hide
47557      */
47558     /**
47559      * @event change
47560      * @hide
47561      */
47562     /**
47563      * @event focus
47564      * @hide
47565      */
47566     /**
47567      * @event specialkey
47568      * @hide
47569      */
47570     /**
47571      * @cfg {String} fieldClass @hide
47572      */
47573     /**
47574      * @cfg {String} focusClass @hide
47575      */
47576     /**
47577      * @cfg {String} autoCreate @hide
47578      */
47579     /**
47580      * @cfg {String} inputType @hide
47581      */
47582     /**
47583      * @cfg {String} invalidClass @hide
47584      */
47585     /**
47586      * @cfg {String} invalidText @hide
47587      */
47588     /**
47589      * @cfg {String} msgFx @hide
47590      */
47591     /**
47592      * @cfg {String} validateOnBlur @hide
47593      */
47594 });
47595
47596 Roo.HtmlEditorCore.white = [
47597         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47598         
47599        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47600        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47601        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47602        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47603        'TABLE',   'UL',         'XMP', 
47604        
47605        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47606       'THEAD',   'TR', 
47607      
47608       'DIR', 'MENU', 'OL', 'UL', 'DL',
47609        
47610       'EMBED',  'OBJECT'
47611 ];
47612
47613
47614 Roo.HtmlEditorCore.black = [
47615     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47616         'APPLET', // 
47617         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47618         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47619         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47620         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47621         //'FONT' // CLEAN LATER..
47622         'COLGROUP', 'COL'  // messy tables.
47623         
47624 ];
47625 Roo.HtmlEditorCore.clean = [ // ?? needed???
47626      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47627 ];
47628 Roo.HtmlEditorCore.tag_remove = [
47629     'FONT', 'TBODY'  
47630 ];
47631 // attributes..
47632
47633 Roo.HtmlEditorCore.ablack = [
47634     'on'
47635 ];
47636     
47637 Roo.HtmlEditorCore.aclean = [ 
47638     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47639 ];
47640
47641 // protocols..
47642 Roo.HtmlEditorCore.pwhite= [
47643         'http',  'https',  'mailto'
47644 ];
47645
47646 // white listed style attributes.
47647 Roo.HtmlEditorCore.cwhite= [
47648       //  'text-align', /// default is to allow most things..
47649       
47650          
47651 //        'font-size'//??
47652 ];
47653
47654 // black listed style attributes.
47655 Roo.HtmlEditorCore.cblack= [
47656       //  'font-size' -- this can be set by the project 
47657 ];
47658
47659
47660
47661
47662     //<script type="text/javascript">
47663
47664 /*
47665  * Ext JS Library 1.1.1
47666  * Copyright(c) 2006-2007, Ext JS, LLC.
47667  * Licence LGPL
47668  * 
47669  */
47670  
47671  
47672 Roo.form.HtmlEditor = function(config){
47673     
47674     
47675     
47676     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
47677     
47678     if (!this.toolbars) {
47679         this.toolbars = [];
47680     }
47681     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
47682     
47683     
47684 };
47685
47686 /**
47687  * @class Roo.form.HtmlEditor
47688  * @extends Roo.form.Field
47689  * Provides a lightweight HTML Editor component.
47690  *
47691  * This has been tested on Fireforx / Chrome.. IE may not be so great..
47692  * 
47693  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
47694  * supported by this editor.</b><br/><br/>
47695  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
47696  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47697  */
47698 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
47699     /**
47700      * @cfg {Boolean} clearUp
47701      */
47702     clearUp : true,
47703       /**
47704      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
47705      */
47706     toolbars : false,
47707    
47708      /**
47709      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47710      *                        Roo.resizable.
47711      */
47712     resizable : false,
47713      /**
47714      * @cfg {Number} height (in pixels)
47715      */   
47716     height: 300,
47717    /**
47718      * @cfg {Number} width (in pixels)
47719      */   
47720     width: 500,
47721     
47722     /**
47723      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
47724      * 
47725      */
47726     stylesheets: false,
47727     
47728     
47729      /**
47730      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
47731      * 
47732      */
47733     cblack: false,
47734     /**
47735      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
47736      * 
47737      */
47738     cwhite: false,
47739     
47740      /**
47741      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
47742      * 
47743      */
47744     black: false,
47745     /**
47746      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
47747      * 
47748      */
47749     white: false,
47750     /**
47751      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
47752      */
47753     allowComments: false,
47754     /**
47755      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
47756      */
47757     
47758     
47759      bodyCls : '',
47760     
47761     // id of frame..
47762     frameId: false,
47763     
47764     // private properties
47765     validationEvent : false,
47766     deferHeight: true,
47767     initialized : false,
47768     activated : false,
47769     
47770     onFocus : Roo.emptyFn,
47771     iframePad:3,
47772     hideMode:'offsets',
47773     
47774     actionMode : 'container', // defaults to hiding it...
47775     
47776     defaultAutoCreate : { // modified by initCompnoent..
47777         tag: "textarea",
47778         style:"width:500px;height:300px;",
47779         autocomplete: "new-password"
47780     },
47781
47782     // private
47783     initComponent : function(){
47784         this.addEvents({
47785             /**
47786              * @event initialize
47787              * Fires when the editor is fully initialized (including the iframe)
47788              * @param {HtmlEditor} this
47789              */
47790             initialize: true,
47791             /**
47792              * @event activate
47793              * Fires when the editor is first receives the focus. Any insertion must wait
47794              * until after this event.
47795              * @param {HtmlEditor} this
47796              */
47797             activate: true,
47798              /**
47799              * @event beforesync
47800              * Fires before the textarea is updated with content from the editor iframe. Return false
47801              * to cancel the sync.
47802              * @param {HtmlEditor} this
47803              * @param {String} html
47804              */
47805             beforesync: true,
47806              /**
47807              * @event beforepush
47808              * Fires before the iframe editor is updated with content from the textarea. Return false
47809              * to cancel the push.
47810              * @param {HtmlEditor} this
47811              * @param {String} html
47812              */
47813             beforepush: true,
47814              /**
47815              * @event sync
47816              * Fires when the textarea is updated with content from the editor iframe.
47817              * @param {HtmlEditor} this
47818              * @param {String} html
47819              */
47820             sync: true,
47821              /**
47822              * @event push
47823              * Fires when the iframe editor is updated with content from the textarea.
47824              * @param {HtmlEditor} this
47825              * @param {String} html
47826              */
47827             push: true,
47828              /**
47829              * @event editmodechange
47830              * Fires when the editor switches edit modes
47831              * @param {HtmlEditor} this
47832              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
47833              */
47834             editmodechange: true,
47835             /**
47836              * @event editorevent
47837              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47838              * @param {HtmlEditor} this
47839              */
47840             editorevent: true,
47841             /**
47842              * @event firstfocus
47843              * Fires when on first focus - needed by toolbars..
47844              * @param {HtmlEditor} this
47845              */
47846             firstfocus: true,
47847             /**
47848              * @event autosave
47849              * Auto save the htmlEditor value as a file into Events
47850              * @param {HtmlEditor} this
47851              */
47852             autosave: true,
47853             /**
47854              * @event savedpreview
47855              * preview the saved version of htmlEditor
47856              * @param {HtmlEditor} this
47857              */
47858             savedpreview: true,
47859             
47860             /**
47861             * @event stylesheetsclick
47862             * Fires when press the Sytlesheets button
47863             * @param {Roo.HtmlEditorCore} this
47864             */
47865             stylesheetsclick: true,
47866             /**
47867             * @event paste
47868             * Fires when press user pastes into the editor
47869             * @param {Roo.HtmlEditorCore} this
47870             */
47871             paste: true 
47872         });
47873         this.defaultAutoCreate =  {
47874             tag: "textarea",
47875             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
47876             autocomplete: "new-password"
47877         };
47878     },
47879
47880     /**
47881      * Protected method that will not generally be called directly. It
47882      * is called when the editor creates its toolbar. Override this method if you need to
47883      * add custom toolbar buttons.
47884      * @param {HtmlEditor} editor
47885      */
47886     createToolbar : function(editor){
47887         Roo.log("create toolbars");
47888         if (!editor.toolbars || !editor.toolbars.length) {
47889             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
47890         }
47891         
47892         for (var i =0 ; i < editor.toolbars.length;i++) {
47893             editor.toolbars[i] = Roo.factory(
47894                     typeof(editor.toolbars[i]) == 'string' ?
47895                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
47896                 Roo.form.HtmlEditor);
47897             editor.toolbars[i].init(editor);
47898         }
47899          
47900         
47901     },
47902
47903      
47904     // private
47905     onRender : function(ct, position)
47906     {
47907         var _t = this;
47908         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
47909         
47910         this.wrap = this.el.wrap({
47911             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
47912         });
47913         
47914         this.editorcore.onRender(ct, position);
47915          
47916         if (this.resizable) {
47917             this.resizeEl = new Roo.Resizable(this.wrap, {
47918                 pinned : true,
47919                 wrap: true,
47920                 dynamic : true,
47921                 minHeight : this.height,
47922                 height: this.height,
47923                 handles : this.resizable,
47924                 width: this.width,
47925                 listeners : {
47926                     resize : function(r, w, h) {
47927                         _t.onResize(w,h); // -something
47928                     }
47929                 }
47930             });
47931             
47932         }
47933         this.createToolbar(this);
47934        
47935         
47936         if(!this.width){
47937             this.setSize(this.wrap.getSize());
47938         }
47939         if (this.resizeEl) {
47940             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
47941             // should trigger onReize..
47942         }
47943         
47944         this.keyNav = new Roo.KeyNav(this.el, {
47945             
47946             "tab" : function(e){
47947                 e.preventDefault();
47948                 
47949                 var value = this.getValue();
47950                 
47951                 var start = this.el.dom.selectionStart;
47952                 var end = this.el.dom.selectionEnd;
47953                 
47954                 if(!e.shiftKey){
47955                     
47956                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
47957                     this.el.dom.setSelectionRange(end + 1, end + 1);
47958                     return;
47959                 }
47960                 
47961                 var f = value.substring(0, start).split("\t");
47962                 
47963                 if(f.pop().length != 0){
47964                     return;
47965                 }
47966                 
47967                 this.setValue(f.join("\t") + value.substring(end));
47968                 this.el.dom.setSelectionRange(start - 1, start - 1);
47969                 
47970             },
47971             
47972             "home" : function(e){
47973                 e.preventDefault();
47974                 
47975                 var curr = this.el.dom.selectionStart;
47976                 var lines = this.getValue().split("\n");
47977                 
47978                 if(!lines.length){
47979                     return;
47980                 }
47981                 
47982                 if(e.ctrlKey){
47983                     this.el.dom.setSelectionRange(0, 0);
47984                     return;
47985                 }
47986                 
47987                 var pos = 0;
47988                 
47989                 for (var i = 0; i < lines.length;i++) {
47990                     pos += lines[i].length;
47991                     
47992                     if(i != 0){
47993                         pos += 1;
47994                     }
47995                     
47996                     if(pos < curr){
47997                         continue;
47998                     }
47999                     
48000                     pos -= lines[i].length;
48001                     
48002                     break;
48003                 }
48004                 
48005                 if(!e.shiftKey){
48006                     this.el.dom.setSelectionRange(pos, pos);
48007                     return;
48008                 }
48009                 
48010                 this.el.dom.selectionStart = pos;
48011                 this.el.dom.selectionEnd = curr;
48012             },
48013             
48014             "end" : function(e){
48015                 e.preventDefault();
48016                 
48017                 var curr = this.el.dom.selectionStart;
48018                 var lines = this.getValue().split("\n");
48019                 
48020                 if(!lines.length){
48021                     return;
48022                 }
48023                 
48024                 if(e.ctrlKey){
48025                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48026                     return;
48027                 }
48028                 
48029                 var pos = 0;
48030                 
48031                 for (var i = 0; i < lines.length;i++) {
48032                     
48033                     pos += lines[i].length;
48034                     
48035                     if(i != 0){
48036                         pos += 1;
48037                     }
48038                     
48039                     if(pos < curr){
48040                         continue;
48041                     }
48042                     
48043                     break;
48044                 }
48045                 
48046                 if(!e.shiftKey){
48047                     this.el.dom.setSelectionRange(pos, pos);
48048                     return;
48049                 }
48050                 
48051                 this.el.dom.selectionStart = curr;
48052                 this.el.dom.selectionEnd = pos;
48053             },
48054
48055             scope : this,
48056
48057             doRelay : function(foo, bar, hname){
48058                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48059             },
48060
48061             forceKeyDown: true
48062         });
48063         
48064 //        if(this.autosave && this.w){
48065 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48066 //        }
48067     },
48068
48069     // private
48070     onResize : function(w, h)
48071     {
48072         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48073         var ew = false;
48074         var eh = false;
48075         
48076         if(this.el ){
48077             if(typeof w == 'number'){
48078                 var aw = w - this.wrap.getFrameWidth('lr');
48079                 this.el.setWidth(this.adjustWidth('textarea', aw));
48080                 ew = aw;
48081             }
48082             if(typeof h == 'number'){
48083                 var tbh = 0;
48084                 for (var i =0; i < this.toolbars.length;i++) {
48085                     // fixme - ask toolbars for heights?
48086                     tbh += this.toolbars[i].tb.el.getHeight();
48087                     if (this.toolbars[i].footer) {
48088                         tbh += this.toolbars[i].footer.el.getHeight();
48089                     }
48090                 }
48091                 
48092                 
48093                 
48094                 
48095                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48096                 ah -= 5; // knock a few pixes off for look..
48097 //                Roo.log(ah);
48098                 this.el.setHeight(this.adjustWidth('textarea', ah));
48099                 var eh = ah;
48100             }
48101         }
48102         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48103         this.editorcore.onResize(ew,eh);
48104         
48105     },
48106
48107     /**
48108      * Toggles the editor between standard and source edit mode.
48109      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48110      */
48111     toggleSourceEdit : function(sourceEditMode)
48112     {
48113         this.editorcore.toggleSourceEdit(sourceEditMode);
48114         
48115         if(this.editorcore.sourceEditMode){
48116             Roo.log('editor - showing textarea');
48117             
48118 //            Roo.log('in');
48119 //            Roo.log(this.syncValue());
48120             this.editorcore.syncValue();
48121             this.el.removeClass('x-hidden');
48122             this.el.dom.removeAttribute('tabIndex');
48123             this.el.focus();
48124             this.el.dom.scrollTop = 0;
48125             
48126             
48127             for (var i = 0; i < this.toolbars.length; i++) {
48128                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48129                     this.toolbars[i].tb.hide();
48130                     this.toolbars[i].footer.hide();
48131                 }
48132             }
48133             
48134         }else{
48135             Roo.log('editor - hiding textarea');
48136 //            Roo.log('out')
48137 //            Roo.log(this.pushValue()); 
48138             this.editorcore.pushValue();
48139             
48140             this.el.addClass('x-hidden');
48141             this.el.dom.setAttribute('tabIndex', -1);
48142             
48143             for (var i = 0; i < this.toolbars.length; i++) {
48144                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48145                     this.toolbars[i].tb.show();
48146                     this.toolbars[i].footer.show();
48147                 }
48148             }
48149             
48150             //this.deferFocus();
48151         }
48152         
48153         this.setSize(this.wrap.getSize());
48154         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48155         
48156         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48157     },
48158  
48159     // private (for BoxComponent)
48160     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48161
48162     // private (for BoxComponent)
48163     getResizeEl : function(){
48164         return this.wrap;
48165     },
48166
48167     // private (for BoxComponent)
48168     getPositionEl : function(){
48169         return this.wrap;
48170     },
48171
48172     // private
48173     initEvents : function(){
48174         this.originalValue = this.getValue();
48175     },
48176
48177     /**
48178      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48179      * @method
48180      */
48181     markInvalid : Roo.emptyFn,
48182     /**
48183      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48184      * @method
48185      */
48186     clearInvalid : Roo.emptyFn,
48187
48188     setValue : function(v){
48189         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48190         this.editorcore.pushValue();
48191     },
48192
48193      
48194     // private
48195     deferFocus : function(){
48196         this.focus.defer(10, this);
48197     },
48198
48199     // doc'ed in Field
48200     focus : function(){
48201         this.editorcore.focus();
48202         
48203     },
48204       
48205
48206     // private
48207     onDestroy : function(){
48208         
48209         
48210         
48211         if(this.rendered){
48212             
48213             for (var i =0; i < this.toolbars.length;i++) {
48214                 // fixme - ask toolbars for heights?
48215                 this.toolbars[i].onDestroy();
48216             }
48217             
48218             this.wrap.dom.innerHTML = '';
48219             this.wrap.remove();
48220         }
48221     },
48222
48223     // private
48224     onFirstFocus : function(){
48225         //Roo.log("onFirstFocus");
48226         this.editorcore.onFirstFocus();
48227          for (var i =0; i < this.toolbars.length;i++) {
48228             this.toolbars[i].onFirstFocus();
48229         }
48230         
48231     },
48232     
48233     // private
48234     syncValue : function()
48235     {
48236         this.editorcore.syncValue();
48237     },
48238     
48239     pushValue : function()
48240     {
48241         this.editorcore.pushValue();
48242     },
48243     
48244     setStylesheets : function(stylesheets)
48245     {
48246         this.editorcore.setStylesheets(stylesheets);
48247     },
48248     
48249     removeStylesheets : function()
48250     {
48251         this.editorcore.removeStylesheets();
48252     }
48253      
48254     
48255     // hide stuff that is not compatible
48256     /**
48257      * @event blur
48258      * @hide
48259      */
48260     /**
48261      * @event change
48262      * @hide
48263      */
48264     /**
48265      * @event focus
48266      * @hide
48267      */
48268     /**
48269      * @event specialkey
48270      * @hide
48271      */
48272     /**
48273      * @cfg {String} fieldClass @hide
48274      */
48275     /**
48276      * @cfg {String} focusClass @hide
48277      */
48278     /**
48279      * @cfg {String} autoCreate @hide
48280      */
48281     /**
48282      * @cfg {String} inputType @hide
48283      */
48284     /**
48285      * @cfg {String} invalidClass @hide
48286      */
48287     /**
48288      * @cfg {String} invalidText @hide
48289      */
48290     /**
48291      * @cfg {String} msgFx @hide
48292      */
48293     /**
48294      * @cfg {String} validateOnBlur @hide
48295      */
48296 });
48297  
48298     // <script type="text/javascript">
48299 /*
48300  * Based on
48301  * Ext JS Library 1.1.1
48302  * Copyright(c) 2006-2007, Ext JS, LLC.
48303  *  
48304  
48305  */
48306
48307 /**
48308  * @class Roo.form.HtmlEditorToolbar1
48309  * Basic Toolbar
48310  * 
48311  * Usage:
48312  *
48313  new Roo.form.HtmlEditor({
48314     ....
48315     toolbars : [
48316         new Roo.form.HtmlEditorToolbar1({
48317             disable : { fonts: 1 , format: 1, ..., ... , ...],
48318             btns : [ .... ]
48319         })
48320     }
48321      
48322  * 
48323  * @cfg {Object} disable List of elements to disable..
48324  * @cfg {Array} btns List of additional buttons.
48325  * 
48326  * 
48327  * NEEDS Extra CSS? 
48328  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48329  */
48330  
48331 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48332 {
48333     
48334     Roo.apply(this, config);
48335     
48336     // default disabled, based on 'good practice'..
48337     this.disable = this.disable || {};
48338     Roo.applyIf(this.disable, {
48339         fontSize : true,
48340         colors : true,
48341         specialElements : true
48342     });
48343     
48344     
48345     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48346     // dont call parent... till later.
48347 }
48348
48349 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48350     
48351     tb: false,
48352     
48353     rendered: false,
48354     
48355     editor : false,
48356     editorcore : false,
48357     /**
48358      * @cfg {Object} disable  List of toolbar elements to disable
48359          
48360      */
48361     disable : false,
48362     
48363     
48364      /**
48365      * @cfg {String} createLinkText The default text for the create link prompt
48366      */
48367     createLinkText : 'Please enter the URL for the link:',
48368     /**
48369      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48370      */
48371     defaultLinkValue : 'http:/'+'/',
48372    
48373     
48374       /**
48375      * @cfg {Array} fontFamilies An array of available font families
48376      */
48377     fontFamilies : [
48378         'Arial',
48379         'Courier New',
48380         'Tahoma',
48381         'Times New Roman',
48382         'Verdana'
48383     ],
48384     
48385     specialChars : [
48386            "&#169;",
48387           "&#174;",     
48388           "&#8482;",    
48389           "&#163;" ,    
48390          // "&#8212;",    
48391           "&#8230;",    
48392           "&#247;" ,    
48393         //  "&#225;" ,     ?? a acute?
48394            "&#8364;"    , //Euro
48395        //   "&#8220;"    ,
48396         //  "&#8221;"    ,
48397         //  "&#8226;"    ,
48398           "&#176;"  //   , // degrees
48399
48400          // "&#233;"     , // e ecute
48401          // "&#250;"     , // u ecute?
48402     ],
48403     
48404     specialElements : [
48405         {
48406             text: "Insert Table",
48407             xtype: 'MenuItem',
48408             xns : Roo.Menu,
48409             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48410                 
48411         },
48412         {    
48413             text: "Insert Image",
48414             xtype: 'MenuItem',
48415             xns : Roo.Menu,
48416             ihtml : '<img src="about:blank"/>'
48417             
48418         }
48419         
48420          
48421     ],
48422     
48423     
48424     inputElements : [ 
48425             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48426             "input:submit", "input:button", "select", "textarea", "label" ],
48427     formats : [
48428         ["p"] ,  
48429         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48430         ["pre"],[ "code"], 
48431         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48432         ['div'],['span'],
48433         ['sup'],['sub']
48434     ],
48435     
48436     cleanStyles : [
48437         "font-size"
48438     ],
48439      /**
48440      * @cfg {String} defaultFont default font to use.
48441      */
48442     defaultFont: 'tahoma',
48443    
48444     fontSelect : false,
48445     
48446     
48447     formatCombo : false,
48448     
48449     init : function(editor)
48450     {
48451         this.editor = editor;
48452         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48453         var editorcore = this.editorcore;
48454         
48455         var _t = this;
48456         
48457         var fid = editorcore.frameId;
48458         var etb = this;
48459         function btn(id, toggle, handler){
48460             var xid = fid + '-'+ id ;
48461             return {
48462                 id : xid,
48463                 cmd : id,
48464                 cls : 'x-btn-icon x-edit-'+id,
48465                 enableToggle:toggle !== false,
48466                 scope: _t, // was editor...
48467                 handler:handler||_t.relayBtnCmd,
48468                 clickEvent:'mousedown',
48469                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48470                 tabIndex:-1
48471             };
48472         }
48473         
48474         
48475         
48476         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48477         this.tb = tb;
48478          // stop form submits
48479         tb.el.on('click', function(e){
48480             e.preventDefault(); // what does this do?
48481         });
48482
48483         if(!this.disable.font) { // && !Roo.isSafari){
48484             /* why no safari for fonts 
48485             editor.fontSelect = tb.el.createChild({
48486                 tag:'select',
48487                 tabIndex: -1,
48488                 cls:'x-font-select',
48489                 html: this.createFontOptions()
48490             });
48491             
48492             editor.fontSelect.on('change', function(){
48493                 var font = editor.fontSelect.dom.value;
48494                 editor.relayCmd('fontname', font);
48495                 editor.deferFocus();
48496             }, editor);
48497             
48498             tb.add(
48499                 editor.fontSelect.dom,
48500                 '-'
48501             );
48502             */
48503             
48504         };
48505         if(!this.disable.formats){
48506             this.formatCombo = new Roo.form.ComboBox({
48507                 store: new Roo.data.SimpleStore({
48508                     id : 'tag',
48509                     fields: ['tag'],
48510                     data : this.formats // from states.js
48511                 }),
48512                 blockFocus : true,
48513                 name : '',
48514                 //autoCreate : {tag: "div",  size: "20"},
48515                 displayField:'tag',
48516                 typeAhead: false,
48517                 mode: 'local',
48518                 editable : false,
48519                 triggerAction: 'all',
48520                 emptyText:'Add tag',
48521                 selectOnFocus:true,
48522                 width:135,
48523                 listeners : {
48524                     'select': function(c, r, i) {
48525                         editorcore.insertTag(r.get('tag'));
48526                         editor.focus();
48527                     }
48528                 }
48529
48530             });
48531             tb.addField(this.formatCombo);
48532             
48533         }
48534         
48535         if(!this.disable.format){
48536             tb.add(
48537                 btn('bold'),
48538                 btn('italic'),
48539                 btn('underline'),
48540                 btn('strikethrough')
48541             );
48542         };
48543         if(!this.disable.fontSize){
48544             tb.add(
48545                 '-',
48546                 
48547                 
48548                 btn('increasefontsize', false, editorcore.adjustFont),
48549                 btn('decreasefontsize', false, editorcore.adjustFont)
48550             );
48551         };
48552         
48553         
48554         if(!this.disable.colors){
48555             tb.add(
48556                 '-', {
48557                     id:editorcore.frameId +'-forecolor',
48558                     cls:'x-btn-icon x-edit-forecolor',
48559                     clickEvent:'mousedown',
48560                     tooltip: this.buttonTips['forecolor'] || undefined,
48561                     tabIndex:-1,
48562                     menu : new Roo.menu.ColorMenu({
48563                         allowReselect: true,
48564                         focus: Roo.emptyFn,
48565                         value:'000000',
48566                         plain:true,
48567                         selectHandler: function(cp, color){
48568                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48569                             editor.deferFocus();
48570                         },
48571                         scope: editorcore,
48572                         clickEvent:'mousedown'
48573                     })
48574                 }, {
48575                     id:editorcore.frameId +'backcolor',
48576                     cls:'x-btn-icon x-edit-backcolor',
48577                     clickEvent:'mousedown',
48578                     tooltip: this.buttonTips['backcolor'] || undefined,
48579                     tabIndex:-1,
48580                     menu : new Roo.menu.ColorMenu({
48581                         focus: Roo.emptyFn,
48582                         value:'FFFFFF',
48583                         plain:true,
48584                         allowReselect: true,
48585                         selectHandler: function(cp, color){
48586                             if(Roo.isGecko){
48587                                 editorcore.execCmd('useCSS', false);
48588                                 editorcore.execCmd('hilitecolor', color);
48589                                 editorcore.execCmd('useCSS', true);
48590                                 editor.deferFocus();
48591                             }else{
48592                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48593                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48594                                 editor.deferFocus();
48595                             }
48596                         },
48597                         scope:editorcore,
48598                         clickEvent:'mousedown'
48599                     })
48600                 }
48601             );
48602         };
48603         // now add all the items...
48604         
48605
48606         if(!this.disable.alignments){
48607             tb.add(
48608                 '-',
48609                 btn('justifyleft'),
48610                 btn('justifycenter'),
48611                 btn('justifyright')
48612             );
48613         };
48614
48615         //if(!Roo.isSafari){
48616             if(!this.disable.links){
48617                 tb.add(
48618                     '-',
48619                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48620                 );
48621             };
48622
48623             if(!this.disable.lists){
48624                 tb.add(
48625                     '-',
48626                     btn('insertorderedlist'),
48627                     btn('insertunorderedlist')
48628                 );
48629             }
48630             if(!this.disable.sourceEdit){
48631                 tb.add(
48632                     '-',
48633                     btn('sourceedit', true, function(btn){
48634                         this.toggleSourceEdit(btn.pressed);
48635                     })
48636                 );
48637             }
48638         //}
48639         
48640         var smenu = { };
48641         // special menu.. - needs to be tidied up..
48642         if (!this.disable.special) {
48643             smenu = {
48644                 text: "&#169;",
48645                 cls: 'x-edit-none',
48646                 
48647                 menu : {
48648                     items : []
48649                 }
48650             };
48651             for (var i =0; i < this.specialChars.length; i++) {
48652                 smenu.menu.items.push({
48653                     
48654                     html: this.specialChars[i],
48655                     handler: function(a,b) {
48656                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48657                         //editor.insertAtCursor(a.html);
48658                         
48659                     },
48660                     tabIndex:-1
48661                 });
48662             }
48663             
48664             
48665             tb.add(smenu);
48666             
48667             
48668         }
48669         
48670         var cmenu = { };
48671         if (!this.disable.cleanStyles) {
48672             cmenu = {
48673                 cls: 'x-btn-icon x-btn-clear',
48674                 
48675                 menu : {
48676                     items : []
48677                 }
48678             };
48679             for (var i =0; i < this.cleanStyles.length; i++) {
48680                 cmenu.menu.items.push({
48681                     actiontype : this.cleanStyles[i],
48682                     html: 'Remove ' + this.cleanStyles[i],
48683                     handler: function(a,b) {
48684 //                        Roo.log(a);
48685 //                        Roo.log(b);
48686                         var c = Roo.get(editorcore.doc.body);
48687                         c.select('[style]').each(function(s) {
48688                             s.dom.style.removeProperty(a.actiontype);
48689                         });
48690                         editorcore.syncValue();
48691                     },
48692                     tabIndex:-1
48693                 });
48694             }
48695             cmenu.menu.items.push({
48696                 actiontype : 'tablewidths',
48697                 html: 'Remove Table Widths',
48698                 handler: function(a,b) {
48699                     editorcore.cleanTableWidths();
48700                     editorcore.syncValue();
48701                 },
48702                 tabIndex:-1
48703             });
48704             cmenu.menu.items.push({
48705                 actiontype : 'word',
48706                 html: 'Remove MS Word Formating',
48707                 handler: function(a,b) {
48708                     editorcore.cleanWord();
48709                     editorcore.syncValue();
48710                 },
48711                 tabIndex:-1
48712             });
48713             
48714             cmenu.menu.items.push({
48715                 actiontype : 'all',
48716                 html: 'Remove All Styles',
48717                 handler: function(a,b) {
48718                     
48719                     var c = Roo.get(editorcore.doc.body);
48720                     c.select('[style]').each(function(s) {
48721                         s.dom.removeAttribute('style');
48722                     });
48723                     editorcore.syncValue();
48724                 },
48725                 tabIndex:-1
48726             });
48727             
48728             cmenu.menu.items.push({
48729                 actiontype : 'all',
48730                 html: 'Remove All CSS Classes',
48731                 handler: function(a,b) {
48732                     
48733                     var c = Roo.get(editorcore.doc.body);
48734                     c.select('[class]').each(function(s) {
48735                         s.dom.removeAttribute('class');
48736                     });
48737                     editorcore.cleanWord();
48738                     editorcore.syncValue();
48739                 },
48740                 tabIndex:-1
48741             });
48742             
48743              cmenu.menu.items.push({
48744                 actiontype : 'tidy',
48745                 html: 'Tidy HTML Source',
48746                 handler: function(a,b) {
48747                     new Roo.htmleditor.Tidy(editorcore.doc.body);
48748                     editorcore.syncValue();
48749                 },
48750                 tabIndex:-1
48751             });
48752             
48753             
48754             tb.add(cmenu);
48755         }
48756          
48757         if (!this.disable.specialElements) {
48758             var semenu = {
48759                 text: "Other;",
48760                 cls: 'x-edit-none',
48761                 menu : {
48762                     items : []
48763                 }
48764             };
48765             for (var i =0; i < this.specialElements.length; i++) {
48766                 semenu.menu.items.push(
48767                     Roo.apply({ 
48768                         handler: function(a,b) {
48769                             editor.insertAtCursor(this.ihtml);
48770                         }
48771                     }, this.specialElements[i])
48772                 );
48773                     
48774             }
48775             
48776             tb.add(semenu);
48777             
48778             
48779         }
48780          
48781         
48782         if (this.btns) {
48783             for(var i =0; i< this.btns.length;i++) {
48784                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
48785                 b.cls =  'x-edit-none';
48786                 
48787                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
48788                     b.cls += ' x-init-enable';
48789                 }
48790                 
48791                 b.scope = editorcore;
48792                 tb.add(b);
48793             }
48794         
48795         }
48796         
48797         
48798         
48799         // disable everything...
48800         
48801         this.tb.items.each(function(item){
48802             
48803            if(
48804                 item.id != editorcore.frameId+ '-sourceedit' && 
48805                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
48806             ){
48807                 
48808                 item.disable();
48809             }
48810         });
48811         this.rendered = true;
48812         
48813         // the all the btns;
48814         editor.on('editorevent', this.updateToolbar, this);
48815         // other toolbars need to implement this..
48816         //editor.on('editmodechange', this.updateToolbar, this);
48817     },
48818     
48819     
48820     relayBtnCmd : function(btn) {
48821         this.editorcore.relayCmd(btn.cmd);
48822     },
48823     // private used internally
48824     createLink : function(){
48825         Roo.log("create link?");
48826         var url = prompt(this.createLinkText, this.defaultLinkValue);
48827         if(url && url != 'http:/'+'/'){
48828             this.editorcore.relayCmd('createlink', url);
48829         }
48830     },
48831
48832     
48833     /**
48834      * Protected method that will not generally be called directly. It triggers
48835      * a toolbar update by reading the markup state of the current selection in the editor.
48836      */
48837     updateToolbar: function(){
48838
48839         if(!this.editorcore.activated){
48840             this.editor.onFirstFocus();
48841             return;
48842         }
48843
48844         var btns = this.tb.items.map, 
48845             doc = this.editorcore.doc,
48846             frameId = this.editorcore.frameId;
48847
48848         if(!this.disable.font && !Roo.isSafari){
48849             /*
48850             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
48851             if(name != this.fontSelect.dom.value){
48852                 this.fontSelect.dom.value = name;
48853             }
48854             */
48855         }
48856         if(!this.disable.format){
48857             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
48858             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
48859             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
48860             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
48861         }
48862         if(!this.disable.alignments){
48863             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
48864             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
48865             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
48866         }
48867         if(!Roo.isSafari && !this.disable.lists){
48868             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
48869             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
48870         }
48871         
48872         var ans = this.editorcore.getAllAncestors();
48873         if (this.formatCombo) {
48874             
48875             
48876             var store = this.formatCombo.store;
48877             this.formatCombo.setValue("");
48878             for (var i =0; i < ans.length;i++) {
48879                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
48880                     // select it..
48881                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
48882                     break;
48883                 }
48884             }
48885         }
48886         
48887         
48888         
48889         // hides menus... - so this cant be on a menu...
48890         Roo.menu.MenuMgr.hideAll();
48891
48892         //this.editorsyncValue();
48893     },
48894    
48895     
48896     createFontOptions : function(){
48897         var buf = [], fs = this.fontFamilies, ff, lc;
48898         
48899         
48900         
48901         for(var i = 0, len = fs.length; i< len; i++){
48902             ff = fs[i];
48903             lc = ff.toLowerCase();
48904             buf.push(
48905                 '<option value="',lc,'" style="font-family:',ff,';"',
48906                     (this.defaultFont == lc ? ' selected="true">' : '>'),
48907                     ff,
48908                 '</option>'
48909             );
48910         }
48911         return buf.join('');
48912     },
48913     
48914     toggleSourceEdit : function(sourceEditMode){
48915         
48916         Roo.log("toolbar toogle");
48917         if(sourceEditMode === undefined){
48918             sourceEditMode = !this.sourceEditMode;
48919         }
48920         this.sourceEditMode = sourceEditMode === true;
48921         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
48922         // just toggle the button?
48923         if(btn.pressed !== this.sourceEditMode){
48924             btn.toggle(this.sourceEditMode);
48925             return;
48926         }
48927         
48928         if(sourceEditMode){
48929             Roo.log("disabling buttons");
48930             this.tb.items.each(function(item){
48931                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
48932                     item.disable();
48933                 }
48934             });
48935           
48936         }else{
48937             Roo.log("enabling buttons");
48938             if(this.editorcore.initialized){
48939                 this.tb.items.each(function(item){
48940                     item.enable();
48941                 });
48942             }
48943             
48944         }
48945         Roo.log("calling toggole on editor");
48946         // tell the editor that it's been pressed..
48947         this.editor.toggleSourceEdit(sourceEditMode);
48948        
48949     },
48950      /**
48951      * Object collection of toolbar tooltips for the buttons in the editor. The key
48952      * is the command id associated with that button and the value is a valid QuickTips object.
48953      * For example:
48954 <pre><code>
48955 {
48956     bold : {
48957         title: 'Bold (Ctrl+B)',
48958         text: 'Make the selected text bold.',
48959         cls: 'x-html-editor-tip'
48960     },
48961     italic : {
48962         title: 'Italic (Ctrl+I)',
48963         text: 'Make the selected text italic.',
48964         cls: 'x-html-editor-tip'
48965     },
48966     ...
48967 </code></pre>
48968     * @type Object
48969      */
48970     buttonTips : {
48971         bold : {
48972             title: 'Bold (Ctrl+B)',
48973             text: 'Make the selected text bold.',
48974             cls: 'x-html-editor-tip'
48975         },
48976         italic : {
48977             title: 'Italic (Ctrl+I)',
48978             text: 'Make the selected text italic.',
48979             cls: 'x-html-editor-tip'
48980         },
48981         underline : {
48982             title: 'Underline (Ctrl+U)',
48983             text: 'Underline the selected text.',
48984             cls: 'x-html-editor-tip'
48985         },
48986         strikethrough : {
48987             title: 'Strikethrough',
48988             text: 'Strikethrough the selected text.',
48989             cls: 'x-html-editor-tip'
48990         },
48991         increasefontsize : {
48992             title: 'Grow Text',
48993             text: 'Increase the font size.',
48994             cls: 'x-html-editor-tip'
48995         },
48996         decreasefontsize : {
48997             title: 'Shrink Text',
48998             text: 'Decrease the font size.',
48999             cls: 'x-html-editor-tip'
49000         },
49001         backcolor : {
49002             title: 'Text Highlight Color',
49003             text: 'Change the background color of the selected text.',
49004             cls: 'x-html-editor-tip'
49005         },
49006         forecolor : {
49007             title: 'Font Color',
49008             text: 'Change the color of the selected text.',
49009             cls: 'x-html-editor-tip'
49010         },
49011         justifyleft : {
49012             title: 'Align Text Left',
49013             text: 'Align text to the left.',
49014             cls: 'x-html-editor-tip'
49015         },
49016         justifycenter : {
49017             title: 'Center Text',
49018             text: 'Center text in the editor.',
49019             cls: 'x-html-editor-tip'
49020         },
49021         justifyright : {
49022             title: 'Align Text Right',
49023             text: 'Align text to the right.',
49024             cls: 'x-html-editor-tip'
49025         },
49026         insertunorderedlist : {
49027             title: 'Bullet List',
49028             text: 'Start a bulleted list.',
49029             cls: 'x-html-editor-tip'
49030         },
49031         insertorderedlist : {
49032             title: 'Numbered List',
49033             text: 'Start a numbered list.',
49034             cls: 'x-html-editor-tip'
49035         },
49036         createlink : {
49037             title: 'Hyperlink',
49038             text: 'Make the selected text a hyperlink.',
49039             cls: 'x-html-editor-tip'
49040         },
49041         sourceedit : {
49042             title: 'Source Edit',
49043             text: 'Switch to source editing mode.',
49044             cls: 'x-html-editor-tip'
49045         }
49046     },
49047     // private
49048     onDestroy : function(){
49049         if(this.rendered){
49050             
49051             this.tb.items.each(function(item){
49052                 if(item.menu){
49053                     item.menu.removeAll();
49054                     if(item.menu.el){
49055                         item.menu.el.destroy();
49056                     }
49057                 }
49058                 item.destroy();
49059             });
49060              
49061         }
49062     },
49063     onFirstFocus: function() {
49064         this.tb.items.each(function(item){
49065            item.enable();
49066         });
49067     }
49068 });
49069
49070
49071
49072
49073 // <script type="text/javascript">
49074 /*
49075  * Based on
49076  * Ext JS Library 1.1.1
49077  * Copyright(c) 2006-2007, Ext JS, LLC.
49078  *  
49079  
49080  */
49081
49082  
49083 /**
49084  * @class Roo.form.HtmlEditor.ToolbarContext
49085  * Context Toolbar
49086  * 
49087  * Usage:
49088  *
49089  new Roo.form.HtmlEditor({
49090     ....
49091     toolbars : [
49092         { xtype: 'ToolbarStandard', styles : {} }
49093         { xtype: 'ToolbarContext', disable : {} }
49094     ]
49095 })
49096
49097      
49098  * 
49099  * @config : {Object} disable List of elements to disable.. (not done yet.)
49100  * @config : {Object} styles  Map of styles available.
49101  * 
49102  */
49103
49104 Roo.form.HtmlEditor.ToolbarContext = function(config)
49105 {
49106     
49107     Roo.apply(this, config);
49108     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49109     // dont call parent... till later.
49110     this.styles = this.styles || {};
49111 }
49112
49113  
49114
49115 Roo.form.HtmlEditor.ToolbarContext.types = {
49116     'IMG' : {
49117         width : {
49118             title: "Width",
49119             width: 40
49120         },
49121         height:  {
49122             title: "Height",
49123             width: 40
49124         },
49125         align: {
49126             title: "Align",
49127             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49128             width : 80
49129             
49130         },
49131         border: {
49132             title: "Border",
49133             width: 40
49134         },
49135         alt: {
49136             title: "Alt",
49137             width: 120
49138         },
49139         src : {
49140             title: "Src",
49141             width: 220
49142         }
49143         
49144     },
49145     
49146     'FIGURE' : {
49147          align: {
49148             title: "Align",
49149             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49150             width : 80  
49151         }
49152     },
49153     'A' : {
49154         name : {
49155             title: "Name",
49156             width: 50
49157         },
49158         target:  {
49159             title: "Target",
49160             width: 120
49161         },
49162         href:  {
49163             title: "Href",
49164             width: 220
49165         } // border?
49166         
49167     },
49168     /*
49169     'TABLE' : {
49170         rows : {
49171             title: "Rows",
49172             width: 20
49173         },
49174         cols : {
49175             title: "Cols",
49176             width: 20
49177         },
49178         width : {
49179             title: "Width",
49180             width: 40
49181         },
49182         height : {
49183             title: "Height",
49184             width: 40
49185         },
49186         border : {
49187             title: "Border",
49188             width: 20
49189         }
49190     },
49191     'TD' : {
49192         width : {
49193             title: "Width",
49194             width: 40
49195         },
49196         height : {
49197             title: "Height",
49198             width: 40
49199         },   
49200         align: {
49201             title: "Align",
49202             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
49203             width: 80
49204         },
49205         valign: {
49206             title: "Valign",
49207             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
49208             width: 80
49209         },
49210         colspan: {
49211             title: "Colspan",
49212             width: 20
49213             
49214         },
49215          'font-family'  : {
49216             title : "Font",
49217             style : 'fontFamily',
49218             displayField: 'display',
49219             optname : 'font-family',
49220             width: 140
49221         }
49222     },
49223     */
49224     'INPUT' : {
49225         name : {
49226             title: "name",
49227             width: 120
49228         },
49229         value : {
49230             title: "Value",
49231             width: 120
49232         },
49233         width : {
49234             title: "Width",
49235             width: 40
49236         }
49237     },
49238     'LABEL' : {
49239         'for' : {
49240             title: "For",
49241             width: 120
49242         }
49243     },
49244     'TEXTAREA' : {
49245         name : {
49246             title: "name",
49247             width: 120
49248         },
49249         rows : {
49250             title: "Rows",
49251             width: 20
49252         },
49253         cols : {
49254             title: "Cols",
49255             width: 20
49256         }
49257     },
49258     'SELECT' : {
49259         name : {
49260             title: "name",
49261             width: 120
49262         },
49263         selectoptions : {
49264             title: "Options",
49265             width: 200
49266         }
49267     },
49268     
49269     // should we really allow this??
49270     // should this just be 
49271     'BODY' : {
49272         title : {
49273             title: "Title",
49274             width: 200,
49275             disabled : true
49276         }
49277     },
49278     /*
49279     'SPAN' : {
49280         'font-family'  : {
49281             title : "Font",
49282             style : 'fontFamily',
49283             displayField: 'display',
49284             optname : 'font-family',
49285             width: 140
49286         }
49287     },
49288     'DIV' : {
49289         'font-family'  : {
49290             title : "Font",
49291             style : 'fontFamily',
49292             displayField: 'display',
49293             optname : 'font-family',
49294             width: 140
49295         }
49296     },
49297      'P' : {
49298         'font-family'  : {
49299             title : "Font",
49300             style : 'fontFamily',
49301             displayField: 'display',
49302             optname : 'font-family',
49303             width: 140
49304         }
49305     },
49306     */
49307     '*' : {
49308         // empty..
49309     }
49310
49311 };
49312
49313 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49314 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49315
49316 Roo.form.HtmlEditor.ToolbarContext.options = {
49317         'font-family'  : [ 
49318                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49319                 [ 'Courier New', 'Courier New'],
49320                 [ 'Tahoma', 'Tahoma'],
49321                 [ 'Times New Roman,serif', 'Times'],
49322                 [ 'Verdana','Verdana' ]
49323         ]
49324 };
49325
49326 // fixme - these need to be configurable..
49327  
49328
49329 //Roo.form.HtmlEditor.ToolbarContext.types
49330
49331
49332 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49333     
49334     tb: false,
49335     
49336     rendered: false,
49337     
49338     editor : false,
49339     editorcore : false,
49340     /**
49341      * @cfg {Object} disable  List of toolbar elements to disable
49342          
49343      */
49344     disable : false,
49345     /**
49346      * @cfg {Object} styles List of styles 
49347      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49348      *
49349      * These must be defined in the page, so they get rendered correctly..
49350      * .headline { }
49351      * TD.underline { }
49352      * 
49353      */
49354     styles : false,
49355     
49356     options: false,
49357     
49358     toolbars : false,
49359     
49360     init : function(editor)
49361     {
49362         this.editor = editor;
49363         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49364         var editorcore = this.editorcore;
49365         
49366         var fid = editorcore.frameId;
49367         var etb = this;
49368         function btn(id, toggle, handler){
49369             var xid = fid + '-'+ id ;
49370             return {
49371                 id : xid,
49372                 cmd : id,
49373                 cls : 'x-btn-icon x-edit-'+id,
49374                 enableToggle:toggle !== false,
49375                 scope: editorcore, // was editor...
49376                 handler:handler||editorcore.relayBtnCmd,
49377                 clickEvent:'mousedown',
49378                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49379                 tabIndex:-1
49380             };
49381         }
49382         // create a new element.
49383         var wdiv = editor.wrap.createChild({
49384                 tag: 'div'
49385             }, editor.wrap.dom.firstChild.nextSibling, true);
49386         
49387         // can we do this more than once??
49388         
49389          // stop form submits
49390       
49391  
49392         // disable everything...
49393         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49394         this.toolbars = {};
49395            
49396         for (var i in  ty) {
49397           
49398             this.toolbars[i] = this.buildToolbar(ty[i],i);
49399         }
49400         this.tb = this.toolbars.BODY;
49401         this.tb.el.show();
49402         this.buildFooter();
49403         this.footer.show();
49404         editor.on('hide', function( ) { this.footer.hide() }, this);
49405         editor.on('show', function( ) { this.footer.show() }, this);
49406         
49407          
49408         this.rendered = true;
49409         
49410         // the all the btns;
49411         editor.on('editorevent', this.updateToolbar, this);
49412         // other toolbars need to implement this..
49413         //editor.on('editmodechange', this.updateToolbar, this);
49414     },
49415     
49416     
49417     
49418     /**
49419      * Protected method that will not generally be called directly. It triggers
49420      * a toolbar update by reading the markup state of the current selection in the editor.
49421      *
49422      * Note you can force an update by calling on('editorevent', scope, false)
49423      */
49424     updateToolbar: function(editor ,ev, sel)
49425     {
49426         
49427         if (ev) {
49428             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49429         }
49430         
49431         //Roo.log(ev);
49432         // capture mouse up - this is handy for selecting images..
49433         // perhaps should go somewhere else...
49434         if(!this.editorcore.activated){
49435              this.editor.onFirstFocus();
49436             return;
49437         }
49438         Roo.log(ev ? ev.target : 'NOTARGET');
49439         
49440         
49441         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49442         // selectNode - might want to handle IE?
49443         
49444         
49445         
49446         if (ev &&
49447             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49448             ev.target && ev.target != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49449             // they have click on an image...
49450             // let's see if we can change the selection...
49451             sel = ev.target;
49452             
49453             // this triggers looping?
49454             //this.editorcore.selectNode(sel);
49455              
49456         }  
49457         
49458       
49459         //var updateFooter = sel ? false : true; 
49460         
49461         
49462         var ans = this.editorcore.getAllAncestors();
49463         
49464         // pick
49465         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49466         
49467         if (!sel) { 
49468             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49469             sel = sel ? sel : this.editorcore.doc.body;
49470             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49471             
49472         }
49473         
49474         var tn = sel.tagName.toUpperCase();
49475         var lastSel = this.tb.selectedNode;
49476         this.tb.selectedNode = sel;
49477         var left_label = tn;
49478         
49479         // ok see if we are editing a block?
49480         var sel_el = Roo.get(sel);
49481         var db = false;
49482         // you are not actually selecting the block.
49483         if (sel && sel.hasAttribute('data-block')) {
49484             db = sel;
49485         } else if (sel && !sel.hasAttribute('contenteditable')) {
49486             db = sel_el.findParent('[data-block]');
49487             var cepar = sel_el.findParent('[contenteditable=true]');
49488             if (db && cepar && cepar.tagName != 'BODY') {
49489                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49490             }   
49491         }
49492         
49493         
49494         var block = false;
49495         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49496         if (db) {
49497             block = Roo.htmleditor.Block.factory(db);
49498             if (block) {
49499                 tn = 'BLOCK.' + db.getAttribute('data-block');
49500                 
49501                 //this.editorcore.selectNode(db);
49502                 if (typeof(this.toolbars[tn]) == 'undefined') {
49503                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49504                 }
49505                 this.toolbars[tn].selectedNode = db;
49506                 left_label = block.friendly_name;
49507                 ans = this.editorcore.getAllAncestors();
49508             }
49509             
49510                 
49511             
49512         }
49513         
49514         
49515         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49516             return; // no change?
49517         }
49518         
49519         
49520           
49521         this.tb.el.hide();
49522         ///console.log("show: " + tn);
49523         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49524         
49525         this.tb.el.show();
49526         // update name
49527         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49528         
49529         
49530         // update attributes
49531         if (block) {
49532              
49533             this.tb.fields.each(function(e) {
49534                 e.setValue(block[e.name]);
49535             });
49536             
49537             
49538         } else  if (this.tb.fields && this.tb.selectedNode) {
49539             this.tb.fields.each( function(e) {
49540                 if (e.stylename) {
49541                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49542                     return;
49543                 } 
49544                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49545             }, this);
49546             this.updateToolbarStyles(this.tb.selectedNode);  
49547         }
49548         
49549         
49550        
49551         Roo.menu.MenuMgr.hideAll();
49552
49553         
49554         
49555     
49556         // update the footer
49557         //
49558         this.updateFooter(ans);
49559              
49560     },
49561     
49562     updateToolbarStyles : function(sel)
49563     {
49564         var hasStyles = false;
49565         for(var i in this.styles) {
49566             hasStyles = true;
49567             break;
49568         }
49569         
49570         // update styles
49571         if (hasStyles && this.tb.hasStyles) { 
49572             var st = this.tb.fields.item(0);
49573             
49574             st.store.removeAll();
49575             var cn = sel.className.split(/\s+/);
49576             
49577             var avs = [];
49578             if (this.styles['*']) {
49579                 
49580                 Roo.each(this.styles['*'], function(v) {
49581                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49582                 });
49583             }
49584             if (this.styles[tn]) { 
49585                 Roo.each(this.styles[tn], function(v) {
49586                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49587                 });
49588             }
49589             
49590             st.store.loadData(avs);
49591             st.collapse();
49592             st.setValue(cn);
49593         }
49594     },
49595     
49596      
49597     updateFooter : function(ans)
49598     {
49599         var html = '';
49600         if (ans === false) {
49601             this.footDisp.dom.innerHTML = '';
49602             return;
49603         }
49604         
49605         this.footerEls = ans.reverse();
49606         Roo.each(this.footerEls, function(a,i) {
49607             if (!a) { return; }
49608             html += html.length ? ' &gt; '  :  '';
49609             
49610             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49611             
49612         });
49613        
49614         // 
49615         var sz = this.footDisp.up('td').getSize();
49616         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49617         this.footDisp.dom.style.marginLeft = '5px';
49618         
49619         this.footDisp.dom.style.overflow = 'hidden';
49620         
49621         this.footDisp.dom.innerHTML = html;
49622             
49623         
49624     },
49625    
49626        
49627     // private
49628     onDestroy : function(){
49629         if(this.rendered){
49630             
49631             this.tb.items.each(function(item){
49632                 if(item.menu){
49633                     item.menu.removeAll();
49634                     if(item.menu.el){
49635                         item.menu.el.destroy();
49636                     }
49637                 }
49638                 item.destroy();
49639             });
49640              
49641         }
49642     },
49643     onFirstFocus: function() {
49644         // need to do this for all the toolbars..
49645         this.tb.items.each(function(item){
49646            item.enable();
49647         });
49648     },
49649     buildToolbar: function(tlist, nm, friendly_name, block)
49650     {
49651         var editor = this.editor;
49652         var editorcore = this.editorcore;
49653          // create a new element.
49654         var wdiv = editor.wrap.createChild({
49655                 tag: 'div'
49656             }, editor.wrap.dom.firstChild.nextSibling, true);
49657         
49658        
49659         var tb = new Roo.Toolbar(wdiv);
49660         this.tb = tb;
49661         if (tlist === false && block) {
49662             tlist = block.contextMenu(this);
49663         }
49664         
49665         tb.hasStyles = false;
49666         tb.name = nm;
49667         
49668         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49669         
49670         var styles = Array.from(this.styles);
49671         
49672         
49673         // styles...
49674         if (styles && styles.length) {
49675             tb.hasStyles = true;
49676             // this needs a multi-select checkbox...
49677             tb.addField( new Roo.form.ComboBox({
49678                 store: new Roo.data.SimpleStore({
49679                     id : 'val',
49680                     fields: ['val', 'selected'],
49681                     data : [] 
49682                 }),
49683                 name : '-roo-edit-className',
49684                 attrname : 'className',
49685                 displayField: 'val',
49686                 typeAhead: false,
49687                 mode: 'local',
49688                 editable : false,
49689                 triggerAction: 'all',
49690                 emptyText:'Select Style',
49691                 selectOnFocus:true,
49692                 width: 130,
49693                 listeners : {
49694                     'select': function(c, r, i) {
49695                         // initial support only for on class per el..
49696                         tb.selectedNode.className =  r ? r.get('val') : '';
49697                         editorcore.syncValue();
49698                     }
49699                 }
49700     
49701             }));
49702         }
49703         
49704         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49705         
49706         
49707         for (var i in tlist) {
49708             
49709             // newer versions will use xtype cfg to create menus.
49710             if (typeof(tlist[i].xtype) != 'undefined') {
49711                 
49712                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
49713                 
49714                 
49715                 continue;
49716             }
49717             
49718             var item = tlist[i];
49719             tb.add(item.title + ":&nbsp;");
49720             
49721             
49722             //optname == used so you can configure the options available..
49723             var opts = item.opts ? item.opts : false;
49724             if (item.optname) { // use the b
49725                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49726            
49727             }
49728             
49729             if (opts) {
49730                 // opts == pulldown..
49731                 tb.addField( new Roo.form.ComboBox({
49732                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
49733                         id : 'val',
49734                         fields: ['val', 'display'],
49735                         data : opts  
49736                     }),
49737                     name : '-roo-edit-' + i,
49738                     
49739                     attrname : i,
49740                     stylename : item.style ? item.style : false,
49741                     
49742                     displayField: item.displayField ? item.displayField : 'val',
49743                     valueField :  'val',
49744                     typeAhead: false,
49745                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
49746                     editable : false,
49747                     triggerAction: 'all',
49748                     emptyText:'Select',
49749                     selectOnFocus:true,
49750                     width: item.width ? item.width  : 130,
49751                     listeners : {
49752                         'select': function(c, r, i) {
49753                             if (tb.selectedNode.hasAttribute('data-block')) {
49754                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49755                                 b[c.attrname] = r.get('val');
49756                                 b.updateElement(tb.selectedNode);
49757                                 editorcore.syncValue();
49758                                 return;
49759                             }
49760                             
49761                             if (c.stylename) {
49762                                 tb.selectedNode.style[c.stylename] =  r.get('val');
49763                                 editorcore.syncValue();
49764                                 return;
49765                             }
49766                             if (r === false) {
49767                                 tb.selectedNode.removeAttribute(c.attrname);
49768                                 editorcore.syncValue();
49769                                 return;
49770                             }
49771                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
49772                             editorcore.syncValue();
49773                         }
49774                     }
49775
49776                 }));
49777                 continue;
49778                     
49779                  
49780                 /*
49781                 tb.addField( new Roo.form.TextField({
49782                     name: i,
49783                     width: 100,
49784                     //allowBlank:false,
49785                     value: ''
49786                 }));
49787                 continue;
49788                 */
49789             }
49790             tb.addField( new Roo.form.TextField({
49791                 name: '-roo-edit-' + i,
49792                 attrname : i,
49793                 
49794                 width: item.width,
49795                 //allowBlank:true,
49796                 value: '',
49797                 listeners: {
49798                     'change' : function(f, nv, ov) {
49799                         
49800                         if (tb.selectedNode.hasAttribute('data-block')) {
49801                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49802                             b[f.attrname] = nv;
49803                             b.updateElement(tb.selectedNode);
49804                             editorcore.syncValue();
49805                             return;
49806                         }
49807                         
49808                         tb.selectedNode.setAttribute(f.attrname, nv);
49809                         editorcore.syncValue();
49810                     }
49811                 }
49812             }));
49813              
49814         }
49815         
49816         var _this = this;
49817         
49818         if(nm == 'BODY'){
49819             tb.addSeparator();
49820         
49821             tb.addButton( {
49822                 text: 'Stylesheets',
49823
49824                 listeners : {
49825                     click : function ()
49826                     {
49827                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
49828                     }
49829                 }
49830             });
49831         }
49832         
49833         tb.addFill();
49834         tb.addButton({
49835             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
49836     
49837             listeners : {
49838                 click : function ()
49839                 {
49840                     // remove
49841                     // undo does not work.
49842                     var sn = tb.selectedNode;
49843                     if (!sn) {
49844                         return;
49845                     }
49846                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
49847                     if (sn.hasAttribute('data-block')) {
49848                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
49849                         sn.parentNode.removeChild(sn);
49850                         
49851                     } else if (sn && sn.tagName != 'BODY') {
49852                         // remove and keep parents.
49853                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
49854                         a.removeTag(sn);
49855                     }
49856                     
49857                     
49858                     var range = editorcore.createRange();
49859         
49860                     range.setStart(stn,0);
49861                     range.setEnd(stn,0); 
49862                     var selection = editorcore.getSelection();
49863                     selection.removeAllRanges();
49864                     selection.addRange(range);
49865                     
49866                     
49867                     //_this.updateToolbar(null, null, pn);
49868                     _this.updateToolbar(null, null, null);
49869                     _this.updateFooter(false);
49870                     
49871                 }
49872             }
49873             
49874                     
49875                 
49876             
49877         });
49878         
49879         
49880         tb.el.on('click', function(e){
49881             e.preventDefault(); // what does this do?
49882         });
49883         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
49884         tb.el.hide();
49885         
49886         // dont need to disable them... as they will get hidden
49887         return tb;
49888          
49889         
49890     },
49891     buildFooter : function()
49892     {
49893         
49894         var fel = this.editor.wrap.createChild();
49895         this.footer = new Roo.Toolbar(fel);
49896         // toolbar has scrolly on left / right?
49897         var footDisp= new Roo.Toolbar.Fill();
49898         var _t = this;
49899         this.footer.add(
49900             {
49901                 text : '&lt;',
49902                 xtype: 'Button',
49903                 handler : function() {
49904                     _t.footDisp.scrollTo('left',0,true)
49905                 }
49906             }
49907         );
49908         this.footer.add( footDisp );
49909         this.footer.add( 
49910             {
49911                 text : '&gt;',
49912                 xtype: 'Button',
49913                 handler : function() {
49914                     // no animation..
49915                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
49916                 }
49917             }
49918         );
49919         var fel = Roo.get(footDisp.el);
49920         fel.addClass('x-editor-context');
49921         this.footDispWrap = fel; 
49922         this.footDispWrap.overflow  = 'hidden';
49923         
49924         this.footDisp = fel.createChild();
49925         this.footDispWrap.on('click', this.onContextClick, this)
49926         
49927         
49928     },
49929     // when the footer contect changes
49930     onContextClick : function (ev,dom)
49931     {
49932         ev.preventDefault();
49933         var  cn = dom.className;
49934         //Roo.log(cn);
49935         if (!cn.match(/x-ed-loc-/)) {
49936             return;
49937         }
49938         var n = cn.split('-').pop();
49939         var ans = this.footerEls;
49940         var sel = ans[n];
49941         
49942          // pick
49943         var range = this.editorcore.createRange();
49944         
49945         range.selectNodeContents(sel);
49946         //range.selectNode(sel);
49947         
49948         
49949         var selection = this.editorcore.getSelection();
49950         selection.removeAllRanges();
49951         selection.addRange(range);
49952         
49953         
49954         
49955         this.updateToolbar(null, null, sel);
49956         
49957         
49958     }
49959     
49960     
49961     
49962     
49963     
49964 });
49965
49966
49967
49968
49969
49970 /*
49971  * Based on:
49972  * Ext JS Library 1.1.1
49973  * Copyright(c) 2006-2007, Ext JS, LLC.
49974  *
49975  * Originally Released Under LGPL - original licence link has changed is not relivant.
49976  *
49977  * Fork - LGPL
49978  * <script type="text/javascript">
49979  */
49980  
49981 /**
49982  * @class Roo.form.BasicForm
49983  * @extends Roo.util.Observable
49984  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
49985  * @constructor
49986  * @param {String/HTMLElement/Roo.Element} el The form element or its id
49987  * @param {Object} config Configuration options
49988  */
49989 Roo.form.BasicForm = function(el, config){
49990     this.allItems = [];
49991     this.childForms = [];
49992     Roo.apply(this, config);
49993     /*
49994      * The Roo.form.Field items in this form.
49995      * @type MixedCollection
49996      */
49997      
49998      
49999     this.items = new Roo.util.MixedCollection(false, function(o){
50000         return o.id || (o.id = Roo.id());
50001     });
50002     this.addEvents({
50003         /**
50004          * @event beforeaction
50005          * Fires before any action is performed. Return false to cancel the action.
50006          * @param {Form} this
50007          * @param {Action} action The action to be performed
50008          */
50009         beforeaction: true,
50010         /**
50011          * @event actionfailed
50012          * Fires when an action fails.
50013          * @param {Form} this
50014          * @param {Action} action The action that failed
50015          */
50016         actionfailed : true,
50017         /**
50018          * @event actioncomplete
50019          * Fires when an action is completed.
50020          * @param {Form} this
50021          * @param {Action} action The action that completed
50022          */
50023         actioncomplete : true
50024     });
50025     if(el){
50026         this.initEl(el);
50027     }
50028     Roo.form.BasicForm.superclass.constructor.call(this);
50029     
50030     Roo.form.BasicForm.popover.apply();
50031 };
50032
50033 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50034     /**
50035      * @cfg {String} method
50036      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50037      */
50038     /**
50039      * @cfg {DataReader} reader
50040      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50041      * This is optional as there is built-in support for processing JSON.
50042      */
50043     /**
50044      * @cfg {DataReader} errorReader
50045      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50046      * This is completely optional as there is built-in support for processing JSON.
50047      */
50048     /**
50049      * @cfg {String} url
50050      * The URL to use for form actions if one isn't supplied in the action options.
50051      */
50052     /**
50053      * @cfg {Boolean} fileUpload
50054      * Set to true if this form is a file upload.
50055      */
50056      
50057     /**
50058      * @cfg {Object} baseParams
50059      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50060      */
50061      /**
50062      
50063     /**
50064      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50065      */
50066     timeout: 30,
50067
50068     // private
50069     activeAction : null,
50070
50071     /**
50072      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50073      * or setValues() data instead of when the form was first created.
50074      */
50075     trackResetOnLoad : false,
50076     
50077     
50078     /**
50079      * childForms - used for multi-tab forms
50080      * @type {Array}
50081      */
50082     childForms : false,
50083     
50084     /**
50085      * allItems - full list of fields.
50086      * @type {Array}
50087      */
50088     allItems : false,
50089     
50090     /**
50091      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50092      * element by passing it or its id or mask the form itself by passing in true.
50093      * @type Mixed
50094      */
50095     waitMsgTarget : false,
50096     
50097     /**
50098      * @type Boolean
50099      */
50100     disableMask : false,
50101     
50102     /**
50103      * @cfg {Boolean} errorMask (true|false) default false
50104      */
50105     errorMask : false,
50106     
50107     /**
50108      * @cfg {Number} maskOffset Default 100
50109      */
50110     maskOffset : 100,
50111
50112     // private
50113     initEl : function(el){
50114         this.el = Roo.get(el);
50115         this.id = this.el.id || Roo.id();
50116         this.el.on('submit', this.onSubmit, this);
50117         this.el.addClass('x-form');
50118     },
50119
50120     // private
50121     onSubmit : function(e){
50122         e.stopEvent();
50123     },
50124
50125     /**
50126      * Returns true if client-side validation on the form is successful.
50127      * @return Boolean
50128      */
50129     isValid : function(){
50130         var valid = true;
50131         var target = false;
50132         this.items.each(function(f){
50133             if(f.validate()){
50134                 return;
50135             }
50136             
50137             valid = false;
50138                 
50139             if(!target && f.el.isVisible(true)){
50140                 target = f;
50141             }
50142         });
50143         
50144         if(this.errorMask && !valid){
50145             Roo.form.BasicForm.popover.mask(this, target);
50146         }
50147         
50148         return valid;
50149     },
50150     /**
50151      * Returns array of invalid form fields.
50152      * @return Array
50153      */
50154     
50155     invalidFields : function()
50156     {
50157         var ret = [];
50158         this.items.each(function(f){
50159             if(f.validate()){
50160                 return;
50161             }
50162             ret.push(f);
50163             
50164         });
50165         
50166         return ret;
50167     },
50168     
50169     
50170     /**
50171      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50172      * @return Boolean
50173      */
50174     isDirty : function(){
50175         var dirty = false;
50176         this.items.each(function(f){
50177            if(f.isDirty()){
50178                dirty = true;
50179                return false;
50180            }
50181         });
50182         return dirty;
50183     },
50184     
50185     /**
50186      * Returns true if any fields in this form have changed since their original load. (New version)
50187      * @return Boolean
50188      */
50189     
50190     hasChanged : function()
50191     {
50192         var dirty = false;
50193         this.items.each(function(f){
50194            if(f.hasChanged()){
50195                dirty = true;
50196                return false;
50197            }
50198         });
50199         return dirty;
50200         
50201     },
50202     /**
50203      * Resets all hasChanged to 'false' -
50204      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50205      * So hasChanged storage is only to be used for this purpose
50206      * @return Boolean
50207      */
50208     resetHasChanged : function()
50209     {
50210         this.items.each(function(f){
50211            f.resetHasChanged();
50212         });
50213         
50214     },
50215     
50216     
50217     /**
50218      * Performs a predefined action (submit or load) or custom actions you define on this form.
50219      * @param {String} actionName The name of the action type
50220      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50221      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50222      * accept other config options):
50223      * <pre>
50224 Property          Type             Description
50225 ----------------  ---------------  ----------------------------------------------------------------------------------
50226 url               String           The url for the action (defaults to the form's url)
50227 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50228 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50229 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50230                                    validate the form on the client (defaults to false)
50231      * </pre>
50232      * @return {BasicForm} this
50233      */
50234     doAction : function(action, options){
50235         if(typeof action == 'string'){
50236             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50237         }
50238         if(this.fireEvent('beforeaction', this, action) !== false){
50239             this.beforeAction(action);
50240             action.run.defer(100, action);
50241         }
50242         return this;
50243     },
50244
50245     /**
50246      * Shortcut to do a submit action.
50247      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50248      * @return {BasicForm} this
50249      */
50250     submit : function(options){
50251         this.doAction('submit', options);
50252         return this;
50253     },
50254
50255     /**
50256      * Shortcut to do a load action.
50257      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50258      * @return {BasicForm} this
50259      */
50260     load : function(options){
50261         this.doAction('load', options);
50262         return this;
50263     },
50264
50265     /**
50266      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50267      * @param {Record} record The record to edit
50268      * @return {BasicForm} this
50269      */
50270     updateRecord : function(record){
50271         record.beginEdit();
50272         var fs = record.fields;
50273         fs.each(function(f){
50274             var field = this.findField(f.name);
50275             if(field){
50276                 record.set(f.name, field.getValue());
50277             }
50278         }, this);
50279         record.endEdit();
50280         return this;
50281     },
50282
50283     /**
50284      * Loads an Roo.data.Record into this form.
50285      * @param {Record} record The record to load
50286      * @return {BasicForm} this
50287      */
50288     loadRecord : function(record){
50289         this.setValues(record.data);
50290         return this;
50291     },
50292
50293     // private
50294     beforeAction : function(action){
50295         var o = action.options;
50296         
50297         if(!this.disableMask) {
50298             if(this.waitMsgTarget === true){
50299                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50300             }else if(this.waitMsgTarget){
50301                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50302                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50303             }else {
50304                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50305             }
50306         }
50307         
50308          
50309     },
50310
50311     // private
50312     afterAction : function(action, success){
50313         this.activeAction = null;
50314         var o = action.options;
50315         
50316         if(!this.disableMask) {
50317             if(this.waitMsgTarget === true){
50318                 this.el.unmask();
50319             }else if(this.waitMsgTarget){
50320                 this.waitMsgTarget.unmask();
50321             }else{
50322                 Roo.MessageBox.updateProgress(1);
50323                 Roo.MessageBox.hide();
50324             }
50325         }
50326         
50327         if(success){
50328             if(o.reset){
50329                 this.reset();
50330             }
50331             Roo.callback(o.success, o.scope, [this, action]);
50332             this.fireEvent('actioncomplete', this, action);
50333             
50334         }else{
50335             
50336             // failure condition..
50337             // we have a scenario where updates need confirming.
50338             // eg. if a locking scenario exists..
50339             // we look for { errors : { needs_confirm : true }} in the response.
50340             if (
50341                 (typeof(action.result) != 'undefined')  &&
50342                 (typeof(action.result.errors) != 'undefined')  &&
50343                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50344            ){
50345                 var _t = this;
50346                 Roo.MessageBox.confirm(
50347                     "Change requires confirmation",
50348                     action.result.errorMsg,
50349                     function(r) {
50350                         if (r != 'yes') {
50351                             return;
50352                         }
50353                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50354                     }
50355                     
50356                 );
50357                 
50358                 
50359                 
50360                 return;
50361             }
50362             
50363             Roo.callback(o.failure, o.scope, [this, action]);
50364             // show an error message if no failed handler is set..
50365             if (!this.hasListener('actionfailed')) {
50366                 Roo.MessageBox.alert("Error",
50367                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50368                         action.result.errorMsg :
50369                         "Saving Failed, please check your entries or try again"
50370                 );
50371             }
50372             
50373             this.fireEvent('actionfailed', this, action);
50374         }
50375         
50376     },
50377
50378     /**
50379      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50380      * @param {String} id The value to search for
50381      * @return Field
50382      */
50383     findField : function(id){
50384         var field = this.items.get(id);
50385         if(!field){
50386             this.items.each(function(f){
50387                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50388                     field = f;
50389                     return false;
50390                 }
50391             });
50392         }
50393         return field || null;
50394     },
50395
50396     /**
50397      * Add a secondary form to this one, 
50398      * Used to provide tabbed forms. One form is primary, with hidden values 
50399      * which mirror the elements from the other forms.
50400      * 
50401      * @param {Roo.form.Form} form to add.
50402      * 
50403      */
50404     addForm : function(form)
50405     {
50406        
50407         if (this.childForms.indexOf(form) > -1) {
50408             // already added..
50409             return;
50410         }
50411         this.childForms.push(form);
50412         var n = '';
50413         Roo.each(form.allItems, function (fe) {
50414             
50415             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50416             if (this.findField(n)) { // already added..
50417                 return;
50418             }
50419             var add = new Roo.form.Hidden({
50420                 name : n
50421             });
50422             add.render(this.el);
50423             
50424             this.add( add );
50425         }, this);
50426         
50427     },
50428     /**
50429      * Mark fields in this form invalid in bulk.
50430      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50431      * @return {BasicForm} this
50432      */
50433     markInvalid : function(errors){
50434         if(errors instanceof Array){
50435             for(var i = 0, len = errors.length; i < len; i++){
50436                 var fieldError = errors[i];
50437                 var f = this.findField(fieldError.id);
50438                 if(f){
50439                     f.markInvalid(fieldError.msg);
50440                 }
50441             }
50442         }else{
50443             var field, id;
50444             for(id in errors){
50445                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50446                     field.markInvalid(errors[id]);
50447                 }
50448             }
50449         }
50450         Roo.each(this.childForms || [], function (f) {
50451             f.markInvalid(errors);
50452         });
50453         
50454         return this;
50455     },
50456
50457     /**
50458      * Set values for fields in this form in bulk.
50459      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50460      * @return {BasicForm} this
50461      */
50462     setValues : function(values){
50463         if(values instanceof Array){ // array of objects
50464             for(var i = 0, len = values.length; i < len; i++){
50465                 var v = values[i];
50466                 var f = this.findField(v.id);
50467                 if(f){
50468                     f.setValue(v.value);
50469                     if(this.trackResetOnLoad){
50470                         f.originalValue = f.getValue();
50471                     }
50472                 }
50473             }
50474         }else{ // object hash
50475             var field, id;
50476             for(id in values){
50477                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50478                     
50479                     if (field.setFromData && 
50480                         field.valueField && 
50481                         field.displayField &&
50482                         // combos' with local stores can 
50483                         // be queried via setValue()
50484                         // to set their value..
50485                         (field.store && !field.store.isLocal)
50486                         ) {
50487                         // it's a combo
50488                         var sd = { };
50489                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50490                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50491                         field.setFromData(sd);
50492                         
50493                     } else {
50494                         field.setValue(values[id]);
50495                     }
50496                     
50497                     
50498                     if(this.trackResetOnLoad){
50499                         field.originalValue = field.getValue();
50500                     }
50501                 }
50502             }
50503         }
50504         this.resetHasChanged();
50505         
50506         
50507         Roo.each(this.childForms || [], function (f) {
50508             f.setValues(values);
50509             f.resetHasChanged();
50510         });
50511                 
50512         return this;
50513     },
50514  
50515     /**
50516      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50517      * they are returned as an array.
50518      * @param {Boolean} asString
50519      * @return {Object}
50520      */
50521     getValues : function(asString){
50522         if (this.childForms) {
50523             // copy values from the child forms
50524             Roo.each(this.childForms, function (f) {
50525                 this.setValues(f.getValues());
50526             }, this);
50527         }
50528         
50529         // use formdata
50530         if (typeof(FormData) != 'undefined' && asString !== true) {
50531             // this relies on a 'recent' version of chrome apparently...
50532             try {
50533                 var fd = (new FormData(this.el.dom)).entries();
50534                 var ret = {};
50535                 var ent = fd.next();
50536                 while (!ent.done) {
50537                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50538                     ent = fd.next();
50539                 };
50540                 return ret;
50541             } catch(e) {
50542                 
50543             }
50544             
50545         }
50546         
50547         
50548         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50549         if(asString === true){
50550             return fs;
50551         }
50552         return Roo.urlDecode(fs);
50553     },
50554     
50555     /**
50556      * Returns the fields in this form as an object with key/value pairs. 
50557      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50558      * @return {Object}
50559      */
50560     getFieldValues : function(with_hidden)
50561     {
50562         if (this.childForms) {
50563             // copy values from the child forms
50564             // should this call getFieldValues - probably not as we do not currently copy
50565             // hidden fields when we generate..
50566             Roo.each(this.childForms, function (f) {
50567                 this.setValues(f.getValues());
50568             }, this);
50569         }
50570         
50571         var ret = {};
50572         this.items.each(function(f){
50573             if (!f.getName()) {
50574                 return;
50575             }
50576             var v = f.getValue();
50577             if (f.inputType =='radio') {
50578                 if (typeof(ret[f.getName()]) == 'undefined') {
50579                     ret[f.getName()] = ''; // empty..
50580                 }
50581                 
50582                 if (!f.el.dom.checked) {
50583                     return;
50584                     
50585                 }
50586                 v = f.el.dom.value;
50587                 
50588             }
50589             
50590             // not sure if this supported any more..
50591             if ((typeof(v) == 'object') && f.getRawValue) {
50592                 v = f.getRawValue() ; // dates..
50593             }
50594             // combo boxes where name != hiddenName...
50595             if (f.name != f.getName()) {
50596                 ret[f.name] = f.getRawValue();
50597             }
50598             ret[f.getName()] = v;
50599         });
50600         
50601         return ret;
50602     },
50603
50604     /**
50605      * Clears all invalid messages in this form.
50606      * @return {BasicForm} this
50607      */
50608     clearInvalid : function(){
50609         this.items.each(function(f){
50610            f.clearInvalid();
50611         });
50612         
50613         Roo.each(this.childForms || [], function (f) {
50614             f.clearInvalid();
50615         });
50616         
50617         
50618         return this;
50619     },
50620
50621     /**
50622      * Resets this form.
50623      * @return {BasicForm} this
50624      */
50625     reset : function(){
50626         this.items.each(function(f){
50627             f.reset();
50628         });
50629         
50630         Roo.each(this.childForms || [], function (f) {
50631             f.reset();
50632         });
50633         this.resetHasChanged();
50634         
50635         return this;
50636     },
50637
50638     /**
50639      * Add Roo.form components to this form.
50640      * @param {Field} field1
50641      * @param {Field} field2 (optional)
50642      * @param {Field} etc (optional)
50643      * @return {BasicForm} this
50644      */
50645     add : function(){
50646         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50647         return this;
50648     },
50649
50650
50651     /**
50652      * Removes a field from the items collection (does NOT remove its markup).
50653      * @param {Field} field
50654      * @return {BasicForm} this
50655      */
50656     remove : function(field){
50657         this.items.remove(field);
50658         return this;
50659     },
50660
50661     /**
50662      * Looks at the fields in this form, checks them for an id attribute,
50663      * and calls applyTo on the existing dom element with that id.
50664      * @return {BasicForm} this
50665      */
50666     render : function(){
50667         this.items.each(function(f){
50668             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50669                 f.applyTo(f.id);
50670             }
50671         });
50672         return this;
50673     },
50674
50675     /**
50676      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50677      * @param {Object} values
50678      * @return {BasicForm} this
50679      */
50680     applyToFields : function(o){
50681         this.items.each(function(f){
50682            Roo.apply(f, o);
50683         });
50684         return this;
50685     },
50686
50687     /**
50688      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50689      * @param {Object} values
50690      * @return {BasicForm} this
50691      */
50692     applyIfToFields : function(o){
50693         this.items.each(function(f){
50694            Roo.applyIf(f, o);
50695         });
50696         return this;
50697     }
50698 });
50699
50700 // back compat
50701 Roo.BasicForm = Roo.form.BasicForm;
50702
50703 Roo.apply(Roo.form.BasicForm, {
50704     
50705     popover : {
50706         
50707         padding : 5,
50708         
50709         isApplied : false,
50710         
50711         isMasked : false,
50712         
50713         form : false,
50714         
50715         target : false,
50716         
50717         intervalID : false,
50718         
50719         maskEl : false,
50720         
50721         apply : function()
50722         {
50723             if(this.isApplied){
50724                 return;
50725             }
50726             
50727             this.maskEl = {
50728                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
50729                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
50730                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
50731                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
50732             };
50733             
50734             this.maskEl.top.enableDisplayMode("block");
50735             this.maskEl.left.enableDisplayMode("block");
50736             this.maskEl.bottom.enableDisplayMode("block");
50737             this.maskEl.right.enableDisplayMode("block");
50738             
50739             Roo.get(document.body).on('click', function(){
50740                 this.unmask();
50741             }, this);
50742             
50743             Roo.get(document.body).on('touchstart', function(){
50744                 this.unmask();
50745             }, this);
50746             
50747             this.isApplied = true
50748         },
50749         
50750         mask : function(form, target)
50751         {
50752             this.form = form;
50753             
50754             this.target = target;
50755             
50756             if(!this.form.errorMask || !target.el){
50757                 return;
50758             }
50759             
50760             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
50761             
50762             var ot = this.target.el.calcOffsetsTo(scrollable);
50763             
50764             var scrollTo = ot[1] - this.form.maskOffset;
50765             
50766             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
50767             
50768             scrollable.scrollTo('top', scrollTo);
50769             
50770             var el = this.target.wrap || this.target.el;
50771             
50772             var box = el.getBox();
50773             
50774             this.maskEl.top.setStyle('position', 'absolute');
50775             this.maskEl.top.setStyle('z-index', 10000);
50776             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
50777             this.maskEl.top.setLeft(0);
50778             this.maskEl.top.setTop(0);
50779             this.maskEl.top.show();
50780             
50781             this.maskEl.left.setStyle('position', 'absolute');
50782             this.maskEl.left.setStyle('z-index', 10000);
50783             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
50784             this.maskEl.left.setLeft(0);
50785             this.maskEl.left.setTop(box.y - this.padding);
50786             this.maskEl.left.show();
50787
50788             this.maskEl.bottom.setStyle('position', 'absolute');
50789             this.maskEl.bottom.setStyle('z-index', 10000);
50790             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
50791             this.maskEl.bottom.setLeft(0);
50792             this.maskEl.bottom.setTop(box.bottom + this.padding);
50793             this.maskEl.bottom.show();
50794
50795             this.maskEl.right.setStyle('position', 'absolute');
50796             this.maskEl.right.setStyle('z-index', 10000);
50797             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
50798             this.maskEl.right.setLeft(box.right + this.padding);
50799             this.maskEl.right.setTop(box.y - this.padding);
50800             this.maskEl.right.show();
50801
50802             this.intervalID = window.setInterval(function() {
50803                 Roo.form.BasicForm.popover.unmask();
50804             }, 10000);
50805
50806             window.onwheel = function(){ return false;};
50807             
50808             (function(){ this.isMasked = true; }).defer(500, this);
50809             
50810         },
50811         
50812         unmask : function()
50813         {
50814             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
50815                 return;
50816             }
50817             
50818             this.maskEl.top.setStyle('position', 'absolute');
50819             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
50820             this.maskEl.top.hide();
50821
50822             this.maskEl.left.setStyle('position', 'absolute');
50823             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
50824             this.maskEl.left.hide();
50825
50826             this.maskEl.bottom.setStyle('position', 'absolute');
50827             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
50828             this.maskEl.bottom.hide();
50829
50830             this.maskEl.right.setStyle('position', 'absolute');
50831             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
50832             this.maskEl.right.hide();
50833             
50834             window.onwheel = function(){ return true;};
50835             
50836             if(this.intervalID){
50837                 window.clearInterval(this.intervalID);
50838                 this.intervalID = false;
50839             }
50840             
50841             this.isMasked = false;
50842             
50843         }
50844         
50845     }
50846     
50847 });/*
50848  * Based on:
50849  * Ext JS Library 1.1.1
50850  * Copyright(c) 2006-2007, Ext JS, LLC.
50851  *
50852  * Originally Released Under LGPL - original licence link has changed is not relivant.
50853  *
50854  * Fork - LGPL
50855  * <script type="text/javascript">
50856  */
50857
50858 /**
50859  * @class Roo.form.Form
50860  * @extends Roo.form.BasicForm
50861  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50862  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
50863  * @constructor
50864  * @param {Object} config Configuration options
50865  */
50866 Roo.form.Form = function(config){
50867     var xitems =  [];
50868     if (config.items) {
50869         xitems = config.items;
50870         delete config.items;
50871     }
50872    
50873     
50874     Roo.form.Form.superclass.constructor.call(this, null, config);
50875     this.url = this.url || this.action;
50876     if(!this.root){
50877         this.root = new Roo.form.Layout(Roo.applyIf({
50878             id: Roo.id()
50879         }, config));
50880     }
50881     this.active = this.root;
50882     /**
50883      * Array of all the buttons that have been added to this form via {@link addButton}
50884      * @type Array
50885      */
50886     this.buttons = [];
50887     this.allItems = [];
50888     this.addEvents({
50889         /**
50890          * @event clientvalidation
50891          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
50892          * @param {Form} this
50893          * @param {Boolean} valid true if the form has passed client-side validation
50894          */
50895         clientvalidation: true,
50896         /**
50897          * @event rendered
50898          * Fires when the form is rendered
50899          * @param {Roo.form.Form} form
50900          */
50901         rendered : true
50902     });
50903     
50904     if (this.progressUrl) {
50905             // push a hidden field onto the list of fields..
50906             this.addxtype( {
50907                     xns: Roo.form, 
50908                     xtype : 'Hidden', 
50909                     name : 'UPLOAD_IDENTIFIER' 
50910             });
50911         }
50912         
50913     
50914     Roo.each(xitems, this.addxtype, this);
50915     
50916 };
50917
50918 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
50919      /**
50920      * @cfg {Roo.Button} buttons[] buttons at bottom of form
50921      */
50922     
50923     /**
50924      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
50925      */
50926     /**
50927      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
50928      */
50929     /**
50930      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
50931      */
50932     buttonAlign:'center',
50933
50934     /**
50935      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
50936      */
50937     minButtonWidth:75,
50938
50939     /**
50940      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
50941      * This property cascades to child containers if not set.
50942      */
50943     labelAlign:'left',
50944
50945     /**
50946      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
50947      * fires a looping event with that state. This is required to bind buttons to the valid
50948      * state using the config value formBind:true on the button.
50949      */
50950     monitorValid : false,
50951
50952     /**
50953      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
50954      */
50955     monitorPoll : 200,
50956     
50957     /**
50958      * @cfg {String} progressUrl - Url to return progress data 
50959      */
50960     
50961     progressUrl : false,
50962     /**
50963      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
50964      * sending a formdata with extra parameters - eg uploaded elements.
50965      */
50966     
50967     formData : false,
50968     
50969     /**
50970      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
50971      * fields are added and the column is closed. If no fields are passed the column remains open
50972      * until end() is called.
50973      * @param {Object} config The config to pass to the column
50974      * @param {Field} field1 (optional)
50975      * @param {Field} field2 (optional)
50976      * @param {Field} etc (optional)
50977      * @return Column The column container object
50978      */
50979     column : function(c){
50980         var col = new Roo.form.Column(c);
50981         this.start(col);
50982         if(arguments.length > 1){ // duplicate code required because of Opera
50983             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50984             this.end();
50985         }
50986         return col;
50987     },
50988
50989     /**
50990      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
50991      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
50992      * until end() is called.
50993      * @param {Object} config The config to pass to the fieldset
50994      * @param {Field} field1 (optional)
50995      * @param {Field} field2 (optional)
50996      * @param {Field} etc (optional)
50997      * @return FieldSet The fieldset container object
50998      */
50999     fieldset : function(c){
51000         var fs = new Roo.form.FieldSet(c);
51001         this.start(fs);
51002         if(arguments.length > 1){ // duplicate code required because of Opera
51003             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51004             this.end();
51005         }
51006         return fs;
51007     },
51008
51009     /**
51010      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51011      * fields are added and the container is closed. If no fields are passed the container remains open
51012      * until end() is called.
51013      * @param {Object} config The config to pass to the Layout
51014      * @param {Field} field1 (optional)
51015      * @param {Field} field2 (optional)
51016      * @param {Field} etc (optional)
51017      * @return Layout The container object
51018      */
51019     container : function(c){
51020         var l = new Roo.form.Layout(c);
51021         this.start(l);
51022         if(arguments.length > 1){ // duplicate code required because of Opera
51023             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51024             this.end();
51025         }
51026         return l;
51027     },
51028
51029     /**
51030      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51031      * @param {Object} container A Roo.form.Layout or subclass of Layout
51032      * @return {Form} this
51033      */
51034     start : function(c){
51035         // cascade label info
51036         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51037         this.active.stack.push(c);
51038         c.ownerCt = this.active;
51039         this.active = c;
51040         return this;
51041     },
51042
51043     /**
51044      * Closes the current open container
51045      * @return {Form} this
51046      */
51047     end : function(){
51048         if(this.active == this.root){
51049             return this;
51050         }
51051         this.active = this.active.ownerCt;
51052         return this;
51053     },
51054
51055     /**
51056      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51057      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51058      * as the label of the field.
51059      * @param {Field} field1
51060      * @param {Field} field2 (optional)
51061      * @param {Field} etc. (optional)
51062      * @return {Form} this
51063      */
51064     add : function(){
51065         this.active.stack.push.apply(this.active.stack, arguments);
51066         this.allItems.push.apply(this.allItems,arguments);
51067         var r = [];
51068         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51069             if(a[i].isFormField){
51070                 r.push(a[i]);
51071             }
51072         }
51073         if(r.length > 0){
51074             Roo.form.Form.superclass.add.apply(this, r);
51075         }
51076         return this;
51077     },
51078     
51079
51080     
51081     
51082     
51083      /**
51084      * Find any element that has been added to a form, using it's ID or name
51085      * This can include framesets, columns etc. along with regular fields..
51086      * @param {String} id - id or name to find.
51087      
51088      * @return {Element} e - or false if nothing found.
51089      */
51090     findbyId : function(id)
51091     {
51092         var ret = false;
51093         if (!id) {
51094             return ret;
51095         }
51096         Roo.each(this.allItems, function(f){
51097             if (f.id == id || f.name == id ){
51098                 ret = f;
51099                 return false;
51100             }
51101         });
51102         return ret;
51103     },
51104
51105     
51106     
51107     /**
51108      * Render this form into the passed container. This should only be called once!
51109      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51110      * @return {Form} this
51111      */
51112     render : function(ct)
51113     {
51114         
51115         
51116         
51117         ct = Roo.get(ct);
51118         var o = this.autoCreate || {
51119             tag: 'form',
51120             method : this.method || 'POST',
51121             id : this.id || Roo.id()
51122         };
51123         this.initEl(ct.createChild(o));
51124
51125         this.root.render(this.el);
51126         
51127        
51128              
51129         this.items.each(function(f){
51130             f.render('x-form-el-'+f.id);
51131         });
51132
51133         if(this.buttons.length > 0){
51134             // tables are required to maintain order and for correct IE layout
51135             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51136                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51137                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51138             }}, null, true);
51139             var tr = tb.getElementsByTagName('tr')[0];
51140             for(var i = 0, len = this.buttons.length; i < len; i++) {
51141                 var b = this.buttons[i];
51142                 var td = document.createElement('td');
51143                 td.className = 'x-form-btn-td';
51144                 b.render(tr.appendChild(td));
51145             }
51146         }
51147         if(this.monitorValid){ // initialize after render
51148             this.startMonitoring();
51149         }
51150         this.fireEvent('rendered', this);
51151         return this;
51152     },
51153
51154     /**
51155      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51156      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51157      * object or a valid Roo.DomHelper element config
51158      * @param {Function} handler The function called when the button is clicked
51159      * @param {Object} scope (optional) The scope of the handler function
51160      * @return {Roo.Button}
51161      */
51162     addButton : function(config, handler, scope){
51163         var bc = {
51164             handler: handler,
51165             scope: scope,
51166             minWidth: this.minButtonWidth,
51167             hideParent:true
51168         };
51169         if(typeof config == "string"){
51170             bc.text = config;
51171         }else{
51172             Roo.apply(bc, config);
51173         }
51174         var btn = new Roo.Button(null, bc);
51175         this.buttons.push(btn);
51176         return btn;
51177     },
51178
51179      /**
51180      * Adds a series of form elements (using the xtype property as the factory method.
51181      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51182      * @param {Object} config 
51183      */
51184     
51185     addxtype : function()
51186     {
51187         var ar = Array.prototype.slice.call(arguments, 0);
51188         var ret = false;
51189         for(var i = 0; i < ar.length; i++) {
51190             if (!ar[i]) {
51191                 continue; // skip -- if this happends something invalid got sent, we 
51192                 // should ignore it, as basically that interface element will not show up
51193                 // and that should be pretty obvious!!
51194             }
51195             
51196             if (Roo.form[ar[i].xtype]) {
51197                 ar[i].form = this;
51198                 var fe = Roo.factory(ar[i], Roo.form);
51199                 if (!ret) {
51200                     ret = fe;
51201                 }
51202                 fe.form = this;
51203                 if (fe.store) {
51204                     fe.store.form = this;
51205                 }
51206                 if (fe.isLayout) {  
51207                          
51208                     this.start(fe);
51209                     this.allItems.push(fe);
51210                     if (fe.items && fe.addxtype) {
51211                         fe.addxtype.apply(fe, fe.items);
51212                         delete fe.items;
51213                     }
51214                      this.end();
51215                     continue;
51216                 }
51217                 
51218                 
51219                  
51220                 this.add(fe);
51221               //  console.log('adding ' + ar[i].xtype);
51222             }
51223             if (ar[i].xtype == 'Button') {  
51224                 //console.log('adding button');
51225                 //console.log(ar[i]);
51226                 this.addButton(ar[i]);
51227                 this.allItems.push(fe);
51228                 continue;
51229             }
51230             
51231             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51232                 alert('end is not supported on xtype any more, use items');
51233             //    this.end();
51234             //    //console.log('adding end');
51235             }
51236             
51237         }
51238         return ret;
51239     },
51240     
51241     /**
51242      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51243      * option "monitorValid"
51244      */
51245     startMonitoring : function(){
51246         if(!this.bound){
51247             this.bound = true;
51248             Roo.TaskMgr.start({
51249                 run : this.bindHandler,
51250                 interval : this.monitorPoll || 200,
51251                 scope: this
51252             });
51253         }
51254     },
51255
51256     /**
51257      * Stops monitoring of the valid state of this form
51258      */
51259     stopMonitoring : function(){
51260         this.bound = false;
51261     },
51262
51263     // private
51264     bindHandler : function(){
51265         if(!this.bound){
51266             return false; // stops binding
51267         }
51268         var valid = true;
51269         this.items.each(function(f){
51270             if(!f.isValid(true)){
51271                 valid = false;
51272                 return false;
51273             }
51274         });
51275         for(var i = 0, len = this.buttons.length; i < len; i++){
51276             var btn = this.buttons[i];
51277             if(btn.formBind === true && btn.disabled === valid){
51278                 btn.setDisabled(!valid);
51279             }
51280         }
51281         this.fireEvent('clientvalidation', this, valid);
51282     }
51283     
51284     
51285     
51286     
51287     
51288     
51289     
51290     
51291 });
51292
51293
51294 // back compat
51295 Roo.Form = Roo.form.Form;
51296 /*
51297  * Based on:
51298  * Ext JS Library 1.1.1
51299  * Copyright(c) 2006-2007, Ext JS, LLC.
51300  *
51301  * Originally Released Under LGPL - original licence link has changed is not relivant.
51302  *
51303  * Fork - LGPL
51304  * <script type="text/javascript">
51305  */
51306
51307 // as we use this in bootstrap.
51308 Roo.namespace('Roo.form');
51309  /**
51310  * @class Roo.form.Action
51311  * Internal Class used to handle form actions
51312  * @constructor
51313  * @param {Roo.form.BasicForm} el The form element or its id
51314  * @param {Object} config Configuration options
51315  */
51316
51317  
51318  
51319 // define the action interface
51320 Roo.form.Action = function(form, options){
51321     this.form = form;
51322     this.options = options || {};
51323 };
51324 /**
51325  * Client Validation Failed
51326  * @const 
51327  */
51328 Roo.form.Action.CLIENT_INVALID = 'client';
51329 /**
51330  * Server Validation Failed
51331  * @const 
51332  */
51333 Roo.form.Action.SERVER_INVALID = 'server';
51334  /**
51335  * Connect to Server Failed
51336  * @const 
51337  */
51338 Roo.form.Action.CONNECT_FAILURE = 'connect';
51339 /**
51340  * Reading Data from Server Failed
51341  * @const 
51342  */
51343 Roo.form.Action.LOAD_FAILURE = 'load';
51344
51345 Roo.form.Action.prototype = {
51346     type : 'default',
51347     failureType : undefined,
51348     response : undefined,
51349     result : undefined,
51350
51351     // interface method
51352     run : function(options){
51353
51354     },
51355
51356     // interface method
51357     success : function(response){
51358
51359     },
51360
51361     // interface method
51362     handleResponse : function(response){
51363
51364     },
51365
51366     // default connection failure
51367     failure : function(response){
51368         
51369         this.response = response;
51370         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51371         this.form.afterAction(this, false);
51372     },
51373
51374     processResponse : function(response){
51375         this.response = response;
51376         if(!response.responseText){
51377             return true;
51378         }
51379         this.result = this.handleResponse(response);
51380         return this.result;
51381     },
51382
51383     // utility functions used internally
51384     getUrl : function(appendParams){
51385         var url = this.options.url || this.form.url || this.form.el.dom.action;
51386         if(appendParams){
51387             var p = this.getParams();
51388             if(p){
51389                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51390             }
51391         }
51392         return url;
51393     },
51394
51395     getMethod : function(){
51396         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51397     },
51398
51399     getParams : function(){
51400         var bp = this.form.baseParams;
51401         var p = this.options.params;
51402         if(p){
51403             if(typeof p == "object"){
51404                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51405             }else if(typeof p == 'string' && bp){
51406                 p += '&' + Roo.urlEncode(bp);
51407             }
51408         }else if(bp){
51409             p = Roo.urlEncode(bp);
51410         }
51411         return p;
51412     },
51413
51414     createCallback : function(){
51415         return {
51416             success: this.success,
51417             failure: this.failure,
51418             scope: this,
51419             timeout: (this.form.timeout*1000),
51420             upload: this.form.fileUpload ? this.success : undefined
51421         };
51422     }
51423 };
51424
51425 Roo.form.Action.Submit = function(form, options){
51426     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51427 };
51428
51429 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51430     type : 'submit',
51431
51432     haveProgress : false,
51433     uploadComplete : false,
51434     
51435     // uploadProgress indicator.
51436     uploadProgress : function()
51437     {
51438         if (!this.form.progressUrl) {
51439             return;
51440         }
51441         
51442         if (!this.haveProgress) {
51443             Roo.MessageBox.progress("Uploading", "Uploading");
51444         }
51445         if (this.uploadComplete) {
51446            Roo.MessageBox.hide();
51447            return;
51448         }
51449         
51450         this.haveProgress = true;
51451    
51452         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51453         
51454         var c = new Roo.data.Connection();
51455         c.request({
51456             url : this.form.progressUrl,
51457             params: {
51458                 id : uid
51459             },
51460             method: 'GET',
51461             success : function(req){
51462                //console.log(data);
51463                 var rdata = false;
51464                 var edata;
51465                 try  {
51466                    rdata = Roo.decode(req.responseText)
51467                 } catch (e) {
51468                     Roo.log("Invalid data from server..");
51469                     Roo.log(edata);
51470                     return;
51471                 }
51472                 if (!rdata || !rdata.success) {
51473                     Roo.log(rdata);
51474                     Roo.MessageBox.alert(Roo.encode(rdata));
51475                     return;
51476                 }
51477                 var data = rdata.data;
51478                 
51479                 if (this.uploadComplete) {
51480                    Roo.MessageBox.hide();
51481                    return;
51482                 }
51483                    
51484                 if (data){
51485                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51486                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51487                     );
51488                 }
51489                 this.uploadProgress.defer(2000,this);
51490             },
51491        
51492             failure: function(data) {
51493                 Roo.log('progress url failed ');
51494                 Roo.log(data);
51495             },
51496             scope : this
51497         });
51498            
51499     },
51500     
51501     
51502     run : function()
51503     {
51504         // run get Values on the form, so it syncs any secondary forms.
51505         this.form.getValues();
51506         
51507         var o = this.options;
51508         var method = this.getMethod();
51509         var isPost = method == 'POST';
51510         if(o.clientValidation === false || this.form.isValid()){
51511             
51512             if (this.form.progressUrl) {
51513                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51514                     (new Date() * 1) + '' + Math.random());
51515                     
51516             } 
51517             
51518             
51519             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51520                 form:this.form.el.dom,
51521                 url:this.getUrl(!isPost),
51522                 method: method,
51523                 params:isPost ? this.getParams() : null,
51524                 isUpload: this.form.fileUpload,
51525                 formData : this.form.formData
51526             }));
51527             
51528             this.uploadProgress();
51529
51530         }else if (o.clientValidation !== false){ // client validation failed
51531             this.failureType = Roo.form.Action.CLIENT_INVALID;
51532             this.form.afterAction(this, false);
51533         }
51534     },
51535
51536     success : function(response)
51537     {
51538         this.uploadComplete= true;
51539         if (this.haveProgress) {
51540             Roo.MessageBox.hide();
51541         }
51542         
51543         
51544         var result = this.processResponse(response);
51545         if(result === true || result.success){
51546             this.form.afterAction(this, true);
51547             return;
51548         }
51549         if(result.errors){
51550             this.form.markInvalid(result.errors);
51551             this.failureType = Roo.form.Action.SERVER_INVALID;
51552         }
51553         this.form.afterAction(this, false);
51554     },
51555     failure : function(response)
51556     {
51557         this.uploadComplete= true;
51558         if (this.haveProgress) {
51559             Roo.MessageBox.hide();
51560         }
51561         
51562         this.response = response;
51563         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51564         this.form.afterAction(this, false);
51565     },
51566     
51567     handleResponse : function(response){
51568         if(this.form.errorReader){
51569             var rs = this.form.errorReader.read(response);
51570             var errors = [];
51571             if(rs.records){
51572                 for(var i = 0, len = rs.records.length; i < len; i++) {
51573                     var r = rs.records[i];
51574                     errors[i] = r.data;
51575                 }
51576             }
51577             if(errors.length < 1){
51578                 errors = null;
51579             }
51580             return {
51581                 success : rs.success,
51582                 errors : errors
51583             };
51584         }
51585         var ret = false;
51586         try {
51587             ret = Roo.decode(response.responseText);
51588         } catch (e) {
51589             ret = {
51590                 success: false,
51591                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51592                 errors : []
51593             };
51594         }
51595         return ret;
51596         
51597     }
51598 });
51599
51600
51601 Roo.form.Action.Load = function(form, options){
51602     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51603     this.reader = this.form.reader;
51604 };
51605
51606 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51607     type : 'load',
51608
51609     run : function(){
51610         
51611         Roo.Ajax.request(Roo.apply(
51612                 this.createCallback(), {
51613                     method:this.getMethod(),
51614                     url:this.getUrl(false),
51615                     params:this.getParams()
51616         }));
51617     },
51618
51619     success : function(response){
51620         
51621         var result = this.processResponse(response);
51622         if(result === true || !result.success || !result.data){
51623             this.failureType = Roo.form.Action.LOAD_FAILURE;
51624             this.form.afterAction(this, false);
51625             return;
51626         }
51627         this.form.clearInvalid();
51628         this.form.setValues(result.data);
51629         this.form.afterAction(this, true);
51630     },
51631
51632     handleResponse : function(response){
51633         if(this.form.reader){
51634             var rs = this.form.reader.read(response);
51635             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51636             return {
51637                 success : rs.success,
51638                 data : data
51639             };
51640         }
51641         return Roo.decode(response.responseText);
51642     }
51643 });
51644
51645 Roo.form.Action.ACTION_TYPES = {
51646     'load' : Roo.form.Action.Load,
51647     'submit' : Roo.form.Action.Submit
51648 };/*
51649  * Based on:
51650  * Ext JS Library 1.1.1
51651  * Copyright(c) 2006-2007, Ext JS, LLC.
51652  *
51653  * Originally Released Under LGPL - original licence link has changed is not relivant.
51654  *
51655  * Fork - LGPL
51656  * <script type="text/javascript">
51657  */
51658  
51659 /**
51660  * @class Roo.form.Layout
51661  * @extends Roo.Component
51662  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51663  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51664  * @constructor
51665  * @param {Object} config Configuration options
51666  */
51667 Roo.form.Layout = function(config){
51668     var xitems = [];
51669     if (config.items) {
51670         xitems = config.items;
51671         delete config.items;
51672     }
51673     Roo.form.Layout.superclass.constructor.call(this, config);
51674     this.stack = [];
51675     Roo.each(xitems, this.addxtype, this);
51676      
51677 };
51678
51679 Roo.extend(Roo.form.Layout, Roo.Component, {
51680     /**
51681      * @cfg {String/Object} autoCreate
51682      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51683      */
51684     /**
51685      * @cfg {String/Object/Function} style
51686      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51687      * a function which returns such a specification.
51688      */
51689     /**
51690      * @cfg {String} labelAlign
51691      * Valid values are "left," "top" and "right" (defaults to "left")
51692      */
51693     /**
51694      * @cfg {Number} labelWidth
51695      * Fixed width in pixels of all field labels (defaults to undefined)
51696      */
51697     /**
51698      * @cfg {Boolean} clear
51699      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51700      */
51701     clear : true,
51702     /**
51703      * @cfg {String} labelSeparator
51704      * The separator to use after field labels (defaults to ':')
51705      */
51706     labelSeparator : ':',
51707     /**
51708      * @cfg {Boolean} hideLabels
51709      * True to suppress the display of field labels in this layout (defaults to false)
51710      */
51711     hideLabels : false,
51712
51713     // private
51714     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51715     
51716     isLayout : true,
51717     
51718     // private
51719     onRender : function(ct, position){
51720         if(this.el){ // from markup
51721             this.el = Roo.get(this.el);
51722         }else {  // generate
51723             var cfg = this.getAutoCreate();
51724             this.el = ct.createChild(cfg, position);
51725         }
51726         if(this.style){
51727             this.el.applyStyles(this.style);
51728         }
51729         if(this.labelAlign){
51730             this.el.addClass('x-form-label-'+this.labelAlign);
51731         }
51732         if(this.hideLabels){
51733             this.labelStyle = "display:none";
51734             this.elementStyle = "padding-left:0;";
51735         }else{
51736             if(typeof this.labelWidth == 'number'){
51737                 this.labelStyle = "width:"+this.labelWidth+"px;";
51738                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
51739             }
51740             if(this.labelAlign == 'top'){
51741                 this.labelStyle = "width:auto;";
51742                 this.elementStyle = "padding-left:0;";
51743             }
51744         }
51745         var stack = this.stack;
51746         var slen = stack.length;
51747         if(slen > 0){
51748             if(!this.fieldTpl){
51749                 var t = new Roo.Template(
51750                     '<div class="x-form-item {5}">',
51751                         '<label for="{0}" style="{2}">{1}{4}</label>',
51752                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51753                         '</div>',
51754                     '</div><div class="x-form-clear-left"></div>'
51755                 );
51756                 t.disableFormats = true;
51757                 t.compile();
51758                 Roo.form.Layout.prototype.fieldTpl = t;
51759             }
51760             for(var i = 0; i < slen; i++) {
51761                 if(stack[i].isFormField){
51762                     this.renderField(stack[i]);
51763                 }else{
51764                     this.renderComponent(stack[i]);
51765                 }
51766             }
51767         }
51768         if(this.clear){
51769             this.el.createChild({cls:'x-form-clear'});
51770         }
51771     },
51772
51773     // private
51774     renderField : function(f){
51775         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
51776                f.id, //0
51777                f.fieldLabel, //1
51778                f.labelStyle||this.labelStyle||'', //2
51779                this.elementStyle||'', //3
51780                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
51781                f.itemCls||this.itemCls||''  //5
51782        ], true).getPrevSibling());
51783     },
51784
51785     // private
51786     renderComponent : function(c){
51787         c.render(c.isLayout ? this.el : this.el.createChild());    
51788     },
51789     /**
51790      * Adds a object form elements (using the xtype property as the factory method.)
51791      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
51792      * @param {Object} config 
51793      */
51794     addxtype : function(o)
51795     {
51796         // create the lement.
51797         o.form = this.form;
51798         var fe = Roo.factory(o, Roo.form);
51799         this.form.allItems.push(fe);
51800         this.stack.push(fe);
51801         
51802         if (fe.isFormField) {
51803             this.form.items.add(fe);
51804         }
51805          
51806         return fe;
51807     }
51808 });
51809
51810 /**
51811  * @class Roo.form.Column
51812  * @extends Roo.form.Layout
51813  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51814  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
51815  * @constructor
51816  * @param {Object} config Configuration options
51817  */
51818 Roo.form.Column = function(config){
51819     Roo.form.Column.superclass.constructor.call(this, config);
51820 };
51821
51822 Roo.extend(Roo.form.Column, Roo.form.Layout, {
51823     /**
51824      * @cfg {Number/String} width
51825      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51826      */
51827     /**
51828      * @cfg {String/Object} autoCreate
51829      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
51830      */
51831
51832     // private
51833     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
51834
51835     // private
51836     onRender : function(ct, position){
51837         Roo.form.Column.superclass.onRender.call(this, ct, position);
51838         if(this.width){
51839             this.el.setWidth(this.width);
51840         }
51841     }
51842 });
51843
51844
51845 /**
51846  * @class Roo.form.Row
51847  * @extends Roo.form.Layout
51848  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51849  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
51850  * @constructor
51851  * @param {Object} config Configuration options
51852  */
51853
51854  
51855 Roo.form.Row = function(config){
51856     Roo.form.Row.superclass.constructor.call(this, config);
51857 };
51858  
51859 Roo.extend(Roo.form.Row, Roo.form.Layout, {
51860       /**
51861      * @cfg {Number/String} width
51862      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51863      */
51864     /**
51865      * @cfg {Number/String} height
51866      * The fixed height of the column in pixels or CSS value (defaults to "auto")
51867      */
51868     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
51869     
51870     padWidth : 20,
51871     // private
51872     onRender : function(ct, position){
51873         //console.log('row render');
51874         if(!this.rowTpl){
51875             var t = new Roo.Template(
51876                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
51877                     '<label for="{0}" style="{2}">{1}{4}</label>',
51878                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51879                     '</div>',
51880                 '</div>'
51881             );
51882             t.disableFormats = true;
51883             t.compile();
51884             Roo.form.Layout.prototype.rowTpl = t;
51885         }
51886         this.fieldTpl = this.rowTpl;
51887         
51888         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
51889         var labelWidth = 100;
51890         
51891         if ((this.labelAlign != 'top')) {
51892             if (typeof this.labelWidth == 'number') {
51893                 labelWidth = this.labelWidth
51894             }
51895             this.padWidth =  20 + labelWidth;
51896             
51897         }
51898         
51899         Roo.form.Column.superclass.onRender.call(this, ct, position);
51900         if(this.width){
51901             this.el.setWidth(this.width);
51902         }
51903         if(this.height){
51904             this.el.setHeight(this.height);
51905         }
51906     },
51907     
51908     // private
51909     renderField : function(f){
51910         f.fieldEl = this.fieldTpl.append(this.el, [
51911                f.id, f.fieldLabel,
51912                f.labelStyle||this.labelStyle||'',
51913                this.elementStyle||'',
51914                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
51915                f.itemCls||this.itemCls||'',
51916                f.width ? f.width + this.padWidth : 160 + this.padWidth
51917        ],true);
51918     }
51919 });
51920  
51921
51922 /**
51923  * @class Roo.form.FieldSet
51924  * @extends Roo.form.Layout
51925  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51926  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
51927  * @constructor
51928  * @param {Object} config Configuration options
51929  */
51930 Roo.form.FieldSet = function(config){
51931     Roo.form.FieldSet.superclass.constructor.call(this, config);
51932 };
51933
51934 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
51935     /**
51936      * @cfg {String} legend
51937      * The text to display as the legend for the FieldSet (defaults to '')
51938      */
51939     /**
51940      * @cfg {String/Object} autoCreate
51941      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
51942      */
51943
51944     // private
51945     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
51946
51947     // private
51948     onRender : function(ct, position){
51949         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
51950         if(this.legend){
51951             this.setLegend(this.legend);
51952         }
51953     },
51954
51955     // private
51956     setLegend : function(text){
51957         if(this.rendered){
51958             this.el.child('legend').update(text);
51959         }
51960     }
51961 });/*
51962  * Based on:
51963  * Ext JS Library 1.1.1
51964  * Copyright(c) 2006-2007, Ext JS, LLC.
51965  *
51966  * Originally Released Under LGPL - original licence link has changed is not relivant.
51967  *
51968  * Fork - LGPL
51969  * <script type="text/javascript">
51970  */
51971 /**
51972  * @class Roo.form.VTypes
51973  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
51974  * @static
51975  */
51976 Roo.form.VTypes = function(){
51977     // closure these in so they are only created once.
51978     var alpha = /^[a-zA-Z_]+$/;
51979     var alphanum = /^[a-zA-Z0-9_]+$/;
51980     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
51981     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
51982
51983     // All these messages and functions are configurable
51984     return {
51985         /**
51986          * The function used to validate email addresses
51987          * @param {String} value The email address
51988          */
51989         'email' : function(v){
51990             return email.test(v);
51991         },
51992         /**
51993          * The error text to display when the email validation function returns false
51994          * @type String
51995          */
51996         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
51997         /**
51998          * The keystroke filter mask to be applied on email input
51999          * @type RegExp
52000          */
52001         'emailMask' : /[a-z0-9_\.\-@]/i,
52002
52003         /**
52004          * The function used to validate URLs
52005          * @param {String} value The URL
52006          */
52007         'url' : function(v){
52008             return url.test(v);
52009         },
52010         /**
52011          * The error text to display when the url validation function returns false
52012          * @type String
52013          */
52014         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52015         
52016         /**
52017          * The function used to validate alpha values
52018          * @param {String} value The value
52019          */
52020         'alpha' : function(v){
52021             return alpha.test(v);
52022         },
52023         /**
52024          * The error text to display when the alpha validation function returns false
52025          * @type String
52026          */
52027         'alphaText' : 'This field should only contain letters and _',
52028         /**
52029          * The keystroke filter mask to be applied on alpha input
52030          * @type RegExp
52031          */
52032         'alphaMask' : /[a-z_]/i,
52033
52034         /**
52035          * The function used to validate alphanumeric values
52036          * @param {String} value The value
52037          */
52038         'alphanum' : function(v){
52039             return alphanum.test(v);
52040         },
52041         /**
52042          * The error text to display when the alphanumeric validation function returns false
52043          * @type String
52044          */
52045         'alphanumText' : 'This field should only contain letters, numbers and _',
52046         /**
52047          * The keystroke filter mask to be applied on alphanumeric input
52048          * @type RegExp
52049          */
52050         'alphanumMask' : /[a-z0-9_]/i
52051     };
52052 }();//<script type="text/javascript">
52053
52054 /**
52055  * @class Roo.form.FCKeditor
52056  * @extends Roo.form.TextArea
52057  * Wrapper around the FCKEditor http://www.fckeditor.net
52058  * @constructor
52059  * Creates a new FCKeditor
52060  * @param {Object} config Configuration options
52061  */
52062 Roo.form.FCKeditor = function(config){
52063     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52064     this.addEvents({
52065          /**
52066          * @event editorinit
52067          * Fired when the editor is initialized - you can add extra handlers here..
52068          * @param {FCKeditor} this
52069          * @param {Object} the FCK object.
52070          */
52071         editorinit : true
52072     });
52073     
52074     
52075 };
52076 Roo.form.FCKeditor.editors = { };
52077 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52078 {
52079     //defaultAutoCreate : {
52080     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52081     //},
52082     // private
52083     /**
52084      * @cfg {Object} fck options - see fck manual for details.
52085      */
52086     fckconfig : false,
52087     
52088     /**
52089      * @cfg {Object} fck toolbar set (Basic or Default)
52090      */
52091     toolbarSet : 'Basic',
52092     /**
52093      * @cfg {Object} fck BasePath
52094      */ 
52095     basePath : '/fckeditor/',
52096     
52097     
52098     frame : false,
52099     
52100     value : '',
52101     
52102    
52103     onRender : function(ct, position)
52104     {
52105         if(!this.el){
52106             this.defaultAutoCreate = {
52107                 tag: "textarea",
52108                 style:"width:300px;height:60px;",
52109                 autocomplete: "new-password"
52110             };
52111         }
52112         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52113         /*
52114         if(this.grow){
52115             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52116             if(this.preventScrollbars){
52117                 this.el.setStyle("overflow", "hidden");
52118             }
52119             this.el.setHeight(this.growMin);
52120         }
52121         */
52122         //console.log('onrender' + this.getId() );
52123         Roo.form.FCKeditor.editors[this.getId()] = this;
52124          
52125
52126         this.replaceTextarea() ;
52127         
52128     },
52129     
52130     getEditor : function() {
52131         return this.fckEditor;
52132     },
52133     /**
52134      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52135      * @param {Mixed} value The value to set
52136      */
52137     
52138     
52139     setValue : function(value)
52140     {
52141         //console.log('setValue: ' + value);
52142         
52143         if(typeof(value) == 'undefined') { // not sure why this is happending...
52144             return;
52145         }
52146         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52147         
52148         //if(!this.el || !this.getEditor()) {
52149         //    this.value = value;
52150             //this.setValue.defer(100,this,[value]);    
52151         //    return;
52152         //} 
52153         
52154         if(!this.getEditor()) {
52155             return;
52156         }
52157         
52158         this.getEditor().SetData(value);
52159         
52160         //
52161
52162     },
52163
52164     /**
52165      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52166      * @return {Mixed} value The field value
52167      */
52168     getValue : function()
52169     {
52170         
52171         if (this.frame && this.frame.dom.style.display == 'none') {
52172             return Roo.form.FCKeditor.superclass.getValue.call(this);
52173         }
52174         
52175         if(!this.el || !this.getEditor()) {
52176            
52177            // this.getValue.defer(100,this); 
52178             return this.value;
52179         }
52180        
52181         
52182         var value=this.getEditor().GetData();
52183         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52184         return Roo.form.FCKeditor.superclass.getValue.call(this);
52185         
52186
52187     },
52188
52189     /**
52190      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52191      * @return {Mixed} value The field value
52192      */
52193     getRawValue : function()
52194     {
52195         if (this.frame && this.frame.dom.style.display == 'none') {
52196             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52197         }
52198         
52199         if(!this.el || !this.getEditor()) {
52200             //this.getRawValue.defer(100,this); 
52201             return this.value;
52202             return;
52203         }
52204         
52205         
52206         
52207         var value=this.getEditor().GetData();
52208         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52209         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52210          
52211     },
52212     
52213     setSize : function(w,h) {
52214         
52215         
52216         
52217         //if (this.frame && this.frame.dom.style.display == 'none') {
52218         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52219         //    return;
52220         //}
52221         //if(!this.el || !this.getEditor()) {
52222         //    this.setSize.defer(100,this, [w,h]); 
52223         //    return;
52224         //}
52225         
52226         
52227         
52228         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52229         
52230         this.frame.dom.setAttribute('width', w);
52231         this.frame.dom.setAttribute('height', h);
52232         this.frame.setSize(w,h);
52233         
52234     },
52235     
52236     toggleSourceEdit : function(value) {
52237         
52238       
52239          
52240         this.el.dom.style.display = value ? '' : 'none';
52241         this.frame.dom.style.display = value ?  'none' : '';
52242         
52243     },
52244     
52245     
52246     focus: function(tag)
52247     {
52248         if (this.frame.dom.style.display == 'none') {
52249             return Roo.form.FCKeditor.superclass.focus.call(this);
52250         }
52251         if(!this.el || !this.getEditor()) {
52252             this.focus.defer(100,this, [tag]); 
52253             return;
52254         }
52255         
52256         
52257         
52258         
52259         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52260         this.getEditor().Focus();
52261         if (tgs.length) {
52262             if (!this.getEditor().Selection.GetSelection()) {
52263                 this.focus.defer(100,this, [tag]); 
52264                 return;
52265             }
52266             
52267             
52268             var r = this.getEditor().EditorDocument.createRange();
52269             r.setStart(tgs[0],0);
52270             r.setEnd(tgs[0],0);
52271             this.getEditor().Selection.GetSelection().removeAllRanges();
52272             this.getEditor().Selection.GetSelection().addRange(r);
52273             this.getEditor().Focus();
52274         }
52275         
52276     },
52277     
52278     
52279     
52280     replaceTextarea : function()
52281     {
52282         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52283             return ;
52284         }
52285         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52286         //{
52287             // We must check the elements firstly using the Id and then the name.
52288         var oTextarea = document.getElementById( this.getId() );
52289         
52290         var colElementsByName = document.getElementsByName( this.getId() ) ;
52291          
52292         oTextarea.style.display = 'none' ;
52293
52294         if ( oTextarea.tabIndex ) {            
52295             this.TabIndex = oTextarea.tabIndex ;
52296         }
52297         
52298         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52299         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52300         this.frame = Roo.get(this.getId() + '___Frame')
52301     },
52302     
52303     _getConfigHtml : function()
52304     {
52305         var sConfig = '' ;
52306
52307         for ( var o in this.fckconfig ) {
52308             sConfig += sConfig.length > 0  ? '&amp;' : '';
52309             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52310         }
52311
52312         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52313     },
52314     
52315     
52316     _getIFrameHtml : function()
52317     {
52318         var sFile = 'fckeditor.html' ;
52319         /* no idea what this is about..
52320         try
52321         {
52322             if ( (/fcksource=true/i).test( window.top.location.search ) )
52323                 sFile = 'fckeditor.original.html' ;
52324         }
52325         catch (e) { 
52326         */
52327
52328         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52329         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52330         
52331         
52332         var html = '<iframe id="' + this.getId() +
52333             '___Frame" src="' + sLink +
52334             '" width="' + this.width +
52335             '" height="' + this.height + '"' +
52336             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52337             ' frameborder="0" scrolling="no"></iframe>' ;
52338
52339         return html ;
52340     },
52341     
52342     _insertHtmlBefore : function( html, element )
52343     {
52344         if ( element.insertAdjacentHTML )       {
52345             // IE
52346             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52347         } else { // Gecko
52348             var oRange = document.createRange() ;
52349             oRange.setStartBefore( element ) ;
52350             var oFragment = oRange.createContextualFragment( html );
52351             element.parentNode.insertBefore( oFragment, element ) ;
52352         }
52353     }
52354     
52355     
52356   
52357     
52358     
52359     
52360     
52361
52362 });
52363
52364 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52365
52366 function FCKeditor_OnComplete(editorInstance){
52367     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52368     f.fckEditor = editorInstance;
52369     //console.log("loaded");
52370     f.fireEvent('editorinit', f, editorInstance);
52371
52372   
52373
52374  
52375
52376
52377
52378
52379
52380
52381
52382
52383
52384
52385
52386
52387
52388
52389
52390 //<script type="text/javascript">
52391 /**
52392  * @class Roo.form.GridField
52393  * @extends Roo.form.Field
52394  * Embed a grid (or editable grid into a form)
52395  * STATUS ALPHA
52396  * 
52397  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52398  * it needs 
52399  * xgrid.store = Roo.data.Store
52400  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52401  * xgrid.store.reader = Roo.data.JsonReader 
52402  * 
52403  * 
52404  * @constructor
52405  * Creates a new GridField
52406  * @param {Object} config Configuration options
52407  */
52408 Roo.form.GridField = function(config){
52409     Roo.form.GridField.superclass.constructor.call(this, config);
52410      
52411 };
52412
52413 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52414     /**
52415      * @cfg {Number} width  - used to restrict width of grid..
52416      */
52417     width : 100,
52418     /**
52419      * @cfg {Number} height - used to restrict height of grid..
52420      */
52421     height : 50,
52422      /**
52423      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52424          * 
52425          *}
52426      */
52427     xgrid : false, 
52428     /**
52429      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52430      * {tag: "input", type: "checkbox", autocomplete: "off"})
52431      */
52432    // defaultAutoCreate : { tag: 'div' },
52433     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52434     /**
52435      * @cfg {String} addTitle Text to include for adding a title.
52436      */
52437     addTitle : false,
52438     //
52439     onResize : function(){
52440         Roo.form.Field.superclass.onResize.apply(this, arguments);
52441     },
52442
52443     initEvents : function(){
52444         // Roo.form.Checkbox.superclass.initEvents.call(this);
52445         // has no events...
52446        
52447     },
52448
52449
52450     getResizeEl : function(){
52451         return this.wrap;
52452     },
52453
52454     getPositionEl : function(){
52455         return this.wrap;
52456     },
52457
52458     // private
52459     onRender : function(ct, position){
52460         
52461         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52462         var style = this.style;
52463         delete this.style;
52464         
52465         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52466         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52467         this.viewEl = this.wrap.createChild({ tag: 'div' });
52468         if (style) {
52469             this.viewEl.applyStyles(style);
52470         }
52471         if (this.width) {
52472             this.viewEl.setWidth(this.width);
52473         }
52474         if (this.height) {
52475             this.viewEl.setHeight(this.height);
52476         }
52477         //if(this.inputValue !== undefined){
52478         //this.setValue(this.value);
52479         
52480         
52481         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52482         
52483         
52484         this.grid.render();
52485         this.grid.getDataSource().on('remove', this.refreshValue, this);
52486         this.grid.getDataSource().on('update', this.refreshValue, this);
52487         this.grid.on('afteredit', this.refreshValue, this);
52488  
52489     },
52490      
52491     
52492     /**
52493      * Sets the value of the item. 
52494      * @param {String} either an object  or a string..
52495      */
52496     setValue : function(v){
52497         //this.value = v;
52498         v = v || []; // empty set..
52499         // this does not seem smart - it really only affects memoryproxy grids..
52500         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52501             var ds = this.grid.getDataSource();
52502             // assumes a json reader..
52503             var data = {}
52504             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52505             ds.loadData( data);
52506         }
52507         // clear selection so it does not get stale.
52508         if (this.grid.sm) { 
52509             this.grid.sm.clearSelections();
52510         }
52511         
52512         Roo.form.GridField.superclass.setValue.call(this, v);
52513         this.refreshValue();
52514         // should load data in the grid really....
52515     },
52516     
52517     // private
52518     refreshValue: function() {
52519          var val = [];
52520         this.grid.getDataSource().each(function(r) {
52521             val.push(r.data);
52522         });
52523         this.el.dom.value = Roo.encode(val);
52524     }
52525     
52526      
52527     
52528     
52529 });/*
52530  * Based on:
52531  * Ext JS Library 1.1.1
52532  * Copyright(c) 2006-2007, Ext JS, LLC.
52533  *
52534  * Originally Released Under LGPL - original licence link has changed is not relivant.
52535  *
52536  * Fork - LGPL
52537  * <script type="text/javascript">
52538  */
52539 /**
52540  * @class Roo.form.DisplayField
52541  * @extends Roo.form.Field
52542  * A generic Field to display non-editable data.
52543  * @cfg {Boolean} closable (true|false) default false
52544  * @constructor
52545  * Creates a new Display Field item.
52546  * @param {Object} config Configuration options
52547  */
52548 Roo.form.DisplayField = function(config){
52549     Roo.form.DisplayField.superclass.constructor.call(this, config);
52550     
52551     this.addEvents({
52552         /**
52553          * @event close
52554          * Fires after the click the close btn
52555              * @param {Roo.form.DisplayField} this
52556              */
52557         close : true
52558     });
52559 };
52560
52561 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52562     inputType:      'hidden',
52563     allowBlank:     true,
52564     readOnly:         true,
52565     
52566  
52567     /**
52568      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52569      */
52570     focusClass : undefined,
52571     /**
52572      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52573      */
52574     fieldClass: 'x-form-field',
52575     
52576      /**
52577      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52578      */
52579     valueRenderer: undefined,
52580     
52581     width: 100,
52582     /**
52583      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52584      * {tag: "input", type: "checkbox", autocomplete: "off"})
52585      */
52586      
52587  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52588  
52589     closable : false,
52590     
52591     onResize : function(){
52592         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52593         
52594     },
52595
52596     initEvents : function(){
52597         // Roo.form.Checkbox.superclass.initEvents.call(this);
52598         // has no events...
52599         
52600         if(this.closable){
52601             this.closeEl.on('click', this.onClose, this);
52602         }
52603        
52604     },
52605
52606
52607     getResizeEl : function(){
52608         return this.wrap;
52609     },
52610
52611     getPositionEl : function(){
52612         return this.wrap;
52613     },
52614
52615     // private
52616     onRender : function(ct, position){
52617         
52618         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52619         //if(this.inputValue !== undefined){
52620         this.wrap = this.el.wrap();
52621         
52622         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52623         
52624         if(this.closable){
52625             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52626         }
52627         
52628         if (this.bodyStyle) {
52629             this.viewEl.applyStyles(this.bodyStyle);
52630         }
52631         //this.viewEl.setStyle('padding', '2px');
52632         
52633         this.setValue(this.value);
52634         
52635     },
52636 /*
52637     // private
52638     initValue : Roo.emptyFn,
52639
52640   */
52641
52642         // private
52643     onClick : function(){
52644         
52645     },
52646
52647     /**
52648      * Sets the checked state of the checkbox.
52649      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52650      */
52651     setValue : function(v){
52652         this.value = v;
52653         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52654         // this might be called before we have a dom element..
52655         if (!this.viewEl) {
52656             return;
52657         }
52658         this.viewEl.dom.innerHTML = html;
52659         Roo.form.DisplayField.superclass.setValue.call(this, v);
52660
52661     },
52662     
52663     onClose : function(e)
52664     {
52665         e.preventDefault();
52666         
52667         this.fireEvent('close', this);
52668     }
52669 });/*
52670  * 
52671  * Licence- LGPL
52672  * 
52673  */
52674
52675 /**
52676  * @class Roo.form.DayPicker
52677  * @extends Roo.form.Field
52678  * A Day picker show [M] [T] [W] ....
52679  * @constructor
52680  * Creates a new Day Picker
52681  * @param {Object} config Configuration options
52682  */
52683 Roo.form.DayPicker= function(config){
52684     Roo.form.DayPicker.superclass.constructor.call(this, config);
52685      
52686 };
52687
52688 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52689     /**
52690      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52691      */
52692     focusClass : undefined,
52693     /**
52694      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52695      */
52696     fieldClass: "x-form-field",
52697    
52698     /**
52699      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52700      * {tag: "input", type: "checkbox", autocomplete: "off"})
52701      */
52702     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52703     
52704    
52705     actionMode : 'viewEl', 
52706     //
52707     // private
52708  
52709     inputType : 'hidden',
52710     
52711      
52712     inputElement: false, // real input element?
52713     basedOn: false, // ????
52714     
52715     isFormField: true, // not sure where this is needed!!!!
52716
52717     onResize : function(){
52718         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52719         if(!this.boxLabel){
52720             this.el.alignTo(this.wrap, 'c-c');
52721         }
52722     },
52723
52724     initEvents : function(){
52725         Roo.form.Checkbox.superclass.initEvents.call(this);
52726         this.el.on("click", this.onClick,  this);
52727         this.el.on("change", this.onClick,  this);
52728     },
52729
52730
52731     getResizeEl : function(){
52732         return this.wrap;
52733     },
52734
52735     getPositionEl : function(){
52736         return this.wrap;
52737     },
52738
52739     
52740     // private
52741     onRender : function(ct, position){
52742         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
52743        
52744         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
52745         
52746         var r1 = '<table><tr>';
52747         var r2 = '<tr class="x-form-daypick-icons">';
52748         for (var i=0; i < 7; i++) {
52749             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
52750             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
52751         }
52752         
52753         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
52754         viewEl.select('img').on('click', this.onClick, this);
52755         this.viewEl = viewEl;   
52756         
52757         
52758         // this will not work on Chrome!!!
52759         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
52760         this.el.on('propertychange', this.setFromHidden,  this);  //ie
52761         
52762         
52763           
52764
52765     },
52766
52767     // private
52768     initValue : Roo.emptyFn,
52769
52770     /**
52771      * Returns the checked state of the checkbox.
52772      * @return {Boolean} True if checked, else false
52773      */
52774     getValue : function(){
52775         return this.el.dom.value;
52776         
52777     },
52778
52779         // private
52780     onClick : function(e){ 
52781         //this.setChecked(!this.checked);
52782         Roo.get(e.target).toggleClass('x-menu-item-checked');
52783         this.refreshValue();
52784         //if(this.el.dom.checked != this.checked){
52785         //    this.setValue(this.el.dom.checked);
52786        // }
52787     },
52788     
52789     // private
52790     refreshValue : function()
52791     {
52792         var val = '';
52793         this.viewEl.select('img',true).each(function(e,i,n)  {
52794             val += e.is(".x-menu-item-checked") ? String(n) : '';
52795         });
52796         this.setValue(val, true);
52797     },
52798
52799     /**
52800      * Sets the checked state of the checkbox.
52801      * On is always based on a string comparison between inputValue and the param.
52802      * @param {Boolean/String} value - the value to set 
52803      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
52804      */
52805     setValue : function(v,suppressEvent){
52806         if (!this.el.dom) {
52807             return;
52808         }
52809         var old = this.el.dom.value ;
52810         this.el.dom.value = v;
52811         if (suppressEvent) {
52812             return ;
52813         }
52814          
52815         // update display..
52816         this.viewEl.select('img',true).each(function(e,i,n)  {
52817             
52818             var on = e.is(".x-menu-item-checked");
52819             var newv = v.indexOf(String(n)) > -1;
52820             if (on != newv) {
52821                 e.toggleClass('x-menu-item-checked');
52822             }
52823             
52824         });
52825         
52826         
52827         this.fireEvent('change', this, v, old);
52828         
52829         
52830     },
52831    
52832     // handle setting of hidden value by some other method!!?!?
52833     setFromHidden: function()
52834     {
52835         if(!this.el){
52836             return;
52837         }
52838         //console.log("SET FROM HIDDEN");
52839         //alert('setFrom hidden');
52840         this.setValue(this.el.dom.value);
52841     },
52842     
52843     onDestroy : function()
52844     {
52845         if(this.viewEl){
52846             Roo.get(this.viewEl).remove();
52847         }
52848          
52849         Roo.form.DayPicker.superclass.onDestroy.call(this);
52850     }
52851
52852 });/*
52853  * RooJS Library 1.1.1
52854  * Copyright(c) 2008-2011  Alan Knowles
52855  *
52856  * License - LGPL
52857  */
52858  
52859
52860 /**
52861  * @class Roo.form.ComboCheck
52862  * @extends Roo.form.ComboBox
52863  * A combobox for multiple select items.
52864  *
52865  * FIXME - could do with a reset button..
52866  * 
52867  * @constructor
52868  * Create a new ComboCheck
52869  * @param {Object} config Configuration options
52870  */
52871 Roo.form.ComboCheck = function(config){
52872     Roo.form.ComboCheck.superclass.constructor.call(this, config);
52873     // should verify some data...
52874     // like
52875     // hiddenName = required..
52876     // displayField = required
52877     // valudField == required
52878     var req= [ 'hiddenName', 'displayField', 'valueField' ];
52879     var _t = this;
52880     Roo.each(req, function(e) {
52881         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
52882             throw "Roo.form.ComboCheck : missing value for: " + e;
52883         }
52884     });
52885     
52886     
52887 };
52888
52889 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
52890      
52891      
52892     editable : false,
52893      
52894     selectedClass: 'x-menu-item-checked', 
52895     
52896     // private
52897     onRender : function(ct, position){
52898         var _t = this;
52899         
52900         
52901         
52902         if(!this.tpl){
52903             var cls = 'x-combo-list';
52904
52905             
52906             this.tpl =  new Roo.Template({
52907                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
52908                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
52909                    '<span>{' + this.displayField + '}</span>' +
52910                     '</div>' 
52911                 
52912             });
52913         }
52914  
52915         
52916         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
52917         this.view.singleSelect = false;
52918         this.view.multiSelect = true;
52919         this.view.toggleSelect = true;
52920         this.pageTb.add(new Roo.Toolbar.Fill(), {
52921             
52922             text: 'Done',
52923             handler: function()
52924             {
52925                 _t.collapse();
52926             }
52927         });
52928     },
52929     
52930     onViewOver : function(e, t){
52931         // do nothing...
52932         return;
52933         
52934     },
52935     
52936     onViewClick : function(doFocus,index){
52937         return;
52938         
52939     },
52940     select: function () {
52941         //Roo.log("SELECT CALLED");
52942     },
52943      
52944     selectByValue : function(xv, scrollIntoView){
52945         var ar = this.getValueArray();
52946         var sels = [];
52947         
52948         Roo.each(ar, function(v) {
52949             if(v === undefined || v === null){
52950                 return;
52951             }
52952             var r = this.findRecord(this.valueField, v);
52953             if(r){
52954                 sels.push(this.store.indexOf(r))
52955                 
52956             }
52957         },this);
52958         this.view.select(sels);
52959         return false;
52960     },
52961     
52962     
52963     
52964     onSelect : function(record, index){
52965        // Roo.log("onselect Called");
52966        // this is only called by the clear button now..
52967         this.view.clearSelections();
52968         this.setValue('[]');
52969         if (this.value != this.valueBefore) {
52970             this.fireEvent('change', this, this.value, this.valueBefore);
52971             this.valueBefore = this.value;
52972         }
52973     },
52974     getValueArray : function()
52975     {
52976         var ar = [] ;
52977         
52978         try {
52979             //Roo.log(this.value);
52980             if (typeof(this.value) == 'undefined') {
52981                 return [];
52982             }
52983             var ar = Roo.decode(this.value);
52984             return  ar instanceof Array ? ar : []; //?? valid?
52985             
52986         } catch(e) {
52987             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
52988             return [];
52989         }
52990          
52991     },
52992     expand : function ()
52993     {
52994         
52995         Roo.form.ComboCheck.superclass.expand.call(this);
52996         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
52997         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
52998         
52999
53000     },
53001     
53002     collapse : function(){
53003         Roo.form.ComboCheck.superclass.collapse.call(this);
53004         var sl = this.view.getSelectedIndexes();
53005         var st = this.store;
53006         var nv = [];
53007         var tv = [];
53008         var r;
53009         Roo.each(sl, function(i) {
53010             r = st.getAt(i);
53011             nv.push(r.get(this.valueField));
53012         },this);
53013         this.setValue(Roo.encode(nv));
53014         if (this.value != this.valueBefore) {
53015
53016             this.fireEvent('change', this, this.value, this.valueBefore);
53017             this.valueBefore = this.value;
53018         }
53019         
53020     },
53021     
53022     setValue : function(v){
53023         // Roo.log(v);
53024         this.value = v;
53025         
53026         var vals = this.getValueArray();
53027         var tv = [];
53028         Roo.each(vals, function(k) {
53029             var r = this.findRecord(this.valueField, k);
53030             if(r){
53031                 tv.push(r.data[this.displayField]);
53032             }else if(this.valueNotFoundText !== undefined){
53033                 tv.push( this.valueNotFoundText );
53034             }
53035         },this);
53036        // Roo.log(tv);
53037         
53038         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53039         this.hiddenField.value = v;
53040         this.value = v;
53041     }
53042     
53043 });/*
53044  * Based on:
53045  * Ext JS Library 1.1.1
53046  * Copyright(c) 2006-2007, Ext JS, LLC.
53047  *
53048  * Originally Released Under LGPL - original licence link has changed is not relivant.
53049  *
53050  * Fork - LGPL
53051  * <script type="text/javascript">
53052  */
53053  
53054 /**
53055  * @class Roo.form.Signature
53056  * @extends Roo.form.Field
53057  * Signature field.  
53058  * @constructor
53059  * 
53060  * @param {Object} config Configuration options
53061  */
53062
53063 Roo.form.Signature = function(config){
53064     Roo.form.Signature.superclass.constructor.call(this, config);
53065     
53066     this.addEvents({// not in used??
53067          /**
53068          * @event confirm
53069          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53070              * @param {Roo.form.Signature} combo This combo box
53071              */
53072         'confirm' : true,
53073         /**
53074          * @event reset
53075          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53076              * @param {Roo.form.ComboBox} combo This combo box
53077              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53078              */
53079         'reset' : true
53080     });
53081 };
53082
53083 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53084     /**
53085      * @cfg {Object} labels Label to use when rendering a form.
53086      * defaults to 
53087      * labels : { 
53088      *      clear : "Clear",
53089      *      confirm : "Confirm"
53090      *  }
53091      */
53092     labels : { 
53093         clear : "Clear",
53094         confirm : "Confirm"
53095     },
53096     /**
53097      * @cfg {Number} width The signature panel width (defaults to 300)
53098      */
53099     width: 300,
53100     /**
53101      * @cfg {Number} height The signature panel height (defaults to 100)
53102      */
53103     height : 100,
53104     /**
53105      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53106      */
53107     allowBlank : false,
53108     
53109     //private
53110     // {Object} signPanel The signature SVG panel element (defaults to {})
53111     signPanel : {},
53112     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53113     isMouseDown : false,
53114     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53115     isConfirmed : false,
53116     // {String} signatureTmp SVG mapping string (defaults to empty string)
53117     signatureTmp : '',
53118     
53119     
53120     defaultAutoCreate : { // modified by initCompnoent..
53121         tag: "input",
53122         type:"hidden"
53123     },
53124
53125     // private
53126     onRender : function(ct, position){
53127         
53128         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53129         
53130         this.wrap = this.el.wrap({
53131             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53132         });
53133         
53134         this.createToolbar(this);
53135         this.signPanel = this.wrap.createChild({
53136                 tag: 'div',
53137                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53138             }, this.el
53139         );
53140             
53141         this.svgID = Roo.id();
53142         this.svgEl = this.signPanel.createChild({
53143               xmlns : 'http://www.w3.org/2000/svg',
53144               tag : 'svg',
53145               id : this.svgID + "-svg",
53146               width: this.width,
53147               height: this.height,
53148               viewBox: '0 0 '+this.width+' '+this.height,
53149               cn : [
53150                 {
53151                     tag: "rect",
53152                     id: this.svgID + "-svg-r",
53153                     width: this.width,
53154                     height: this.height,
53155                     fill: "#ffa"
53156                 },
53157                 {
53158                     tag: "line",
53159                     id: this.svgID + "-svg-l",
53160                     x1: "0", // start
53161                     y1: (this.height*0.8), // start set the line in 80% of height
53162                     x2: this.width, // end
53163                     y2: (this.height*0.8), // end set the line in 80% of height
53164                     'stroke': "#666",
53165                     'stroke-width': "1",
53166                     'stroke-dasharray': "3",
53167                     'shape-rendering': "crispEdges",
53168                     'pointer-events': "none"
53169                 },
53170                 {
53171                     tag: "path",
53172                     id: this.svgID + "-svg-p",
53173                     'stroke': "navy",
53174                     'stroke-width': "3",
53175                     'fill': "none",
53176                     'pointer-events': 'none'
53177                 }
53178               ]
53179         });
53180         this.createSVG();
53181         this.svgBox = this.svgEl.dom.getScreenCTM();
53182     },
53183     createSVG : function(){ 
53184         var svg = this.signPanel;
53185         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53186         var t = this;
53187
53188         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53189         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53190         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53191         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53192         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53193         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53194         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53195         
53196     },
53197     isTouchEvent : function(e){
53198         return e.type.match(/^touch/);
53199     },
53200     getCoords : function (e) {
53201         var pt    = this.svgEl.dom.createSVGPoint();
53202         pt.x = e.clientX; 
53203         pt.y = e.clientY;
53204         if (this.isTouchEvent(e)) {
53205             pt.x =  e.targetTouches[0].clientX;
53206             pt.y = e.targetTouches[0].clientY;
53207         }
53208         var a = this.svgEl.dom.getScreenCTM();
53209         var b = a.inverse();
53210         var mx = pt.matrixTransform(b);
53211         return mx.x + ',' + mx.y;
53212     },
53213     //mouse event headler 
53214     down : function (e) {
53215         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53216         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53217         
53218         this.isMouseDown = true;
53219         
53220         e.preventDefault();
53221     },
53222     move : function (e) {
53223         if (this.isMouseDown) {
53224             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53225             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53226         }
53227         
53228         e.preventDefault();
53229     },
53230     up : function (e) {
53231         this.isMouseDown = false;
53232         var sp = this.signatureTmp.split(' ');
53233         
53234         if(sp.length > 1){
53235             if(!sp[sp.length-2].match(/^L/)){
53236                 sp.pop();
53237                 sp.pop();
53238                 sp.push("");
53239                 this.signatureTmp = sp.join(" ");
53240             }
53241         }
53242         if(this.getValue() != this.signatureTmp){
53243             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53244             this.isConfirmed = false;
53245         }
53246         e.preventDefault();
53247     },
53248     
53249     /**
53250      * Protected method that will not generally be called directly. It
53251      * is called when the editor creates its toolbar. Override this method if you need to
53252      * add custom toolbar buttons.
53253      * @param {HtmlEditor} editor
53254      */
53255     createToolbar : function(editor){
53256          function btn(id, toggle, handler){
53257             var xid = fid + '-'+ id ;
53258             return {
53259                 id : xid,
53260                 cmd : id,
53261                 cls : 'x-btn-icon x-edit-'+id,
53262                 enableToggle:toggle !== false,
53263                 scope: editor, // was editor...
53264                 handler:handler||editor.relayBtnCmd,
53265                 clickEvent:'mousedown',
53266                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53267                 tabIndex:-1
53268             };
53269         }
53270         
53271         
53272         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53273         this.tb = tb;
53274         this.tb.add(
53275            {
53276                 cls : ' x-signature-btn x-signature-'+id,
53277                 scope: editor, // was editor...
53278                 handler: this.reset,
53279                 clickEvent:'mousedown',
53280                 text: this.labels.clear
53281             },
53282             {
53283                  xtype : 'Fill',
53284                  xns: Roo.Toolbar
53285             }, 
53286             {
53287                 cls : '  x-signature-btn x-signature-'+id,
53288                 scope: editor, // was editor...
53289                 handler: this.confirmHandler,
53290                 clickEvent:'mousedown',
53291                 text: this.labels.confirm
53292             }
53293         );
53294     
53295     },
53296     //public
53297     /**
53298      * when user is clicked confirm then show this image.....
53299      * 
53300      * @return {String} Image Data URI
53301      */
53302     getImageDataURI : function(){
53303         var svg = this.svgEl.dom.parentNode.innerHTML;
53304         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53305         return src; 
53306     },
53307     /**
53308      * 
53309      * @return {Boolean} this.isConfirmed
53310      */
53311     getConfirmed : function(){
53312         return this.isConfirmed;
53313     },
53314     /**
53315      * 
53316      * @return {Number} this.width
53317      */
53318     getWidth : function(){
53319         return this.width;
53320     },
53321     /**
53322      * 
53323      * @return {Number} this.height
53324      */
53325     getHeight : function(){
53326         return this.height;
53327     },
53328     // private
53329     getSignature : function(){
53330         return this.signatureTmp;
53331     },
53332     // private
53333     reset : function(){
53334         this.signatureTmp = '';
53335         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53336         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53337         this.isConfirmed = false;
53338         Roo.form.Signature.superclass.reset.call(this);
53339     },
53340     setSignature : function(s){
53341         this.signatureTmp = s;
53342         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53343         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53344         this.setValue(s);
53345         this.isConfirmed = false;
53346         Roo.form.Signature.superclass.reset.call(this);
53347     }, 
53348     test : function(){
53349 //        Roo.log(this.signPanel.dom.contentWindow.up())
53350     },
53351     //private
53352     setConfirmed : function(){
53353         
53354         
53355         
53356 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53357     },
53358     // private
53359     confirmHandler : function(){
53360         if(!this.getSignature()){
53361             return;
53362         }
53363         
53364         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53365         this.setValue(this.getSignature());
53366         this.isConfirmed = true;
53367         
53368         this.fireEvent('confirm', this);
53369     },
53370     // private
53371     // Subclasses should provide the validation implementation by overriding this
53372     validateValue : function(value){
53373         if(this.allowBlank){
53374             return true;
53375         }
53376         
53377         if(this.isConfirmed){
53378             return true;
53379         }
53380         return false;
53381     }
53382 });/*
53383  * Based on:
53384  * Ext JS Library 1.1.1
53385  * Copyright(c) 2006-2007, Ext JS, LLC.
53386  *
53387  * Originally Released Under LGPL - original licence link has changed is not relivant.
53388  *
53389  * Fork - LGPL
53390  * <script type="text/javascript">
53391  */
53392  
53393
53394 /**
53395  * @class Roo.form.ComboBox
53396  * @extends Roo.form.TriggerField
53397  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53398  * @constructor
53399  * Create a new ComboBox.
53400  * @param {Object} config Configuration options
53401  */
53402 Roo.form.Select = function(config){
53403     Roo.form.Select.superclass.constructor.call(this, config);
53404      
53405 };
53406
53407 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53408     /**
53409      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53410      */
53411     /**
53412      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53413      * rendering into an Roo.Editor, defaults to false)
53414      */
53415     /**
53416      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53417      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53418      */
53419     /**
53420      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53421      */
53422     /**
53423      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53424      * the dropdown list (defaults to undefined, with no header element)
53425      */
53426
53427      /**
53428      * @cfg {String/Roo.Template} tpl The template to use to render the output
53429      */
53430      
53431     // private
53432     defaultAutoCreate : {tag: "select"  },
53433     /**
53434      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53435      */
53436     listWidth: undefined,
53437     /**
53438      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53439      * mode = 'remote' or 'text' if mode = 'local')
53440      */
53441     displayField: undefined,
53442     /**
53443      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53444      * mode = 'remote' or 'value' if mode = 'local'). 
53445      * Note: use of a valueField requires the user make a selection
53446      * in order for a value to be mapped.
53447      */
53448     valueField: undefined,
53449     
53450     
53451     /**
53452      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53453      * field's data value (defaults to the underlying DOM element's name)
53454      */
53455     hiddenName: undefined,
53456     /**
53457      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53458      */
53459     listClass: '',
53460     /**
53461      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53462      */
53463     selectedClass: 'x-combo-selected',
53464     /**
53465      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53466      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53467      * which displays a downward arrow icon).
53468      */
53469     triggerClass : 'x-form-arrow-trigger',
53470     /**
53471      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53472      */
53473     shadow:'sides',
53474     /**
53475      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53476      * anchor positions (defaults to 'tl-bl')
53477      */
53478     listAlign: 'tl-bl?',
53479     /**
53480      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53481      */
53482     maxHeight: 300,
53483     /**
53484      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53485      * query specified by the allQuery config option (defaults to 'query')
53486      */
53487     triggerAction: 'query',
53488     /**
53489      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53490      * (defaults to 4, does not apply if editable = false)
53491      */
53492     minChars : 4,
53493     /**
53494      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53495      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53496      */
53497     typeAhead: false,
53498     /**
53499      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53500      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53501      */
53502     queryDelay: 500,
53503     /**
53504      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53505      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53506      */
53507     pageSize: 0,
53508     /**
53509      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53510      * when editable = true (defaults to false)
53511      */
53512     selectOnFocus:false,
53513     /**
53514      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53515      */
53516     queryParam: 'query',
53517     /**
53518      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53519      * when mode = 'remote' (defaults to 'Loading...')
53520      */
53521     loadingText: 'Loading...',
53522     /**
53523      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53524      */
53525     resizable: false,
53526     /**
53527      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53528      */
53529     handleHeight : 8,
53530     /**
53531      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53532      * traditional select (defaults to true)
53533      */
53534     editable: true,
53535     /**
53536      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53537      */
53538     allQuery: '',
53539     /**
53540      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53541      */
53542     mode: 'remote',
53543     /**
53544      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53545      * listWidth has a higher value)
53546      */
53547     minListWidth : 70,
53548     /**
53549      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53550      * allow the user to set arbitrary text into the field (defaults to false)
53551      */
53552     forceSelection:false,
53553     /**
53554      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53555      * if typeAhead = true (defaults to 250)
53556      */
53557     typeAheadDelay : 250,
53558     /**
53559      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53560      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53561      */
53562     valueNotFoundText : undefined,
53563     
53564     /**
53565      * @cfg {String} defaultValue The value displayed after loading the store.
53566      */
53567     defaultValue: '',
53568     
53569     /**
53570      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53571      */
53572     blockFocus : false,
53573     
53574     /**
53575      * @cfg {Boolean} disableClear Disable showing of clear button.
53576      */
53577     disableClear : false,
53578     /**
53579      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53580      */
53581     alwaysQuery : false,
53582     
53583     //private
53584     addicon : false,
53585     editicon: false,
53586     
53587     // element that contains real text value.. (when hidden is used..)
53588      
53589     // private
53590     onRender : function(ct, position){
53591         Roo.form.Field.prototype.onRender.call(this, ct, position);
53592         
53593         if(this.store){
53594             this.store.on('beforeload', this.onBeforeLoad, this);
53595             this.store.on('load', this.onLoad, this);
53596             this.store.on('loadexception', this.onLoadException, this);
53597             this.store.load({});
53598         }
53599         
53600         
53601         
53602     },
53603
53604     // private
53605     initEvents : function(){
53606         //Roo.form.ComboBox.superclass.initEvents.call(this);
53607  
53608     },
53609
53610     onDestroy : function(){
53611        
53612         if(this.store){
53613             this.store.un('beforeload', this.onBeforeLoad, this);
53614             this.store.un('load', this.onLoad, this);
53615             this.store.un('loadexception', this.onLoadException, this);
53616         }
53617         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53618     },
53619
53620     // private
53621     fireKey : function(e){
53622         if(e.isNavKeyPress() && !this.list.isVisible()){
53623             this.fireEvent("specialkey", this, e);
53624         }
53625     },
53626
53627     // private
53628     onResize: function(w, h){
53629         
53630         return; 
53631     
53632         
53633     },
53634
53635     /**
53636      * Allow or prevent the user from directly editing the field text.  If false is passed,
53637      * the user will only be able to select from the items defined in the dropdown list.  This method
53638      * is the runtime equivalent of setting the 'editable' config option at config time.
53639      * @param {Boolean} value True to allow the user to directly edit the field text
53640      */
53641     setEditable : function(value){
53642          
53643     },
53644
53645     // private
53646     onBeforeLoad : function(){
53647         
53648         Roo.log("Select before load");
53649         return;
53650     
53651         this.innerList.update(this.loadingText ?
53652                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53653         //this.restrictHeight();
53654         this.selectedIndex = -1;
53655     },
53656
53657     // private
53658     onLoad : function(){
53659
53660     
53661         var dom = this.el.dom;
53662         dom.innerHTML = '';
53663          var od = dom.ownerDocument;
53664          
53665         if (this.emptyText) {
53666             var op = od.createElement('option');
53667             op.setAttribute('value', '');
53668             op.innerHTML = String.format('{0}', this.emptyText);
53669             dom.appendChild(op);
53670         }
53671         if(this.store.getCount() > 0){
53672            
53673             var vf = this.valueField;
53674             var df = this.displayField;
53675             this.store.data.each(function(r) {
53676                 // which colmsn to use... testing - cdoe / title..
53677                 var op = od.createElement('option');
53678                 op.setAttribute('value', r.data[vf]);
53679                 op.innerHTML = String.format('{0}', r.data[df]);
53680                 dom.appendChild(op);
53681             });
53682             if (typeof(this.defaultValue != 'undefined')) {
53683                 this.setValue(this.defaultValue);
53684             }
53685             
53686              
53687         }else{
53688             //this.onEmptyResults();
53689         }
53690         //this.el.focus();
53691     },
53692     // private
53693     onLoadException : function()
53694     {
53695         dom.innerHTML = '';
53696             
53697         Roo.log("Select on load exception");
53698         return;
53699     
53700         this.collapse();
53701         Roo.log(this.store.reader.jsonData);
53702         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53703             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53704         }
53705         
53706         
53707     },
53708     // private
53709     onTypeAhead : function(){
53710          
53711     },
53712
53713     // private
53714     onSelect : function(record, index){
53715         Roo.log('on select?');
53716         return;
53717         if(this.fireEvent('beforeselect', this, record, index) !== false){
53718             this.setFromData(index > -1 ? record.data : false);
53719             this.collapse();
53720             this.fireEvent('select', this, record, index);
53721         }
53722     },
53723
53724     /**
53725      * Returns the currently selected field value or empty string if no value is set.
53726      * @return {String} value The selected value
53727      */
53728     getValue : function(){
53729         var dom = this.el.dom;
53730         this.value = dom.options[dom.selectedIndex].value;
53731         return this.value;
53732         
53733     },
53734
53735     /**
53736      * Clears any text/value currently set in the field
53737      */
53738     clearValue : function(){
53739         this.value = '';
53740         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
53741         
53742     },
53743
53744     /**
53745      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
53746      * will be displayed in the field.  If the value does not match the data value of an existing item,
53747      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
53748      * Otherwise the field will be blank (although the value will still be set).
53749      * @param {String} value The value to match
53750      */
53751     setValue : function(v){
53752         var d = this.el.dom;
53753         for (var i =0; i < d.options.length;i++) {
53754             if (v == d.options[i].value) {
53755                 d.selectedIndex = i;
53756                 this.value = v;
53757                 return;
53758             }
53759         }
53760         this.clearValue();
53761     },
53762     /**
53763      * @property {Object} the last set data for the element
53764      */
53765     
53766     lastData : false,
53767     /**
53768      * Sets the value of the field based on a object which is related to the record format for the store.
53769      * @param {Object} value the value to set as. or false on reset?
53770      */
53771     setFromData : function(o){
53772         Roo.log('setfrom data?');
53773          
53774         
53775         
53776     },
53777     // private
53778     reset : function(){
53779         this.clearValue();
53780     },
53781     // private
53782     findRecord : function(prop, value){
53783         
53784         return false;
53785     
53786         var record;
53787         if(this.store.getCount() > 0){
53788             this.store.each(function(r){
53789                 if(r.data[prop] == value){
53790                     record = r;
53791                     return false;
53792                 }
53793                 return true;
53794             });
53795         }
53796         return record;
53797     },
53798     
53799     getName: function()
53800     {
53801         // returns hidden if it's set..
53802         if (!this.rendered) {return ''};
53803         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
53804         
53805     },
53806      
53807
53808     
53809
53810     // private
53811     onEmptyResults : function(){
53812         Roo.log('empty results');
53813         //this.collapse();
53814     },
53815
53816     /**
53817      * Returns true if the dropdown list is expanded, else false.
53818      */
53819     isExpanded : function(){
53820         return false;
53821     },
53822
53823     /**
53824      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
53825      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53826      * @param {String} value The data value of the item to select
53827      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53828      * selected item if it is not currently in view (defaults to true)
53829      * @return {Boolean} True if the value matched an item in the list, else false
53830      */
53831     selectByValue : function(v, scrollIntoView){
53832         Roo.log('select By Value');
53833         return false;
53834     
53835         if(v !== undefined && v !== null){
53836             var r = this.findRecord(this.valueField || this.displayField, v);
53837             if(r){
53838                 this.select(this.store.indexOf(r), scrollIntoView);
53839                 return true;
53840             }
53841         }
53842         return false;
53843     },
53844
53845     /**
53846      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
53847      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53848      * @param {Number} index The zero-based index of the list item to select
53849      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53850      * selected item if it is not currently in view (defaults to true)
53851      */
53852     select : function(index, scrollIntoView){
53853         Roo.log('select ');
53854         return  ;
53855         
53856         this.selectedIndex = index;
53857         this.view.select(index);
53858         if(scrollIntoView !== false){
53859             var el = this.view.getNode(index);
53860             if(el){
53861                 this.innerList.scrollChildIntoView(el, false);
53862             }
53863         }
53864     },
53865
53866       
53867
53868     // private
53869     validateBlur : function(){
53870         
53871         return;
53872         
53873     },
53874
53875     // private
53876     initQuery : function(){
53877         this.doQuery(this.getRawValue());
53878     },
53879
53880     // private
53881     doForce : function(){
53882         if(this.el.dom.value.length > 0){
53883             this.el.dom.value =
53884                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
53885              
53886         }
53887     },
53888
53889     /**
53890      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
53891      * query allowing the query action to be canceled if needed.
53892      * @param {String} query The SQL query to execute
53893      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
53894      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
53895      * saved in the current store (defaults to false)
53896      */
53897     doQuery : function(q, forceAll){
53898         
53899         Roo.log('doQuery?');
53900         if(q === undefined || q === null){
53901             q = '';
53902         }
53903         var qe = {
53904             query: q,
53905             forceAll: forceAll,
53906             combo: this,
53907             cancel:false
53908         };
53909         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
53910             return false;
53911         }
53912         q = qe.query;
53913         forceAll = qe.forceAll;
53914         if(forceAll === true || (q.length >= this.minChars)){
53915             if(this.lastQuery != q || this.alwaysQuery){
53916                 this.lastQuery = q;
53917                 if(this.mode == 'local'){
53918                     this.selectedIndex = -1;
53919                     if(forceAll){
53920                         this.store.clearFilter();
53921                     }else{
53922                         this.store.filter(this.displayField, q);
53923                     }
53924                     this.onLoad();
53925                 }else{
53926                     this.store.baseParams[this.queryParam] = q;
53927                     this.store.load({
53928                         params: this.getParams(q)
53929                     });
53930                     this.expand();
53931                 }
53932             }else{
53933                 this.selectedIndex = -1;
53934                 this.onLoad();   
53935             }
53936         }
53937     },
53938
53939     // private
53940     getParams : function(q){
53941         var p = {};
53942         //p[this.queryParam] = q;
53943         if(this.pageSize){
53944             p.start = 0;
53945             p.limit = this.pageSize;
53946         }
53947         return p;
53948     },
53949
53950     /**
53951      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
53952      */
53953     collapse : function(){
53954         
53955     },
53956
53957     // private
53958     collapseIf : function(e){
53959         
53960     },
53961
53962     /**
53963      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
53964      */
53965     expand : function(){
53966         
53967     } ,
53968
53969     // private
53970      
53971
53972     /** 
53973     * @cfg {Boolean} grow 
53974     * @hide 
53975     */
53976     /** 
53977     * @cfg {Number} growMin 
53978     * @hide 
53979     */
53980     /** 
53981     * @cfg {Number} growMax 
53982     * @hide 
53983     */
53984     /**
53985      * @hide
53986      * @method autoSize
53987      */
53988     
53989     setWidth : function()
53990     {
53991         
53992     },
53993     getResizeEl : function(){
53994         return this.el;
53995     }
53996 });//<script type="text/javasscript">
53997  
53998
53999 /**
54000  * @class Roo.DDView
54001  * A DnD enabled version of Roo.View.
54002  * @param {Element/String} container The Element in which to create the View.
54003  * @param {String} tpl The template string used to create the markup for each element of the View
54004  * @param {Object} config The configuration properties. These include all the config options of
54005  * {@link Roo.View} plus some specific to this class.<br>
54006  * <p>
54007  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54008  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54009  * <p>
54010  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54011 .x-view-drag-insert-above {
54012         border-top:1px dotted #3366cc;
54013 }
54014 .x-view-drag-insert-below {
54015         border-bottom:1px dotted #3366cc;
54016 }
54017 </code></pre>
54018  * 
54019  */
54020  
54021 Roo.DDView = function(container, tpl, config) {
54022     Roo.DDView.superclass.constructor.apply(this, arguments);
54023     this.getEl().setStyle("outline", "0px none");
54024     this.getEl().unselectable();
54025     if (this.dragGroup) {
54026         this.setDraggable(this.dragGroup.split(","));
54027     }
54028     if (this.dropGroup) {
54029         this.setDroppable(this.dropGroup.split(","));
54030     }
54031     if (this.deletable) {
54032         this.setDeletable();
54033     }
54034     this.isDirtyFlag = false;
54035         this.addEvents({
54036                 "drop" : true
54037         });
54038 };
54039
54040 Roo.extend(Roo.DDView, Roo.View, {
54041 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54042 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54043 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54044 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54045
54046         isFormField: true,
54047
54048         reset: Roo.emptyFn,
54049         
54050         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54051
54052         validate: function() {
54053                 return true;
54054         },
54055         
54056         destroy: function() {
54057                 this.purgeListeners();
54058                 this.getEl.removeAllListeners();
54059                 this.getEl().remove();
54060                 if (this.dragZone) {
54061                         if (this.dragZone.destroy) {
54062                                 this.dragZone.destroy();
54063                         }
54064                 }
54065                 if (this.dropZone) {
54066                         if (this.dropZone.destroy) {
54067                                 this.dropZone.destroy();
54068                         }
54069                 }
54070         },
54071
54072 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54073         getName: function() {
54074                 return this.name;
54075         },
54076
54077 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54078         setValue: function(v) {
54079                 if (!this.store) {
54080                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54081                 }
54082                 var data = {};
54083                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54084                 this.store.proxy = new Roo.data.MemoryProxy(data);
54085                 this.store.load();
54086         },
54087
54088 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54089         getValue: function() {
54090                 var result = '(';
54091                 this.store.each(function(rec) {
54092                         result += rec.id + ',';
54093                 });
54094                 return result.substr(0, result.length - 1) + ')';
54095         },
54096         
54097         getIds: function() {
54098                 var i = 0, result = new Array(this.store.getCount());
54099                 this.store.each(function(rec) {
54100                         result[i++] = rec.id;
54101                 });
54102                 return result;
54103         },
54104         
54105         isDirty: function() {
54106                 return this.isDirtyFlag;
54107         },
54108
54109 /**
54110  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54111  *      whole Element becomes the target, and this causes the drop gesture to append.
54112  */
54113     getTargetFromEvent : function(e) {
54114                 var target = e.getTarget();
54115                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54116                 target = target.parentNode;
54117                 }
54118                 if (!target) {
54119                         target = this.el.dom.lastChild || this.el.dom;
54120                 }
54121                 return target;
54122     },
54123
54124 /**
54125  *      Create the drag data which consists of an object which has the property "ddel" as
54126  *      the drag proxy element. 
54127  */
54128     getDragData : function(e) {
54129         var target = this.findItemFromChild(e.getTarget());
54130                 if(target) {
54131                         this.handleSelection(e);
54132                         var selNodes = this.getSelectedNodes();
54133             var dragData = {
54134                 source: this,
54135                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54136                 nodes: selNodes,
54137                 records: []
54138                         };
54139                         var selectedIndices = this.getSelectedIndexes();
54140                         for (var i = 0; i < selectedIndices.length; i++) {
54141                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54142                         }
54143                         if (selNodes.length == 1) {
54144                                 dragData.ddel = target.cloneNode(true); // the div element
54145                         } else {
54146                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54147                                 div.className = 'multi-proxy';
54148                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54149                                         div.appendChild(selNodes[i].cloneNode(true));
54150                                 }
54151                                 dragData.ddel = div;
54152                         }
54153             //console.log(dragData)
54154             //console.log(dragData.ddel.innerHTML)
54155                         return dragData;
54156                 }
54157         //console.log('nodragData')
54158                 return false;
54159     },
54160     
54161 /**     Specify to which ddGroup items in this DDView may be dragged. */
54162     setDraggable: function(ddGroup) {
54163         if (ddGroup instanceof Array) {
54164                 Roo.each(ddGroup, this.setDraggable, this);
54165                 return;
54166         }
54167         if (this.dragZone) {
54168                 this.dragZone.addToGroup(ddGroup);
54169         } else {
54170                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54171                                 containerScroll: true,
54172                                 ddGroup: ddGroup 
54173
54174                         });
54175 //                      Draggability implies selection. DragZone's mousedown selects the element.
54176                         if (!this.multiSelect) { this.singleSelect = true; }
54177
54178 //                      Wire the DragZone's handlers up to methods in *this*
54179                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54180                 }
54181     },
54182
54183 /**     Specify from which ddGroup this DDView accepts drops. */
54184     setDroppable: function(ddGroup) {
54185         if (ddGroup instanceof Array) {
54186                 Roo.each(ddGroup, this.setDroppable, this);
54187                 return;
54188         }
54189         if (this.dropZone) {
54190                 this.dropZone.addToGroup(ddGroup);
54191         } else {
54192                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54193                                 containerScroll: true,
54194                                 ddGroup: ddGroup
54195                         });
54196
54197 //                      Wire the DropZone's handlers up to methods in *this*
54198                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54199                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54200                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54201                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54202                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54203                 }
54204     },
54205
54206 /**     Decide whether to drop above or below a View node. */
54207     getDropPoint : function(e, n, dd){
54208         if (n == this.el.dom) { return "above"; }
54209                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54210                 var c = t + (b - t) / 2;
54211                 var y = Roo.lib.Event.getPageY(e);
54212                 if(y <= c) {
54213                         return "above";
54214                 }else{
54215                         return "below";
54216                 }
54217     },
54218
54219     onNodeEnter : function(n, dd, e, data){
54220                 return false;
54221     },
54222     
54223     onNodeOver : function(n, dd, e, data){
54224                 var pt = this.getDropPoint(e, n, dd);
54225                 // set the insert point style on the target node
54226                 var dragElClass = this.dropNotAllowed;
54227                 if (pt) {
54228                         var targetElClass;
54229                         if (pt == "above"){
54230                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54231                                 targetElClass = "x-view-drag-insert-above";
54232                         } else {
54233                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54234                                 targetElClass = "x-view-drag-insert-below";
54235                         }
54236                         if (this.lastInsertClass != targetElClass){
54237                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54238                                 this.lastInsertClass = targetElClass;
54239                         }
54240                 }
54241                 return dragElClass;
54242         },
54243
54244     onNodeOut : function(n, dd, e, data){
54245                 this.removeDropIndicators(n);
54246     },
54247
54248     onNodeDrop : function(n, dd, e, data){
54249         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54250                 return false;
54251         }
54252         var pt = this.getDropPoint(e, n, dd);
54253                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54254                 if (pt == "below") { insertAt++; }
54255                 for (var i = 0; i < data.records.length; i++) {
54256                         var r = data.records[i];
54257                         var dup = this.store.getById(r.id);
54258                         if (dup && (dd != this.dragZone)) {
54259                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54260                         } else {
54261                                 if (data.copy) {
54262                                         this.store.insert(insertAt++, r.copy());
54263                                 } else {
54264                                         data.source.isDirtyFlag = true;
54265                                         r.store.remove(r);
54266                                         this.store.insert(insertAt++, r);
54267                                 }
54268                                 this.isDirtyFlag = true;
54269                         }
54270                 }
54271                 this.dragZone.cachedTarget = null;
54272                 return true;
54273     },
54274
54275     removeDropIndicators : function(n){
54276                 if(n){
54277                         Roo.fly(n).removeClass([
54278                                 "x-view-drag-insert-above",
54279                                 "x-view-drag-insert-below"]);
54280                         this.lastInsertClass = "_noclass";
54281                 }
54282     },
54283
54284 /**
54285  *      Utility method. Add a delete option to the DDView's context menu.
54286  *      @param {String} imageUrl The URL of the "delete" icon image.
54287  */
54288         setDeletable: function(imageUrl) {
54289                 if (!this.singleSelect && !this.multiSelect) {
54290                         this.singleSelect = true;
54291                 }
54292                 var c = this.getContextMenu();
54293                 this.contextMenu.on("itemclick", function(item) {
54294                         switch (item.id) {
54295                                 case "delete":
54296                                         this.remove(this.getSelectedIndexes());
54297                                         break;
54298                         }
54299                 }, this);
54300                 this.contextMenu.add({
54301                         icon: imageUrl,
54302                         id: "delete",
54303                         text: 'Delete'
54304                 });
54305         },
54306         
54307 /**     Return the context menu for this DDView. */
54308         getContextMenu: function() {
54309                 if (!this.contextMenu) {
54310 //                      Create the View's context menu
54311                         this.contextMenu = new Roo.menu.Menu({
54312                                 id: this.id + "-contextmenu"
54313                         });
54314                         this.el.on("contextmenu", this.showContextMenu, this);
54315                 }
54316                 return this.contextMenu;
54317         },
54318         
54319         disableContextMenu: function() {
54320                 if (this.contextMenu) {
54321                         this.el.un("contextmenu", this.showContextMenu, this);
54322                 }
54323         },
54324
54325         showContextMenu: function(e, item) {
54326         item = this.findItemFromChild(e.getTarget());
54327                 if (item) {
54328                         e.stopEvent();
54329                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54330                         this.contextMenu.showAt(e.getXY());
54331             }
54332     },
54333
54334 /**
54335  *      Remove {@link Roo.data.Record}s at the specified indices.
54336  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54337  */
54338     remove: function(selectedIndices) {
54339                 selectedIndices = [].concat(selectedIndices);
54340                 for (var i = 0; i < selectedIndices.length; i++) {
54341                         var rec = this.store.getAt(selectedIndices[i]);
54342                         this.store.remove(rec);
54343                 }
54344     },
54345
54346 /**
54347  *      Double click fires the event, but also, if this is draggable, and there is only one other
54348  *      related DropZone, it transfers the selected node.
54349  */
54350     onDblClick : function(e){
54351         var item = this.findItemFromChild(e.getTarget());
54352         if(item){
54353             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54354                 return false;
54355             }
54356             if (this.dragGroup) {
54357                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54358                     while (targets.indexOf(this.dropZone) > -1) {
54359                             targets.remove(this.dropZone);
54360                                 }
54361                     if (targets.length == 1) {
54362                                         this.dragZone.cachedTarget = null;
54363                         var el = Roo.get(targets[0].getEl());
54364                         var box = el.getBox(true);
54365                         targets[0].onNodeDrop(el.dom, {
54366                                 target: el.dom,
54367                                 xy: [box.x, box.y + box.height - 1]
54368                         }, null, this.getDragData(e));
54369                     }
54370                 }
54371         }
54372     },
54373     
54374     handleSelection: function(e) {
54375                 this.dragZone.cachedTarget = null;
54376         var item = this.findItemFromChild(e.getTarget());
54377         if (!item) {
54378                 this.clearSelections(true);
54379                 return;
54380         }
54381                 if (item && (this.multiSelect || this.singleSelect)){
54382                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54383                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54384                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54385                                 this.unselect(item);
54386                         } else {
54387                                 this.select(item, this.multiSelect && e.ctrlKey);
54388                                 this.lastSelection = item;
54389                         }
54390                 }
54391     },
54392
54393     onItemClick : function(item, index, e){
54394                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54395                         return false;
54396                 }
54397                 return true;
54398     },
54399
54400     unselect : function(nodeInfo, suppressEvent){
54401                 var node = this.getNode(nodeInfo);
54402                 if(node && this.isSelected(node)){
54403                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54404                                 Roo.fly(node).removeClass(this.selectedClass);
54405                                 this.selections.remove(node);
54406                                 if(!suppressEvent){
54407                                         this.fireEvent("selectionchange", this, this.selections);
54408                                 }
54409                         }
54410                 }
54411     }
54412 });
54413 /*
54414  * Based on:
54415  * Ext JS Library 1.1.1
54416  * Copyright(c) 2006-2007, Ext JS, LLC.
54417  *
54418  * Originally Released Under LGPL - original licence link has changed is not relivant.
54419  *
54420  * Fork - LGPL
54421  * <script type="text/javascript">
54422  */
54423  
54424 /**
54425  * @class Roo.LayoutManager
54426  * @extends Roo.util.Observable
54427  * Base class for layout managers.
54428  */
54429 Roo.LayoutManager = function(container, config){
54430     Roo.LayoutManager.superclass.constructor.call(this);
54431     this.el = Roo.get(container);
54432     // ie scrollbar fix
54433     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54434         document.body.scroll = "no";
54435     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54436         this.el.position('relative');
54437     }
54438     this.id = this.el.id;
54439     this.el.addClass("x-layout-container");
54440     /** false to disable window resize monitoring @type Boolean */
54441     this.monitorWindowResize = true;
54442     this.regions = {};
54443     this.addEvents({
54444         /**
54445          * @event layout
54446          * Fires when a layout is performed. 
54447          * @param {Roo.LayoutManager} this
54448          */
54449         "layout" : true,
54450         /**
54451          * @event regionresized
54452          * Fires when the user resizes a region. 
54453          * @param {Roo.LayoutRegion} region The resized region
54454          * @param {Number} newSize The new size (width for east/west, height for north/south)
54455          */
54456         "regionresized" : true,
54457         /**
54458          * @event regioncollapsed
54459          * Fires when a region is collapsed. 
54460          * @param {Roo.LayoutRegion} region The collapsed region
54461          */
54462         "regioncollapsed" : true,
54463         /**
54464          * @event regionexpanded
54465          * Fires when a region is expanded.  
54466          * @param {Roo.LayoutRegion} region The expanded region
54467          */
54468         "regionexpanded" : true
54469     });
54470     this.updating = false;
54471     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54472 };
54473
54474 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54475     /**
54476      * Returns true if this layout is currently being updated
54477      * @return {Boolean}
54478      */
54479     isUpdating : function(){
54480         return this.updating; 
54481     },
54482     
54483     /**
54484      * Suspend the LayoutManager from doing auto-layouts while
54485      * making multiple add or remove calls
54486      */
54487     beginUpdate : function(){
54488         this.updating = true;    
54489     },
54490     
54491     /**
54492      * Restore auto-layouts and optionally disable the manager from performing a layout
54493      * @param {Boolean} noLayout true to disable a layout update 
54494      */
54495     endUpdate : function(noLayout){
54496         this.updating = false;
54497         if(!noLayout){
54498             this.layout();
54499         }    
54500     },
54501     
54502     layout: function(){
54503         
54504     },
54505     
54506     onRegionResized : function(region, newSize){
54507         this.fireEvent("regionresized", region, newSize);
54508         this.layout();
54509     },
54510     
54511     onRegionCollapsed : function(region){
54512         this.fireEvent("regioncollapsed", region);
54513     },
54514     
54515     onRegionExpanded : function(region){
54516         this.fireEvent("regionexpanded", region);
54517     },
54518         
54519     /**
54520      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54521      * performs box-model adjustments.
54522      * @return {Object} The size as an object {width: (the width), height: (the height)}
54523      */
54524     getViewSize : function(){
54525         var size;
54526         if(this.el.dom != document.body){
54527             size = this.el.getSize();
54528         }else{
54529             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54530         }
54531         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54532         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54533         return size;
54534     },
54535     
54536     /**
54537      * Returns the Element this layout is bound to.
54538      * @return {Roo.Element}
54539      */
54540     getEl : function(){
54541         return this.el;
54542     },
54543     
54544     /**
54545      * Returns the specified region.
54546      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54547      * @return {Roo.LayoutRegion}
54548      */
54549     getRegion : function(target){
54550         return this.regions[target.toLowerCase()];
54551     },
54552     
54553     onWindowResize : function(){
54554         if(this.monitorWindowResize){
54555             this.layout();
54556         }
54557     }
54558 });/*
54559  * Based on:
54560  * Ext JS Library 1.1.1
54561  * Copyright(c) 2006-2007, Ext JS, LLC.
54562  *
54563  * Originally Released Under LGPL - original licence link has changed is not relivant.
54564  *
54565  * Fork - LGPL
54566  * <script type="text/javascript">
54567  */
54568 /**
54569  * @class Roo.BorderLayout
54570  * @extends Roo.LayoutManager
54571  * @children Roo.ContentPanel
54572  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54573  * please see: <br><br>
54574  * <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>
54575  * <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>
54576  * Example:
54577  <pre><code>
54578  var layout = new Roo.BorderLayout(document.body, {
54579     north: {
54580         initialSize: 25,
54581         titlebar: false
54582     },
54583     west: {
54584         split:true,
54585         initialSize: 200,
54586         minSize: 175,
54587         maxSize: 400,
54588         titlebar: true,
54589         collapsible: true
54590     },
54591     east: {
54592         split:true,
54593         initialSize: 202,
54594         minSize: 175,
54595         maxSize: 400,
54596         titlebar: true,
54597         collapsible: true
54598     },
54599     south: {
54600         split:true,
54601         initialSize: 100,
54602         minSize: 100,
54603         maxSize: 200,
54604         titlebar: true,
54605         collapsible: true
54606     },
54607     center: {
54608         titlebar: true,
54609         autoScroll:true,
54610         resizeTabs: true,
54611         minTabWidth: 50,
54612         preferredTabWidth: 150
54613     }
54614 });
54615
54616 // shorthand
54617 var CP = Roo.ContentPanel;
54618
54619 layout.beginUpdate();
54620 layout.add("north", new CP("north", "North"));
54621 layout.add("south", new CP("south", {title: "South", closable: true}));
54622 layout.add("west", new CP("west", {title: "West"}));
54623 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54624 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54625 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54626 layout.getRegion("center").showPanel("center1");
54627 layout.endUpdate();
54628 </code></pre>
54629
54630 <b>The container the layout is rendered into can be either the body element or any other element.
54631 If it is not the body element, the container needs to either be an absolute positioned element,
54632 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54633 the container size if it is not the body element.</b>
54634
54635 * @constructor
54636 * Create a new BorderLayout
54637 * @param {String/HTMLElement/Element} container The container this layout is bound to
54638 * @param {Object} config Configuration options
54639  */
54640 Roo.BorderLayout = function(container, config){
54641     config = config || {};
54642     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54643     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54644     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54645         var target = this.factory.validRegions[i];
54646         if(config[target]){
54647             this.addRegion(target, config[target]);
54648         }
54649     }
54650 };
54651
54652 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54653         
54654         /**
54655          * @cfg {Roo.LayoutRegion} east
54656          */
54657         /**
54658          * @cfg {Roo.LayoutRegion} west
54659          */
54660         /**
54661          * @cfg {Roo.LayoutRegion} north
54662          */
54663         /**
54664          * @cfg {Roo.LayoutRegion} south
54665          */
54666         /**
54667          * @cfg {Roo.LayoutRegion} center
54668          */
54669     /**
54670      * Creates and adds a new region if it doesn't already exist.
54671      * @param {String} target The target region key (north, south, east, west or center).
54672      * @param {Object} config The regions config object
54673      * @return {BorderLayoutRegion} The new region
54674      */
54675     addRegion : function(target, config){
54676         if(!this.regions[target]){
54677             var r = this.factory.create(target, this, config);
54678             this.bindRegion(target, r);
54679         }
54680         return this.regions[target];
54681     },
54682
54683     // private (kinda)
54684     bindRegion : function(name, r){
54685         this.regions[name] = r;
54686         r.on("visibilitychange", this.layout, this);
54687         r.on("paneladded", this.layout, this);
54688         r.on("panelremoved", this.layout, this);
54689         r.on("invalidated", this.layout, this);
54690         r.on("resized", this.onRegionResized, this);
54691         r.on("collapsed", this.onRegionCollapsed, this);
54692         r.on("expanded", this.onRegionExpanded, this);
54693     },
54694
54695     /**
54696      * Performs a layout update.
54697      */
54698     layout : function(){
54699         if(this.updating) {
54700             return;
54701         }
54702         var size = this.getViewSize();
54703         var w = size.width;
54704         var h = size.height;
54705         var centerW = w;
54706         var centerH = h;
54707         var centerY = 0;
54708         var centerX = 0;
54709         //var x = 0, y = 0;
54710
54711         var rs = this.regions;
54712         var north = rs["north"];
54713         var south = rs["south"]; 
54714         var west = rs["west"];
54715         var east = rs["east"];
54716         var center = rs["center"];
54717         //if(this.hideOnLayout){ // not supported anymore
54718             //c.el.setStyle("display", "none");
54719         //}
54720         if(north && north.isVisible()){
54721             var b = north.getBox();
54722             var m = north.getMargins();
54723             b.width = w - (m.left+m.right);
54724             b.x = m.left;
54725             b.y = m.top;
54726             centerY = b.height + b.y + m.bottom;
54727             centerH -= centerY;
54728             north.updateBox(this.safeBox(b));
54729         }
54730         if(south && south.isVisible()){
54731             var b = south.getBox();
54732             var m = south.getMargins();
54733             b.width = w - (m.left+m.right);
54734             b.x = m.left;
54735             var totalHeight = (b.height + m.top + m.bottom);
54736             b.y = h - totalHeight + m.top;
54737             centerH -= totalHeight;
54738             south.updateBox(this.safeBox(b));
54739         }
54740         if(west && west.isVisible()){
54741             var b = west.getBox();
54742             var m = west.getMargins();
54743             b.height = centerH - (m.top+m.bottom);
54744             b.x = m.left;
54745             b.y = centerY + m.top;
54746             var totalWidth = (b.width + m.left + m.right);
54747             centerX += totalWidth;
54748             centerW -= totalWidth;
54749             west.updateBox(this.safeBox(b));
54750         }
54751         if(east && east.isVisible()){
54752             var b = east.getBox();
54753             var m = east.getMargins();
54754             b.height = centerH - (m.top+m.bottom);
54755             var totalWidth = (b.width + m.left + m.right);
54756             b.x = w - totalWidth + m.left;
54757             b.y = centerY + m.top;
54758             centerW -= totalWidth;
54759             east.updateBox(this.safeBox(b));
54760         }
54761         if(center){
54762             var m = center.getMargins();
54763             var centerBox = {
54764                 x: centerX + m.left,
54765                 y: centerY + m.top,
54766                 width: centerW - (m.left+m.right),
54767                 height: centerH - (m.top+m.bottom)
54768             };
54769             //if(this.hideOnLayout){
54770                 //center.el.setStyle("display", "block");
54771             //}
54772             center.updateBox(this.safeBox(centerBox));
54773         }
54774         this.el.repaint();
54775         this.fireEvent("layout", this);
54776     },
54777
54778     // private
54779     safeBox : function(box){
54780         box.width = Math.max(0, box.width);
54781         box.height = Math.max(0, box.height);
54782         return box;
54783     },
54784
54785     /**
54786      * Adds a ContentPanel (or subclass) to this layout.
54787      * @param {String} target The target region key (north, south, east, west or center).
54788      * @param {Roo.ContentPanel} panel The panel to add
54789      * @return {Roo.ContentPanel} The added panel
54790      */
54791     add : function(target, panel){
54792          
54793         target = target.toLowerCase();
54794         return this.regions[target].add(panel);
54795     },
54796
54797     /**
54798      * Remove a ContentPanel (or subclass) to this layout.
54799      * @param {String} target The target region key (north, south, east, west or center).
54800      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
54801      * @return {Roo.ContentPanel} The removed panel
54802      */
54803     remove : function(target, panel){
54804         target = target.toLowerCase();
54805         return this.regions[target].remove(panel);
54806     },
54807
54808     /**
54809      * Searches all regions for a panel with the specified id
54810      * @param {String} panelId
54811      * @return {Roo.ContentPanel} The panel or null if it wasn't found
54812      */
54813     findPanel : function(panelId){
54814         var rs = this.regions;
54815         for(var target in rs){
54816             if(typeof rs[target] != "function"){
54817                 var p = rs[target].getPanel(panelId);
54818                 if(p){
54819                     return p;
54820                 }
54821             }
54822         }
54823         return null;
54824     },
54825
54826     /**
54827      * Searches all regions for a panel with the specified id and activates (shows) it.
54828      * @param {String/ContentPanel} panelId The panels id or the panel itself
54829      * @return {Roo.ContentPanel} The shown panel or null
54830      */
54831     showPanel : function(panelId) {
54832       var rs = this.regions;
54833       for(var target in rs){
54834          var r = rs[target];
54835          if(typeof r != "function"){
54836             if(r.hasPanel(panelId)){
54837                return r.showPanel(panelId);
54838             }
54839          }
54840       }
54841       return null;
54842    },
54843
54844    /**
54845      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
54846      * @param {Roo.state.Provider} provider (optional) An alternate state provider
54847      */
54848     restoreState : function(provider){
54849         if(!provider){
54850             provider = Roo.state.Manager;
54851         }
54852         var sm = new Roo.LayoutStateManager();
54853         sm.init(this, provider);
54854     },
54855
54856     /**
54857      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
54858      * object should contain properties for each region to add ContentPanels to, and each property's value should be
54859      * a valid ContentPanel config object.  Example:
54860      * <pre><code>
54861 // Create the main layout
54862 var layout = new Roo.BorderLayout('main-ct', {
54863     west: {
54864         split:true,
54865         minSize: 175,
54866         titlebar: true
54867     },
54868     center: {
54869         title:'Components'
54870     }
54871 }, 'main-ct');
54872
54873 // Create and add multiple ContentPanels at once via configs
54874 layout.batchAdd({
54875    west: {
54876        id: 'source-files',
54877        autoCreate:true,
54878        title:'Ext Source Files',
54879        autoScroll:true,
54880        fitToFrame:true
54881    },
54882    center : {
54883        el: cview,
54884        autoScroll:true,
54885        fitToFrame:true,
54886        toolbar: tb,
54887        resizeEl:'cbody'
54888    }
54889 });
54890 </code></pre>
54891      * @param {Object} regions An object containing ContentPanel configs by region name
54892      */
54893     batchAdd : function(regions){
54894         this.beginUpdate();
54895         for(var rname in regions){
54896             var lr = this.regions[rname];
54897             if(lr){
54898                 this.addTypedPanels(lr, regions[rname]);
54899             }
54900         }
54901         this.endUpdate();
54902     },
54903
54904     // private
54905     addTypedPanels : function(lr, ps){
54906         if(typeof ps == 'string'){
54907             lr.add(new Roo.ContentPanel(ps));
54908         }
54909         else if(ps instanceof Array){
54910             for(var i =0, len = ps.length; i < len; i++){
54911                 this.addTypedPanels(lr, ps[i]);
54912             }
54913         }
54914         else if(!ps.events){ // raw config?
54915             var el = ps.el;
54916             delete ps.el; // prevent conflict
54917             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
54918         }
54919         else {  // panel object assumed!
54920             lr.add(ps);
54921         }
54922     },
54923     /**
54924      * Adds a xtype elements to the layout.
54925      * <pre><code>
54926
54927 layout.addxtype({
54928        xtype : 'ContentPanel',
54929        region: 'west',
54930        items: [ .... ]
54931    }
54932 );
54933
54934 layout.addxtype({
54935         xtype : 'NestedLayoutPanel',
54936         region: 'west',
54937         layout: {
54938            center: { },
54939            west: { }   
54940         },
54941         items : [ ... list of content panels or nested layout panels.. ]
54942    }
54943 );
54944 </code></pre>
54945      * @param {Object} cfg Xtype definition of item to add.
54946      */
54947     addxtype : function(cfg)
54948     {
54949         // basically accepts a pannel...
54950         // can accept a layout region..!?!?
54951         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
54952         
54953         if (!cfg.xtype.match(/Panel$/)) {
54954             return false;
54955         }
54956         var ret = false;
54957         
54958         if (typeof(cfg.region) == 'undefined') {
54959             Roo.log("Failed to add Panel, region was not set");
54960             Roo.log(cfg);
54961             return false;
54962         }
54963         var region = cfg.region;
54964         delete cfg.region;
54965         
54966           
54967         var xitems = [];
54968         if (cfg.items) {
54969             xitems = cfg.items;
54970             delete cfg.items;
54971         }
54972         var nb = false;
54973         
54974         switch(cfg.xtype) 
54975         {
54976             case 'ContentPanel':  // ContentPanel (el, cfg)
54977             case 'ScrollPanel':  // ContentPanel (el, cfg)
54978             case 'ViewPanel': 
54979                 if(cfg.autoCreate) {
54980                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54981                 } else {
54982                     var el = this.el.createChild();
54983                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
54984                 }
54985                 
54986                 this.add(region, ret);
54987                 break;
54988             
54989             
54990             case 'TreePanel': // our new panel!
54991                 cfg.el = this.el.createChild();
54992                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54993                 this.add(region, ret);
54994                 break;
54995             
54996             case 'NestedLayoutPanel': 
54997                 // create a new Layout (which is  a Border Layout...
54998                 var el = this.el.createChild();
54999                 var clayout = cfg.layout;
55000                 delete cfg.layout;
55001                 clayout.items   = clayout.items  || [];
55002                 // replace this exitems with the clayout ones..
55003                 xitems = clayout.items;
55004                  
55005                 
55006                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55007                     cfg.background = false;
55008                 }
55009                 var layout = new Roo.BorderLayout(el, clayout);
55010                 
55011                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55012                 //console.log('adding nested layout panel '  + cfg.toSource());
55013                 this.add(region, ret);
55014                 nb = {}; /// find first...
55015                 break;
55016                 
55017             case 'GridPanel': 
55018             
55019                 // needs grid and region
55020                 
55021                 //var el = this.getRegion(region).el.createChild();
55022                 var el = this.el.createChild();
55023                 // create the grid first...
55024                 
55025                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55026                 delete cfg.grid;
55027                 if (region == 'center' && this.active ) {
55028                     cfg.background = false;
55029                 }
55030                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55031                 
55032                 this.add(region, ret);
55033                 if (cfg.background) {
55034                     ret.on('activate', function(gp) {
55035                         if (!gp.grid.rendered) {
55036                             gp.grid.render();
55037                         }
55038                     });
55039                 } else {
55040                     grid.render();
55041                 }
55042                 break;
55043            
55044            
55045            
55046                 
55047                 
55048                 
55049             default:
55050                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55051                     
55052                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55053                     this.add(region, ret);
55054                 } else {
55055                 
55056                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55057                     return null;
55058                 }
55059                 
55060              // GridPanel (grid, cfg)
55061             
55062         }
55063         this.beginUpdate();
55064         // add children..
55065         var region = '';
55066         var abn = {};
55067         Roo.each(xitems, function(i)  {
55068             region = nb && i.region ? i.region : false;
55069             
55070             var add = ret.addxtype(i);
55071            
55072             if (region) {
55073                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55074                 if (!i.background) {
55075                     abn[region] = nb[region] ;
55076                 }
55077             }
55078             
55079         });
55080         this.endUpdate();
55081
55082         // make the last non-background panel active..
55083         //if (nb) { Roo.log(abn); }
55084         if (nb) {
55085             
55086             for(var r in abn) {
55087                 region = this.getRegion(r);
55088                 if (region) {
55089                     // tried using nb[r], but it does not work..
55090                      
55091                     region.showPanel(abn[r]);
55092                    
55093                 }
55094             }
55095         }
55096         return ret;
55097         
55098     }
55099 });
55100
55101 /**
55102  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55103  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55104  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55105  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55106  * <pre><code>
55107 // shorthand
55108 var CP = Roo.ContentPanel;
55109
55110 var layout = Roo.BorderLayout.create({
55111     north: {
55112         initialSize: 25,
55113         titlebar: false,
55114         panels: [new CP("north", "North")]
55115     },
55116     west: {
55117         split:true,
55118         initialSize: 200,
55119         minSize: 175,
55120         maxSize: 400,
55121         titlebar: true,
55122         collapsible: true,
55123         panels: [new CP("west", {title: "West"})]
55124     },
55125     east: {
55126         split:true,
55127         initialSize: 202,
55128         minSize: 175,
55129         maxSize: 400,
55130         titlebar: true,
55131         collapsible: true,
55132         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55133     },
55134     south: {
55135         split:true,
55136         initialSize: 100,
55137         minSize: 100,
55138         maxSize: 200,
55139         titlebar: true,
55140         collapsible: true,
55141         panels: [new CP("south", {title: "South", closable: true})]
55142     },
55143     center: {
55144         titlebar: true,
55145         autoScroll:true,
55146         resizeTabs: true,
55147         minTabWidth: 50,
55148         preferredTabWidth: 150,
55149         panels: [
55150             new CP("center1", {title: "Close Me", closable: true}),
55151             new CP("center2", {title: "Center Panel", closable: false})
55152         ]
55153     }
55154 }, document.body);
55155
55156 layout.getRegion("center").showPanel("center1");
55157 </code></pre>
55158  * @param config
55159  * @param targetEl
55160  */
55161 Roo.BorderLayout.create = function(config, targetEl){
55162     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55163     layout.beginUpdate();
55164     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55165     for(var j = 0, jlen = regions.length; j < jlen; j++){
55166         var lr = regions[j];
55167         if(layout.regions[lr] && config[lr].panels){
55168             var r = layout.regions[lr];
55169             var ps = config[lr].panels;
55170             layout.addTypedPanels(r, ps);
55171         }
55172     }
55173     layout.endUpdate();
55174     return layout;
55175 };
55176
55177 // private
55178 Roo.BorderLayout.RegionFactory = {
55179     // private
55180     validRegions : ["north","south","east","west","center"],
55181
55182     // private
55183     create : function(target, mgr, config){
55184         target = target.toLowerCase();
55185         if(config.lightweight || config.basic){
55186             return new Roo.BasicLayoutRegion(mgr, config, target);
55187         }
55188         switch(target){
55189             case "north":
55190                 return new Roo.NorthLayoutRegion(mgr, config);
55191             case "south":
55192                 return new Roo.SouthLayoutRegion(mgr, config);
55193             case "east":
55194                 return new Roo.EastLayoutRegion(mgr, config);
55195             case "west":
55196                 return new Roo.WestLayoutRegion(mgr, config);
55197             case "center":
55198                 return new Roo.CenterLayoutRegion(mgr, config);
55199         }
55200         throw 'Layout region "'+target+'" not supported.';
55201     }
55202 };/*
55203  * Based on:
55204  * Ext JS Library 1.1.1
55205  * Copyright(c) 2006-2007, Ext JS, LLC.
55206  *
55207  * Originally Released Under LGPL - original licence link has changed is not relivant.
55208  *
55209  * Fork - LGPL
55210  * <script type="text/javascript">
55211  */
55212  
55213 /**
55214  * @class Roo.BasicLayoutRegion
55215  * @extends Roo.util.Observable
55216  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55217  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55218  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55219  */
55220 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55221     this.mgr = mgr;
55222     this.position  = pos;
55223     this.events = {
55224         /**
55225          * @scope Roo.BasicLayoutRegion
55226          */
55227         
55228         /**
55229          * @event beforeremove
55230          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55231          * @param {Roo.LayoutRegion} this
55232          * @param {Roo.ContentPanel} panel The panel
55233          * @param {Object} e The cancel event object
55234          */
55235         "beforeremove" : true,
55236         /**
55237          * @event invalidated
55238          * Fires when the layout for this region is changed.
55239          * @param {Roo.LayoutRegion} this
55240          */
55241         "invalidated" : true,
55242         /**
55243          * @event visibilitychange
55244          * Fires when this region is shown or hidden 
55245          * @param {Roo.LayoutRegion} this
55246          * @param {Boolean} visibility true or false
55247          */
55248         "visibilitychange" : true,
55249         /**
55250          * @event paneladded
55251          * Fires when a panel is added. 
55252          * @param {Roo.LayoutRegion} this
55253          * @param {Roo.ContentPanel} panel The panel
55254          */
55255         "paneladded" : true,
55256         /**
55257          * @event panelremoved
55258          * Fires when a panel is removed. 
55259          * @param {Roo.LayoutRegion} this
55260          * @param {Roo.ContentPanel} panel The panel
55261          */
55262         "panelremoved" : true,
55263         /**
55264          * @event beforecollapse
55265          * Fires when this region before collapse.
55266          * @param {Roo.LayoutRegion} this
55267          */
55268         "beforecollapse" : true,
55269         /**
55270          * @event collapsed
55271          * Fires when this region is collapsed.
55272          * @param {Roo.LayoutRegion} this
55273          */
55274         "collapsed" : true,
55275         /**
55276          * @event expanded
55277          * Fires when this region is expanded.
55278          * @param {Roo.LayoutRegion} this
55279          */
55280         "expanded" : true,
55281         /**
55282          * @event slideshow
55283          * Fires when this region is slid into view.
55284          * @param {Roo.LayoutRegion} this
55285          */
55286         "slideshow" : true,
55287         /**
55288          * @event slidehide
55289          * Fires when this region slides out of view. 
55290          * @param {Roo.LayoutRegion} this
55291          */
55292         "slidehide" : true,
55293         /**
55294          * @event panelactivated
55295          * Fires when a panel is activated. 
55296          * @param {Roo.LayoutRegion} this
55297          * @param {Roo.ContentPanel} panel The activated panel
55298          */
55299         "panelactivated" : true,
55300         /**
55301          * @event resized
55302          * Fires when the user resizes this region. 
55303          * @param {Roo.LayoutRegion} this
55304          * @param {Number} newSize The new size (width for east/west, height for north/south)
55305          */
55306         "resized" : true
55307     };
55308     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55309     this.panels = new Roo.util.MixedCollection();
55310     this.panels.getKey = this.getPanelId.createDelegate(this);
55311     this.box = null;
55312     this.activePanel = null;
55313     // ensure listeners are added...
55314     
55315     if (config.listeners || config.events) {
55316         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55317             listeners : config.listeners || {},
55318             events : config.events || {}
55319         });
55320     }
55321     
55322     if(skipConfig !== true){
55323         this.applyConfig(config);
55324     }
55325 };
55326
55327 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55328     getPanelId : function(p){
55329         return p.getId();
55330     },
55331     
55332     applyConfig : function(config){
55333         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55334         this.config = config;
55335         
55336     },
55337     
55338     /**
55339      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55340      * the width, for horizontal (north, south) the height.
55341      * @param {Number} newSize The new width or height
55342      */
55343     resizeTo : function(newSize){
55344         var el = this.el ? this.el :
55345                  (this.activePanel ? this.activePanel.getEl() : null);
55346         if(el){
55347             switch(this.position){
55348                 case "east":
55349                 case "west":
55350                     el.setWidth(newSize);
55351                     this.fireEvent("resized", this, newSize);
55352                 break;
55353                 case "north":
55354                 case "south":
55355                     el.setHeight(newSize);
55356                     this.fireEvent("resized", this, newSize);
55357                 break;                
55358             }
55359         }
55360     },
55361     
55362     getBox : function(){
55363         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55364     },
55365     
55366     getMargins : function(){
55367         return this.margins;
55368     },
55369     
55370     updateBox : function(box){
55371         this.box = box;
55372         var el = this.activePanel.getEl();
55373         el.dom.style.left = box.x + "px";
55374         el.dom.style.top = box.y + "px";
55375         this.activePanel.setSize(box.width, box.height);
55376     },
55377     
55378     /**
55379      * Returns the container element for this region.
55380      * @return {Roo.Element}
55381      */
55382     getEl : function(){
55383         return this.activePanel;
55384     },
55385     
55386     /**
55387      * Returns true if this region is currently visible.
55388      * @return {Boolean}
55389      */
55390     isVisible : function(){
55391         return this.activePanel ? true : false;
55392     },
55393     
55394     setActivePanel : function(panel){
55395         panel = this.getPanel(panel);
55396         if(this.activePanel && this.activePanel != panel){
55397             this.activePanel.setActiveState(false);
55398             this.activePanel.getEl().setLeftTop(-10000,-10000);
55399         }
55400         this.activePanel = panel;
55401         panel.setActiveState(true);
55402         if(this.box){
55403             panel.setSize(this.box.width, this.box.height);
55404         }
55405         this.fireEvent("panelactivated", this, panel);
55406         this.fireEvent("invalidated");
55407     },
55408     
55409     /**
55410      * Show the specified panel.
55411      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55412      * @return {Roo.ContentPanel} The shown panel or null
55413      */
55414     showPanel : function(panel){
55415         if(panel = this.getPanel(panel)){
55416             this.setActivePanel(panel);
55417         }
55418         return panel;
55419     },
55420     
55421     /**
55422      * Get the active panel for this region.
55423      * @return {Roo.ContentPanel} The active panel or null
55424      */
55425     getActivePanel : function(){
55426         return this.activePanel;
55427     },
55428     
55429     /**
55430      * Add the passed ContentPanel(s)
55431      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55432      * @return {Roo.ContentPanel} The panel added (if only one was added)
55433      */
55434     add : function(panel){
55435         if(arguments.length > 1){
55436             for(var i = 0, len = arguments.length; i < len; i++) {
55437                 this.add(arguments[i]);
55438             }
55439             return null;
55440         }
55441         if(this.hasPanel(panel)){
55442             this.showPanel(panel);
55443             return panel;
55444         }
55445         var el = panel.getEl();
55446         if(el.dom.parentNode != this.mgr.el.dom){
55447             this.mgr.el.dom.appendChild(el.dom);
55448         }
55449         if(panel.setRegion){
55450             panel.setRegion(this);
55451         }
55452         this.panels.add(panel);
55453         el.setStyle("position", "absolute");
55454         if(!panel.background){
55455             this.setActivePanel(panel);
55456             if(this.config.initialSize && this.panels.getCount()==1){
55457                 this.resizeTo(this.config.initialSize);
55458             }
55459         }
55460         this.fireEvent("paneladded", this, panel);
55461         return panel;
55462     },
55463     
55464     /**
55465      * Returns true if the panel is in this region.
55466      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55467      * @return {Boolean}
55468      */
55469     hasPanel : function(panel){
55470         if(typeof panel == "object"){ // must be panel obj
55471             panel = panel.getId();
55472         }
55473         return this.getPanel(panel) ? true : false;
55474     },
55475     
55476     /**
55477      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55478      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55479      * @param {Boolean} preservePanel Overrides the config preservePanel option
55480      * @return {Roo.ContentPanel} The panel that was removed
55481      */
55482     remove : function(panel, preservePanel){
55483         panel = this.getPanel(panel);
55484         if(!panel){
55485             return null;
55486         }
55487         var e = {};
55488         this.fireEvent("beforeremove", this, panel, e);
55489         if(e.cancel === true){
55490             return null;
55491         }
55492         var panelId = panel.getId();
55493         this.panels.removeKey(panelId);
55494         return panel;
55495     },
55496     
55497     /**
55498      * Returns the panel specified or null if it's not in this region.
55499      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55500      * @return {Roo.ContentPanel}
55501      */
55502     getPanel : function(id){
55503         if(typeof id == "object"){ // must be panel obj
55504             return id;
55505         }
55506         return this.panels.get(id);
55507     },
55508     
55509     /**
55510      * Returns this regions position (north/south/east/west/center).
55511      * @return {String} 
55512      */
55513     getPosition: function(){
55514         return this.position;    
55515     }
55516 });/*
55517  * Based on:
55518  * Ext JS Library 1.1.1
55519  * Copyright(c) 2006-2007, Ext JS, LLC.
55520  *
55521  * Originally Released Under LGPL - original licence link has changed is not relivant.
55522  *
55523  * Fork - LGPL
55524  * <script type="text/javascript">
55525  */
55526  
55527 /**
55528  * @class Roo.LayoutRegion
55529  * @extends Roo.BasicLayoutRegion
55530  * This class represents a region in a layout manager.
55531  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55532  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55533  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55534  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55535  * @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})
55536  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55537  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55538  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55539  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55540  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55541  * @cfg {String}    title           The title for the region (overrides panel titles)
55542  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55543  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55544  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55545  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55546  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55547  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55548  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55549  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55550  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55551  * @cfg {Boolean}   showPin         True to show a pin button
55552  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55553  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55554  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55555  * @cfg {Number}    width           For East/West panels
55556  * @cfg {Number}    height          For North/South panels
55557  * @cfg {Boolean}   split           To show the splitter
55558  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55559  */
55560 Roo.LayoutRegion = function(mgr, config, pos){
55561     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55562     var dh = Roo.DomHelper;
55563     /** This region's container element 
55564     * @type Roo.Element */
55565     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55566     /** This region's title element 
55567     * @type Roo.Element */
55568
55569     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55570         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55571         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55572     ]}, true);
55573     this.titleEl.enableDisplayMode();
55574     /** This region's title text element 
55575     * @type HTMLElement */
55576     this.titleTextEl = this.titleEl.dom.firstChild;
55577     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55578     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55579     this.closeBtn.enableDisplayMode();
55580     this.closeBtn.on("click", this.closeClicked, this);
55581     this.closeBtn.hide();
55582
55583     this.createBody(config);
55584     this.visible = true;
55585     this.collapsed = false;
55586
55587     if(config.hideWhenEmpty){
55588         this.hide();
55589         this.on("paneladded", this.validateVisibility, this);
55590         this.on("panelremoved", this.validateVisibility, this);
55591     }
55592     this.applyConfig(config);
55593 };
55594
55595 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55596
55597     createBody : function(){
55598         /** This region's body element 
55599         * @type Roo.Element */
55600         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55601     },
55602
55603     applyConfig : function(c){
55604         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55605             var dh = Roo.DomHelper;
55606             if(c.titlebar !== false){
55607                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55608                 this.collapseBtn.on("click", this.collapse, this);
55609                 this.collapseBtn.enableDisplayMode();
55610
55611                 if(c.showPin === true || this.showPin){
55612                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55613                     this.stickBtn.enableDisplayMode();
55614                     this.stickBtn.on("click", this.expand, this);
55615                     this.stickBtn.hide();
55616                 }
55617             }
55618             /** This region's collapsed element
55619             * @type Roo.Element */
55620             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55621                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55622             ]}, true);
55623             if(c.floatable !== false){
55624                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55625                this.collapsedEl.on("click", this.collapseClick, this);
55626             }
55627
55628             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55629                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55630                    id: "message", unselectable: "on", style:{"float":"left"}});
55631                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55632              }
55633             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55634             this.expandBtn.on("click", this.expand, this);
55635         }
55636         if(this.collapseBtn){
55637             this.collapseBtn.setVisible(c.collapsible == true);
55638         }
55639         this.cmargins = c.cmargins || this.cmargins ||
55640                          (this.position == "west" || this.position == "east" ?
55641                              {top: 0, left: 2, right:2, bottom: 0} :
55642                              {top: 2, left: 0, right:0, bottom: 2});
55643         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55644         this.bottomTabs = c.tabPosition != "top";
55645         this.autoScroll = c.autoScroll || false;
55646         if(this.autoScroll){
55647             this.bodyEl.setStyle("overflow", "auto");
55648         }else{
55649             this.bodyEl.setStyle("overflow", "hidden");
55650         }
55651         //if(c.titlebar !== false){
55652             if((!c.titlebar && !c.title) || c.titlebar === false){
55653                 this.titleEl.hide();
55654             }else{
55655                 this.titleEl.show();
55656                 if(c.title){
55657                     this.titleTextEl.innerHTML = c.title;
55658                 }
55659             }
55660         //}
55661         this.duration = c.duration || .30;
55662         this.slideDuration = c.slideDuration || .45;
55663         this.config = c;
55664         if(c.collapsed){
55665             this.collapse(true);
55666         }
55667         if(c.hidden){
55668             this.hide();
55669         }
55670     },
55671     /**
55672      * Returns true if this region is currently visible.
55673      * @return {Boolean}
55674      */
55675     isVisible : function(){
55676         return this.visible;
55677     },
55678
55679     /**
55680      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55681      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55682      */
55683     setCollapsedTitle : function(title){
55684         title = title || "&#160;";
55685         if(this.collapsedTitleTextEl){
55686             this.collapsedTitleTextEl.innerHTML = title;
55687         }
55688     },
55689
55690     getBox : function(){
55691         var b;
55692         if(!this.collapsed){
55693             b = this.el.getBox(false, true);
55694         }else{
55695             b = this.collapsedEl.getBox(false, true);
55696         }
55697         return b;
55698     },
55699
55700     getMargins : function(){
55701         return this.collapsed ? this.cmargins : this.margins;
55702     },
55703
55704     highlight : function(){
55705         this.el.addClass("x-layout-panel-dragover");
55706     },
55707
55708     unhighlight : function(){
55709         this.el.removeClass("x-layout-panel-dragover");
55710     },
55711
55712     updateBox : function(box){
55713         this.box = box;
55714         if(!this.collapsed){
55715             this.el.dom.style.left = box.x + "px";
55716             this.el.dom.style.top = box.y + "px";
55717             this.updateBody(box.width, box.height);
55718         }else{
55719             this.collapsedEl.dom.style.left = box.x + "px";
55720             this.collapsedEl.dom.style.top = box.y + "px";
55721             this.collapsedEl.setSize(box.width, box.height);
55722         }
55723         if(this.tabs){
55724             this.tabs.autoSizeTabs();
55725         }
55726     },
55727
55728     updateBody : function(w, h){
55729         if(w !== null){
55730             this.el.setWidth(w);
55731             w -= this.el.getBorderWidth("rl");
55732             if(this.config.adjustments){
55733                 w += this.config.adjustments[0];
55734             }
55735         }
55736         if(h !== null){
55737             this.el.setHeight(h);
55738             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
55739             h -= this.el.getBorderWidth("tb");
55740             if(this.config.adjustments){
55741                 h += this.config.adjustments[1];
55742             }
55743             this.bodyEl.setHeight(h);
55744             if(this.tabs){
55745                 h = this.tabs.syncHeight(h);
55746             }
55747         }
55748         if(this.panelSize){
55749             w = w !== null ? w : this.panelSize.width;
55750             h = h !== null ? h : this.panelSize.height;
55751         }
55752         if(this.activePanel){
55753             var el = this.activePanel.getEl();
55754             w = w !== null ? w : el.getWidth();
55755             h = h !== null ? h : el.getHeight();
55756             this.panelSize = {width: w, height: h};
55757             this.activePanel.setSize(w, h);
55758         }
55759         if(Roo.isIE && this.tabs){
55760             this.tabs.el.repaint();
55761         }
55762     },
55763
55764     /**
55765      * Returns the container element for this region.
55766      * @return {Roo.Element}
55767      */
55768     getEl : function(){
55769         return this.el;
55770     },
55771
55772     /**
55773      * Hides this region.
55774      */
55775     hide : function(){
55776         if(!this.collapsed){
55777             this.el.dom.style.left = "-2000px";
55778             this.el.hide();
55779         }else{
55780             this.collapsedEl.dom.style.left = "-2000px";
55781             this.collapsedEl.hide();
55782         }
55783         this.visible = false;
55784         this.fireEvent("visibilitychange", this, false);
55785     },
55786
55787     /**
55788      * Shows this region if it was previously hidden.
55789      */
55790     show : function(){
55791         if(!this.collapsed){
55792             this.el.show();
55793         }else{
55794             this.collapsedEl.show();
55795         }
55796         this.visible = true;
55797         this.fireEvent("visibilitychange", this, true);
55798     },
55799
55800     closeClicked : function(){
55801         if(this.activePanel){
55802             this.remove(this.activePanel);
55803         }
55804     },
55805
55806     collapseClick : function(e){
55807         if(this.isSlid){
55808            e.stopPropagation();
55809            this.slideIn();
55810         }else{
55811            e.stopPropagation();
55812            this.slideOut();
55813         }
55814     },
55815
55816     /**
55817      * Collapses this region.
55818      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
55819      */
55820     collapse : function(skipAnim, skipCheck){
55821         if(this.collapsed) {
55822             return;
55823         }
55824         
55825         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
55826             
55827             this.collapsed = true;
55828             if(this.split){
55829                 this.split.el.hide();
55830             }
55831             if(this.config.animate && skipAnim !== true){
55832                 this.fireEvent("invalidated", this);
55833                 this.animateCollapse();
55834             }else{
55835                 this.el.setLocation(-20000,-20000);
55836                 this.el.hide();
55837                 this.collapsedEl.show();
55838                 this.fireEvent("collapsed", this);
55839                 this.fireEvent("invalidated", this);
55840             }
55841         }
55842         
55843     },
55844
55845     animateCollapse : function(){
55846         // overridden
55847     },
55848
55849     /**
55850      * Expands this region if it was previously collapsed.
55851      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
55852      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
55853      */
55854     expand : function(e, skipAnim){
55855         if(e) {
55856             e.stopPropagation();
55857         }
55858         if(!this.collapsed || this.el.hasActiveFx()) {
55859             return;
55860         }
55861         if(this.isSlid){
55862             this.afterSlideIn();
55863             skipAnim = true;
55864         }
55865         this.collapsed = false;
55866         if(this.config.animate && skipAnim !== true){
55867             this.animateExpand();
55868         }else{
55869             this.el.show();
55870             if(this.split){
55871                 this.split.el.show();
55872             }
55873             this.collapsedEl.setLocation(-2000,-2000);
55874             this.collapsedEl.hide();
55875             this.fireEvent("invalidated", this);
55876             this.fireEvent("expanded", this);
55877         }
55878     },
55879
55880     animateExpand : function(){
55881         // overridden
55882     },
55883
55884     initTabs : function()
55885     {
55886         this.bodyEl.setStyle("overflow", "hidden");
55887         var ts = new Roo.TabPanel(
55888                 this.bodyEl.dom,
55889                 {
55890                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
55891                     disableTooltips: this.config.disableTabTips,
55892                     toolbar : this.config.toolbar
55893                 }
55894         );
55895         if(this.config.hideTabs){
55896             ts.stripWrap.setDisplayed(false);
55897         }
55898         this.tabs = ts;
55899         ts.resizeTabs = this.config.resizeTabs === true;
55900         ts.minTabWidth = this.config.minTabWidth || 40;
55901         ts.maxTabWidth = this.config.maxTabWidth || 250;
55902         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
55903         ts.monitorResize = false;
55904         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55905         ts.bodyEl.addClass('x-layout-tabs-body');
55906         this.panels.each(this.initPanelAsTab, this);
55907     },
55908
55909     initPanelAsTab : function(panel){
55910         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
55911                     this.config.closeOnTab && panel.isClosable());
55912         if(panel.tabTip !== undefined){
55913             ti.setTooltip(panel.tabTip);
55914         }
55915         ti.on("activate", function(){
55916               this.setActivePanel(panel);
55917         }, this);
55918         if(this.config.closeOnTab){
55919             ti.on("beforeclose", function(t, e){
55920                 e.cancel = true;
55921                 this.remove(panel);
55922             }, this);
55923         }
55924         return ti;
55925     },
55926
55927     updatePanelTitle : function(panel, title){
55928         if(this.activePanel == panel){
55929             this.updateTitle(title);
55930         }
55931         if(this.tabs){
55932             var ti = this.tabs.getTab(panel.getEl().id);
55933             ti.setText(title);
55934             if(panel.tabTip !== undefined){
55935                 ti.setTooltip(panel.tabTip);
55936             }
55937         }
55938     },
55939
55940     updateTitle : function(title){
55941         if(this.titleTextEl && !this.config.title){
55942             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
55943         }
55944     },
55945
55946     setActivePanel : function(panel){
55947         panel = this.getPanel(panel);
55948         if(this.activePanel && this.activePanel != panel){
55949             this.activePanel.setActiveState(false);
55950         }
55951         this.activePanel = panel;
55952         panel.setActiveState(true);
55953         if(this.panelSize){
55954             panel.setSize(this.panelSize.width, this.panelSize.height);
55955         }
55956         if(this.closeBtn){
55957             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
55958         }
55959         this.updateTitle(panel.getTitle());
55960         if(this.tabs){
55961             this.fireEvent("invalidated", this);
55962         }
55963         this.fireEvent("panelactivated", this, panel);
55964     },
55965
55966     /**
55967      * Shows the specified panel.
55968      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
55969      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
55970      */
55971     showPanel : function(panel)
55972     {
55973         panel = this.getPanel(panel);
55974         if(panel){
55975             if(this.tabs){
55976                 var tab = this.tabs.getTab(panel.getEl().id);
55977                 if(tab.isHidden()){
55978                     this.tabs.unhideTab(tab.id);
55979                 }
55980                 tab.activate();
55981             }else{
55982                 this.setActivePanel(panel);
55983             }
55984         }
55985         return panel;
55986     },
55987
55988     /**
55989      * Get the active panel for this region.
55990      * @return {Roo.ContentPanel} The active panel or null
55991      */
55992     getActivePanel : function(){
55993         return this.activePanel;
55994     },
55995
55996     validateVisibility : function(){
55997         if(this.panels.getCount() < 1){
55998             this.updateTitle("&#160;");
55999             this.closeBtn.hide();
56000             this.hide();
56001         }else{
56002             if(!this.isVisible()){
56003                 this.show();
56004             }
56005         }
56006     },
56007
56008     /**
56009      * Adds the passed ContentPanel(s) to this region.
56010      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56011      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56012      */
56013     add : function(panel){
56014         if(arguments.length > 1){
56015             for(var i = 0, len = arguments.length; i < len; i++) {
56016                 this.add(arguments[i]);
56017             }
56018             return null;
56019         }
56020         if(this.hasPanel(panel)){
56021             this.showPanel(panel);
56022             return panel;
56023         }
56024         panel.setRegion(this);
56025         this.panels.add(panel);
56026         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56027             this.bodyEl.dom.appendChild(panel.getEl().dom);
56028             if(panel.background !== true){
56029                 this.setActivePanel(panel);
56030             }
56031             this.fireEvent("paneladded", this, panel);
56032             return panel;
56033         }
56034         if(!this.tabs){
56035             this.initTabs();
56036         }else{
56037             this.initPanelAsTab(panel);
56038         }
56039         if(panel.background !== true){
56040             this.tabs.activate(panel.getEl().id);
56041         }
56042         this.fireEvent("paneladded", this, panel);
56043         return panel;
56044     },
56045
56046     /**
56047      * Hides the tab for the specified panel.
56048      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56049      */
56050     hidePanel : function(panel){
56051         if(this.tabs && (panel = this.getPanel(panel))){
56052             this.tabs.hideTab(panel.getEl().id);
56053         }
56054     },
56055
56056     /**
56057      * Unhides the tab for a previously hidden panel.
56058      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56059      */
56060     unhidePanel : function(panel){
56061         if(this.tabs && (panel = this.getPanel(panel))){
56062             this.tabs.unhideTab(panel.getEl().id);
56063         }
56064     },
56065
56066     clearPanels : function(){
56067         while(this.panels.getCount() > 0){
56068              this.remove(this.panels.first());
56069         }
56070     },
56071
56072     /**
56073      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56074      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56075      * @param {Boolean} preservePanel Overrides the config preservePanel option
56076      * @return {Roo.ContentPanel} The panel that was removed
56077      */
56078     remove : function(panel, preservePanel){
56079         panel = this.getPanel(panel);
56080         if(!panel){
56081             return null;
56082         }
56083         var e = {};
56084         this.fireEvent("beforeremove", this, panel, e);
56085         if(e.cancel === true){
56086             return null;
56087         }
56088         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56089         var panelId = panel.getId();
56090         this.panels.removeKey(panelId);
56091         if(preservePanel){
56092             document.body.appendChild(panel.getEl().dom);
56093         }
56094         if(this.tabs){
56095             this.tabs.removeTab(panel.getEl().id);
56096         }else if (!preservePanel){
56097             this.bodyEl.dom.removeChild(panel.getEl().dom);
56098         }
56099         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56100             var p = this.panels.first();
56101             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56102             tempEl.appendChild(p.getEl().dom);
56103             this.bodyEl.update("");
56104             this.bodyEl.dom.appendChild(p.getEl().dom);
56105             tempEl = null;
56106             this.updateTitle(p.getTitle());
56107             this.tabs = null;
56108             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56109             this.setActivePanel(p);
56110         }
56111         panel.setRegion(null);
56112         if(this.activePanel == panel){
56113             this.activePanel = null;
56114         }
56115         if(this.config.autoDestroy !== false && preservePanel !== true){
56116             try{panel.destroy();}catch(e){}
56117         }
56118         this.fireEvent("panelremoved", this, panel);
56119         return panel;
56120     },
56121
56122     /**
56123      * Returns the TabPanel component used by this region
56124      * @return {Roo.TabPanel}
56125      */
56126     getTabs : function(){
56127         return this.tabs;
56128     },
56129
56130     createTool : function(parentEl, className){
56131         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56132             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56133         btn.addClassOnOver("x-layout-tools-button-over");
56134         return btn;
56135     }
56136 });/*
56137  * Based on:
56138  * Ext JS Library 1.1.1
56139  * Copyright(c) 2006-2007, Ext JS, LLC.
56140  *
56141  * Originally Released Under LGPL - original licence link has changed is not relivant.
56142  *
56143  * Fork - LGPL
56144  * <script type="text/javascript">
56145  */
56146  
56147
56148
56149 /**
56150  * @class Roo.SplitLayoutRegion
56151  * @extends Roo.LayoutRegion
56152  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56153  */
56154 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56155     this.cursor = cursor;
56156     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56157 };
56158
56159 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56160     splitTip : "Drag to resize.",
56161     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56162     useSplitTips : false,
56163
56164     applyConfig : function(config){
56165         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56166         if(config.split){
56167             if(!this.split){
56168                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56169                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56170                 /** The SplitBar for this region 
56171                 * @type Roo.SplitBar */
56172                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56173                 this.split.on("moved", this.onSplitMove, this);
56174                 this.split.useShim = config.useShim === true;
56175                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56176                 if(this.useSplitTips){
56177                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56178                 }
56179                 if(config.collapsible){
56180                     this.split.el.on("dblclick", this.collapse,  this);
56181                 }
56182             }
56183             if(typeof config.minSize != "undefined"){
56184                 this.split.minSize = config.minSize;
56185             }
56186             if(typeof config.maxSize != "undefined"){
56187                 this.split.maxSize = config.maxSize;
56188             }
56189             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56190                 this.hideSplitter();
56191             }
56192         }
56193     },
56194
56195     getHMaxSize : function(){
56196          var cmax = this.config.maxSize || 10000;
56197          var center = this.mgr.getRegion("center");
56198          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56199     },
56200
56201     getVMaxSize : function(){
56202          var cmax = this.config.maxSize || 10000;
56203          var center = this.mgr.getRegion("center");
56204          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56205     },
56206
56207     onSplitMove : function(split, newSize){
56208         this.fireEvent("resized", this, newSize);
56209     },
56210     
56211     /** 
56212      * Returns the {@link Roo.SplitBar} for this region.
56213      * @return {Roo.SplitBar}
56214      */
56215     getSplitBar : function(){
56216         return this.split;
56217     },
56218     
56219     hide : function(){
56220         this.hideSplitter();
56221         Roo.SplitLayoutRegion.superclass.hide.call(this);
56222     },
56223
56224     hideSplitter : function(){
56225         if(this.split){
56226             this.split.el.setLocation(-2000,-2000);
56227             this.split.el.hide();
56228         }
56229     },
56230
56231     show : function(){
56232         if(this.split){
56233             this.split.el.show();
56234         }
56235         Roo.SplitLayoutRegion.superclass.show.call(this);
56236     },
56237     
56238     beforeSlide: function(){
56239         if(Roo.isGecko){// firefox overflow auto bug workaround
56240             this.bodyEl.clip();
56241             if(this.tabs) {
56242                 this.tabs.bodyEl.clip();
56243             }
56244             if(this.activePanel){
56245                 this.activePanel.getEl().clip();
56246                 
56247                 if(this.activePanel.beforeSlide){
56248                     this.activePanel.beforeSlide();
56249                 }
56250             }
56251         }
56252     },
56253     
56254     afterSlide : function(){
56255         if(Roo.isGecko){// firefox overflow auto bug workaround
56256             this.bodyEl.unclip();
56257             if(this.tabs) {
56258                 this.tabs.bodyEl.unclip();
56259             }
56260             if(this.activePanel){
56261                 this.activePanel.getEl().unclip();
56262                 if(this.activePanel.afterSlide){
56263                     this.activePanel.afterSlide();
56264                 }
56265             }
56266         }
56267     },
56268
56269     initAutoHide : function(){
56270         if(this.autoHide !== false){
56271             if(!this.autoHideHd){
56272                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56273                 this.autoHideHd = {
56274                     "mouseout": function(e){
56275                         if(!e.within(this.el, true)){
56276                             st.delay(500);
56277                         }
56278                     },
56279                     "mouseover" : function(e){
56280                         st.cancel();
56281                     },
56282                     scope : this
56283                 };
56284             }
56285             this.el.on(this.autoHideHd);
56286         }
56287     },
56288
56289     clearAutoHide : function(){
56290         if(this.autoHide !== false){
56291             this.el.un("mouseout", this.autoHideHd.mouseout);
56292             this.el.un("mouseover", this.autoHideHd.mouseover);
56293         }
56294     },
56295
56296     clearMonitor : function(){
56297         Roo.get(document).un("click", this.slideInIf, this);
56298     },
56299
56300     // these names are backwards but not changed for compat
56301     slideOut : function(){
56302         if(this.isSlid || this.el.hasActiveFx()){
56303             return;
56304         }
56305         this.isSlid = true;
56306         if(this.collapseBtn){
56307             this.collapseBtn.hide();
56308         }
56309         this.closeBtnState = this.closeBtn.getStyle('display');
56310         this.closeBtn.hide();
56311         if(this.stickBtn){
56312             this.stickBtn.show();
56313         }
56314         this.el.show();
56315         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56316         this.beforeSlide();
56317         this.el.setStyle("z-index", 10001);
56318         this.el.slideIn(this.getSlideAnchor(), {
56319             callback: function(){
56320                 this.afterSlide();
56321                 this.initAutoHide();
56322                 Roo.get(document).on("click", this.slideInIf, this);
56323                 this.fireEvent("slideshow", this);
56324             },
56325             scope: this,
56326             block: true
56327         });
56328     },
56329
56330     afterSlideIn : function(){
56331         this.clearAutoHide();
56332         this.isSlid = false;
56333         this.clearMonitor();
56334         this.el.setStyle("z-index", "");
56335         if(this.collapseBtn){
56336             this.collapseBtn.show();
56337         }
56338         this.closeBtn.setStyle('display', this.closeBtnState);
56339         if(this.stickBtn){
56340             this.stickBtn.hide();
56341         }
56342         this.fireEvent("slidehide", this);
56343     },
56344
56345     slideIn : function(cb){
56346         if(!this.isSlid || this.el.hasActiveFx()){
56347             Roo.callback(cb);
56348             return;
56349         }
56350         this.isSlid = false;
56351         this.beforeSlide();
56352         this.el.slideOut(this.getSlideAnchor(), {
56353             callback: function(){
56354                 this.el.setLeftTop(-10000, -10000);
56355                 this.afterSlide();
56356                 this.afterSlideIn();
56357                 Roo.callback(cb);
56358             },
56359             scope: this,
56360             block: true
56361         });
56362     },
56363     
56364     slideInIf : function(e){
56365         if(!e.within(this.el)){
56366             this.slideIn();
56367         }
56368     },
56369
56370     animateCollapse : function(){
56371         this.beforeSlide();
56372         this.el.setStyle("z-index", 20000);
56373         var anchor = this.getSlideAnchor();
56374         this.el.slideOut(anchor, {
56375             callback : function(){
56376                 this.el.setStyle("z-index", "");
56377                 this.collapsedEl.slideIn(anchor, {duration:.3});
56378                 this.afterSlide();
56379                 this.el.setLocation(-10000,-10000);
56380                 this.el.hide();
56381                 this.fireEvent("collapsed", this);
56382             },
56383             scope: this,
56384             block: true
56385         });
56386     },
56387
56388     animateExpand : function(){
56389         this.beforeSlide();
56390         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56391         this.el.setStyle("z-index", 20000);
56392         this.collapsedEl.hide({
56393             duration:.1
56394         });
56395         this.el.slideIn(this.getSlideAnchor(), {
56396             callback : function(){
56397                 this.el.setStyle("z-index", "");
56398                 this.afterSlide();
56399                 if(this.split){
56400                     this.split.el.show();
56401                 }
56402                 this.fireEvent("invalidated", this);
56403                 this.fireEvent("expanded", this);
56404             },
56405             scope: this,
56406             block: true
56407         });
56408     },
56409
56410     anchors : {
56411         "west" : "left",
56412         "east" : "right",
56413         "north" : "top",
56414         "south" : "bottom"
56415     },
56416
56417     sanchors : {
56418         "west" : "l",
56419         "east" : "r",
56420         "north" : "t",
56421         "south" : "b"
56422     },
56423
56424     canchors : {
56425         "west" : "tl-tr",
56426         "east" : "tr-tl",
56427         "north" : "tl-bl",
56428         "south" : "bl-tl"
56429     },
56430
56431     getAnchor : function(){
56432         return this.anchors[this.position];
56433     },
56434
56435     getCollapseAnchor : function(){
56436         return this.canchors[this.position];
56437     },
56438
56439     getSlideAnchor : function(){
56440         return this.sanchors[this.position];
56441     },
56442
56443     getAlignAdj : function(){
56444         var cm = this.cmargins;
56445         switch(this.position){
56446             case "west":
56447                 return [0, 0];
56448             break;
56449             case "east":
56450                 return [0, 0];
56451             break;
56452             case "north":
56453                 return [0, 0];
56454             break;
56455             case "south":
56456                 return [0, 0];
56457             break;
56458         }
56459     },
56460
56461     getExpandAdj : function(){
56462         var c = this.collapsedEl, cm = this.cmargins;
56463         switch(this.position){
56464             case "west":
56465                 return [-(cm.right+c.getWidth()+cm.left), 0];
56466             break;
56467             case "east":
56468                 return [cm.right+c.getWidth()+cm.left, 0];
56469             break;
56470             case "north":
56471                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56472             break;
56473             case "south":
56474                 return [0, cm.top+cm.bottom+c.getHeight()];
56475             break;
56476         }
56477     }
56478 });/*
56479  * Based on:
56480  * Ext JS Library 1.1.1
56481  * Copyright(c) 2006-2007, Ext JS, LLC.
56482  *
56483  * Originally Released Under LGPL - original licence link has changed is not relivant.
56484  *
56485  * Fork - LGPL
56486  * <script type="text/javascript">
56487  */
56488 /*
56489  * These classes are private internal classes
56490  */
56491 Roo.CenterLayoutRegion = function(mgr, config){
56492     Roo.LayoutRegion.call(this, mgr, config, "center");
56493     this.visible = true;
56494     this.minWidth = config.minWidth || 20;
56495     this.minHeight = config.minHeight || 20;
56496 };
56497
56498 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56499     hide : function(){
56500         // center panel can't be hidden
56501     },
56502     
56503     show : function(){
56504         // center panel can't be hidden
56505     },
56506     
56507     getMinWidth: function(){
56508         return this.minWidth;
56509     },
56510     
56511     getMinHeight: function(){
56512         return this.minHeight;
56513     }
56514 });
56515
56516
56517 Roo.NorthLayoutRegion = function(mgr, config){
56518     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56519     if(this.split){
56520         this.split.placement = Roo.SplitBar.TOP;
56521         this.split.orientation = Roo.SplitBar.VERTICAL;
56522         this.split.el.addClass("x-layout-split-v");
56523     }
56524     var size = config.initialSize || config.height;
56525     if(typeof size != "undefined"){
56526         this.el.setHeight(size);
56527     }
56528 };
56529 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56530     orientation: Roo.SplitBar.VERTICAL,
56531     getBox : function(){
56532         if(this.collapsed){
56533             return this.collapsedEl.getBox();
56534         }
56535         var box = this.el.getBox();
56536         if(this.split){
56537             box.height += this.split.el.getHeight();
56538         }
56539         return box;
56540     },
56541     
56542     updateBox : function(box){
56543         if(this.split && !this.collapsed){
56544             box.height -= this.split.el.getHeight();
56545             this.split.el.setLeft(box.x);
56546             this.split.el.setTop(box.y+box.height);
56547             this.split.el.setWidth(box.width);
56548         }
56549         if(this.collapsed){
56550             this.updateBody(box.width, null);
56551         }
56552         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56553     }
56554 });
56555
56556 Roo.SouthLayoutRegion = function(mgr, config){
56557     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56558     if(this.split){
56559         this.split.placement = Roo.SplitBar.BOTTOM;
56560         this.split.orientation = Roo.SplitBar.VERTICAL;
56561         this.split.el.addClass("x-layout-split-v");
56562     }
56563     var size = config.initialSize || config.height;
56564     if(typeof size != "undefined"){
56565         this.el.setHeight(size);
56566     }
56567 };
56568 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56569     orientation: Roo.SplitBar.VERTICAL,
56570     getBox : function(){
56571         if(this.collapsed){
56572             return this.collapsedEl.getBox();
56573         }
56574         var box = this.el.getBox();
56575         if(this.split){
56576             var sh = this.split.el.getHeight();
56577             box.height += sh;
56578             box.y -= sh;
56579         }
56580         return box;
56581     },
56582     
56583     updateBox : function(box){
56584         if(this.split && !this.collapsed){
56585             var sh = this.split.el.getHeight();
56586             box.height -= sh;
56587             box.y += sh;
56588             this.split.el.setLeft(box.x);
56589             this.split.el.setTop(box.y-sh);
56590             this.split.el.setWidth(box.width);
56591         }
56592         if(this.collapsed){
56593             this.updateBody(box.width, null);
56594         }
56595         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56596     }
56597 });
56598
56599 Roo.EastLayoutRegion = function(mgr, config){
56600     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56601     if(this.split){
56602         this.split.placement = Roo.SplitBar.RIGHT;
56603         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56604         this.split.el.addClass("x-layout-split-h");
56605     }
56606     var size = config.initialSize || config.width;
56607     if(typeof size != "undefined"){
56608         this.el.setWidth(size);
56609     }
56610 };
56611 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56612     orientation: Roo.SplitBar.HORIZONTAL,
56613     getBox : function(){
56614         if(this.collapsed){
56615             return this.collapsedEl.getBox();
56616         }
56617         var box = this.el.getBox();
56618         if(this.split){
56619             var sw = this.split.el.getWidth();
56620             box.width += sw;
56621             box.x -= sw;
56622         }
56623         return box;
56624     },
56625
56626     updateBox : function(box){
56627         if(this.split && !this.collapsed){
56628             var sw = this.split.el.getWidth();
56629             box.width -= sw;
56630             this.split.el.setLeft(box.x);
56631             this.split.el.setTop(box.y);
56632             this.split.el.setHeight(box.height);
56633             box.x += sw;
56634         }
56635         if(this.collapsed){
56636             this.updateBody(null, box.height);
56637         }
56638         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56639     }
56640 });
56641
56642 Roo.WestLayoutRegion = function(mgr, config){
56643     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56644     if(this.split){
56645         this.split.placement = Roo.SplitBar.LEFT;
56646         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56647         this.split.el.addClass("x-layout-split-h");
56648     }
56649     var size = config.initialSize || config.width;
56650     if(typeof size != "undefined"){
56651         this.el.setWidth(size);
56652     }
56653 };
56654 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56655     orientation: Roo.SplitBar.HORIZONTAL,
56656     getBox : function(){
56657         if(this.collapsed){
56658             return this.collapsedEl.getBox();
56659         }
56660         var box = this.el.getBox();
56661         if(this.split){
56662             box.width += this.split.el.getWidth();
56663         }
56664         return box;
56665     },
56666     
56667     updateBox : function(box){
56668         if(this.split && !this.collapsed){
56669             var sw = this.split.el.getWidth();
56670             box.width -= sw;
56671             this.split.el.setLeft(box.x+box.width);
56672             this.split.el.setTop(box.y);
56673             this.split.el.setHeight(box.height);
56674         }
56675         if(this.collapsed){
56676             this.updateBody(null, box.height);
56677         }
56678         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56679     }
56680 });
56681 /*
56682  * Based on:
56683  * Ext JS Library 1.1.1
56684  * Copyright(c) 2006-2007, Ext JS, LLC.
56685  *
56686  * Originally Released Under LGPL - original licence link has changed is not relivant.
56687  *
56688  * Fork - LGPL
56689  * <script type="text/javascript">
56690  */
56691  
56692  
56693 /*
56694  * Private internal class for reading and applying state
56695  */
56696 Roo.LayoutStateManager = function(layout){
56697      // default empty state
56698      this.state = {
56699         north: {},
56700         south: {},
56701         east: {},
56702         west: {}       
56703     };
56704 };
56705
56706 Roo.LayoutStateManager.prototype = {
56707     init : function(layout, provider){
56708         this.provider = provider;
56709         var state = provider.get(layout.id+"-layout-state");
56710         if(state){
56711             var wasUpdating = layout.isUpdating();
56712             if(!wasUpdating){
56713                 layout.beginUpdate();
56714             }
56715             for(var key in state){
56716                 if(typeof state[key] != "function"){
56717                     var rstate = state[key];
56718                     var r = layout.getRegion(key);
56719                     if(r && rstate){
56720                         if(rstate.size){
56721                             r.resizeTo(rstate.size);
56722                         }
56723                         if(rstate.collapsed == true){
56724                             r.collapse(true);
56725                         }else{
56726                             r.expand(null, true);
56727                         }
56728                     }
56729                 }
56730             }
56731             if(!wasUpdating){
56732                 layout.endUpdate();
56733             }
56734             this.state = state; 
56735         }
56736         this.layout = layout;
56737         layout.on("regionresized", this.onRegionResized, this);
56738         layout.on("regioncollapsed", this.onRegionCollapsed, this);
56739         layout.on("regionexpanded", this.onRegionExpanded, this);
56740     },
56741     
56742     storeState : function(){
56743         this.provider.set(this.layout.id+"-layout-state", this.state);
56744     },
56745     
56746     onRegionResized : function(region, newSize){
56747         this.state[region.getPosition()].size = newSize;
56748         this.storeState();
56749     },
56750     
56751     onRegionCollapsed : function(region){
56752         this.state[region.getPosition()].collapsed = true;
56753         this.storeState();
56754     },
56755     
56756     onRegionExpanded : function(region){
56757         this.state[region.getPosition()].collapsed = false;
56758         this.storeState();
56759     }
56760 };/*
56761  * Based on:
56762  * Ext JS Library 1.1.1
56763  * Copyright(c) 2006-2007, Ext JS, LLC.
56764  *
56765  * Originally Released Under LGPL - original licence link has changed is not relivant.
56766  *
56767  * Fork - LGPL
56768  * <script type="text/javascript">
56769  */
56770 /**
56771  * @class Roo.ContentPanel
56772  * @extends Roo.util.Observable
56773  * @children Roo.form.Form Roo.JsonView Roo.View
56774  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
56775  * A basic ContentPanel element.
56776  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
56777  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
56778  * @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
56779  * @cfg {Boolean}   closable      True if the panel can be closed/removed
56780  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
56781  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
56782  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
56783  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
56784  * @cfg {String} title          The title for this panel
56785  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
56786  * @cfg {String} url            Calls {@link #setUrl} with this value
56787  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
56788  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
56789  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
56790  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
56791  * @cfg {String}    style  Extra style to add to the content panel
56792  * @cfg {Roo.menu.Menu} menu  popup menu
56793
56794  * @constructor
56795  * Create a new ContentPanel.
56796  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
56797  * @param {String/Object} config A string to set only the title or a config object
56798  * @param {String} content (optional) Set the HTML content for this panel
56799  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
56800  */
56801 Roo.ContentPanel = function(el, config, content){
56802     
56803      
56804     /*
56805     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
56806         config = el;
56807         el = Roo.id();
56808     }
56809     if (config && config.parentLayout) { 
56810         el = config.parentLayout.el.createChild(); 
56811     }
56812     */
56813     if(el.autoCreate){ // xtype is available if this is called from factory
56814         config = el;
56815         el = Roo.id();
56816     }
56817     this.el = Roo.get(el);
56818     if(!this.el && config && config.autoCreate){
56819         if(typeof config.autoCreate == "object"){
56820             if(!config.autoCreate.id){
56821                 config.autoCreate.id = config.id||el;
56822             }
56823             this.el = Roo.DomHelper.append(document.body,
56824                         config.autoCreate, true);
56825         }else{
56826             this.el = Roo.DomHelper.append(document.body,
56827                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
56828         }
56829     }
56830     
56831     
56832     this.closable = false;
56833     this.loaded = false;
56834     this.active = false;
56835     if(typeof config == "string"){
56836         this.title = config;
56837     }else{
56838         Roo.apply(this, config);
56839     }
56840     
56841     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
56842         this.wrapEl = this.el.wrap();
56843         this.toolbar.container = this.el.insertSibling(false, 'before');
56844         this.toolbar = new Roo.Toolbar(this.toolbar);
56845     }
56846     
56847     // xtype created footer. - not sure if will work as we normally have to render first..
56848     if (this.footer && !this.footer.el && this.footer.xtype) {
56849         if (!this.wrapEl) {
56850             this.wrapEl = this.el.wrap();
56851         }
56852     
56853         this.footer.container = this.wrapEl.createChild();
56854          
56855         this.footer = Roo.factory(this.footer, Roo);
56856         
56857     }
56858     
56859     if(this.resizeEl){
56860         this.resizeEl = Roo.get(this.resizeEl, true);
56861     }else{
56862         this.resizeEl = this.el;
56863     }
56864     // handle view.xtype
56865     
56866  
56867     
56868     
56869     this.addEvents({
56870         /**
56871          * @event activate
56872          * Fires when this panel is activated. 
56873          * @param {Roo.ContentPanel} this
56874          */
56875         "activate" : true,
56876         /**
56877          * @event deactivate
56878          * Fires when this panel is activated. 
56879          * @param {Roo.ContentPanel} this
56880          */
56881         "deactivate" : true,
56882
56883         /**
56884          * @event resize
56885          * Fires when this panel is resized if fitToFrame is true.
56886          * @param {Roo.ContentPanel} this
56887          * @param {Number} width The width after any component adjustments
56888          * @param {Number} height The height after any component adjustments
56889          */
56890         "resize" : true,
56891         
56892          /**
56893          * @event render
56894          * Fires when this tab is created
56895          * @param {Roo.ContentPanel} this
56896          */
56897         "render" : true
56898          
56899         
56900     });
56901     
56902
56903     
56904     
56905     if(this.autoScroll){
56906         this.resizeEl.setStyle("overflow", "auto");
56907     } else {
56908         // fix randome scrolling
56909         this.el.on('scroll', function() {
56910             Roo.log('fix random scolling');
56911             this.scrollTo('top',0); 
56912         });
56913     }
56914     content = content || this.content;
56915     if(content){
56916         this.setContent(content);
56917     }
56918     if(config && config.url){
56919         this.setUrl(this.url, this.params, this.loadOnce);
56920     }
56921     
56922     
56923     
56924     Roo.ContentPanel.superclass.constructor.call(this);
56925     
56926     if (this.view && typeof(this.view.xtype) != 'undefined') {
56927         this.view.el = this.el.appendChild(document.createElement("div"));
56928         this.view = Roo.factory(this.view); 
56929         this.view.render  &&  this.view.render(false, '');  
56930     }
56931     
56932     
56933     this.fireEvent('render', this);
56934 };
56935
56936 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
56937     tabTip:'',
56938     setRegion : function(region){
56939         this.region = region;
56940         if(region){
56941            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
56942         }else{
56943            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
56944         } 
56945     },
56946     
56947     /**
56948      * Returns the toolbar for this Panel if one was configured. 
56949      * @return {Roo.Toolbar} 
56950      */
56951     getToolbar : function(){
56952         return this.toolbar;
56953     },
56954     
56955     setActiveState : function(active){
56956         this.active = active;
56957         if(!active){
56958             this.fireEvent("deactivate", this);
56959         }else{
56960             this.fireEvent("activate", this);
56961         }
56962     },
56963     /**
56964      * Updates this panel's element
56965      * @param {String} content The new content
56966      * @param {Boolean} loadScripts (optional) true to look for and process scripts
56967     */
56968     setContent : function(content, loadScripts){
56969         this.el.update(content, loadScripts);
56970     },
56971
56972     ignoreResize : function(w, h){
56973         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
56974             return true;
56975         }else{
56976             this.lastSize = {width: w, height: h};
56977             return false;
56978         }
56979     },
56980     /**
56981      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
56982      * @return {Roo.UpdateManager} The UpdateManager
56983      */
56984     getUpdateManager : function(){
56985         return this.el.getUpdateManager();
56986     },
56987      /**
56988      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
56989      * @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:
56990 <pre><code>
56991 panel.load({
56992     url: "your-url.php",
56993     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
56994     callback: yourFunction,
56995     scope: yourObject, //(optional scope)
56996     discardUrl: false,
56997     nocache: false,
56998     text: "Loading...",
56999     timeout: 30,
57000     scripts: false
57001 });
57002 </code></pre>
57003      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57004      * 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.
57005      * @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}
57006      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57007      * @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.
57008      * @return {Roo.ContentPanel} this
57009      */
57010     load : function(){
57011         var um = this.el.getUpdateManager();
57012         um.update.apply(um, arguments);
57013         return this;
57014     },
57015
57016
57017     /**
57018      * 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.
57019      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57020      * @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)
57021      * @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)
57022      * @return {Roo.UpdateManager} The UpdateManager
57023      */
57024     setUrl : function(url, params, loadOnce){
57025         if(this.refreshDelegate){
57026             this.removeListener("activate", this.refreshDelegate);
57027         }
57028         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57029         this.on("activate", this.refreshDelegate);
57030         return this.el.getUpdateManager();
57031     },
57032     
57033     _handleRefresh : function(url, params, loadOnce){
57034         if(!loadOnce || !this.loaded){
57035             var updater = this.el.getUpdateManager();
57036             updater.update(url, params, this._setLoaded.createDelegate(this));
57037         }
57038     },
57039     
57040     _setLoaded : function(){
57041         this.loaded = true;
57042     }, 
57043     
57044     /**
57045      * Returns this panel's id
57046      * @return {String} 
57047      */
57048     getId : function(){
57049         return this.el.id;
57050     },
57051     
57052     /** 
57053      * Returns this panel's element - used by regiosn to add.
57054      * @return {Roo.Element} 
57055      */
57056     getEl : function(){
57057         return this.wrapEl || this.el;
57058     },
57059     
57060     adjustForComponents : function(width, height)
57061     {
57062         //Roo.log('adjustForComponents ');
57063         if(this.resizeEl != this.el){
57064             width -= this.el.getFrameWidth('lr');
57065             height -= this.el.getFrameWidth('tb');
57066         }
57067         if(this.toolbar){
57068             var te = this.toolbar.getEl();
57069             height -= te.getHeight();
57070             te.setWidth(width);
57071         }
57072         if(this.footer){
57073             var te = this.footer.getEl();
57074             //Roo.log("footer:" + te.getHeight());
57075             
57076             height -= te.getHeight();
57077             te.setWidth(width);
57078         }
57079         
57080         
57081         if(this.adjustments){
57082             width += this.adjustments[0];
57083             height += this.adjustments[1];
57084         }
57085         return {"width": width, "height": height};
57086     },
57087     
57088     setSize : function(width, height){
57089         if(this.fitToFrame && !this.ignoreResize(width, height)){
57090             if(this.fitContainer && this.resizeEl != this.el){
57091                 this.el.setSize(width, height);
57092             }
57093             var size = this.adjustForComponents(width, height);
57094             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57095             this.fireEvent('resize', this, size.width, size.height);
57096         }
57097     },
57098     
57099     /**
57100      * Returns this panel's title
57101      * @return {String} 
57102      */
57103     getTitle : function(){
57104         return this.title;
57105     },
57106     
57107     /**
57108      * Set this panel's title
57109      * @param {String} title
57110      */
57111     setTitle : function(title){
57112         this.title = title;
57113         if(this.region){
57114             this.region.updatePanelTitle(this, title);
57115         }
57116     },
57117     
57118     /**
57119      * Returns true is this panel was configured to be closable
57120      * @return {Boolean} 
57121      */
57122     isClosable : function(){
57123         return this.closable;
57124     },
57125     
57126     beforeSlide : function(){
57127         this.el.clip();
57128         this.resizeEl.clip();
57129     },
57130     
57131     afterSlide : function(){
57132         this.el.unclip();
57133         this.resizeEl.unclip();
57134     },
57135     
57136     /**
57137      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57138      *   Will fail silently if the {@link #setUrl} method has not been called.
57139      *   This does not activate the panel, just updates its content.
57140      */
57141     refresh : function(){
57142         if(this.refreshDelegate){
57143            this.loaded = false;
57144            this.refreshDelegate();
57145         }
57146     },
57147     
57148     /**
57149      * Destroys this panel
57150      */
57151     destroy : function(){
57152         this.el.removeAllListeners();
57153         var tempEl = document.createElement("span");
57154         tempEl.appendChild(this.el.dom);
57155         tempEl.innerHTML = "";
57156         this.el.remove();
57157         this.el = null;
57158     },
57159     
57160     /**
57161      * form - if the content panel contains a form - this is a reference to it.
57162      * @type {Roo.form.Form}
57163      */
57164     form : false,
57165     /**
57166      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57167      *    This contains a reference to it.
57168      * @type {Roo.View}
57169      */
57170     view : false,
57171     
57172       /**
57173      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57174      * <pre><code>
57175
57176 layout.addxtype({
57177        xtype : 'Form',
57178        items: [ .... ]
57179    }
57180 );
57181
57182 </code></pre>
57183      * @param {Object} cfg Xtype definition of item to add.
57184      */
57185     
57186     addxtype : function(cfg) {
57187         // add form..
57188         if (cfg.xtype.match(/^Form$/)) {
57189             
57190             var el;
57191             //if (this.footer) {
57192             //    el = this.footer.container.insertSibling(false, 'before');
57193             //} else {
57194                 el = this.el.createChild();
57195             //}
57196
57197             this.form = new  Roo.form.Form(cfg);
57198             
57199             
57200             if ( this.form.allItems.length) {
57201                 this.form.render(el.dom);
57202             }
57203             return this.form;
57204         }
57205         // should only have one of theses..
57206         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57207             // views.. should not be just added - used named prop 'view''
57208             
57209             cfg.el = this.el.appendChild(document.createElement("div"));
57210             // factory?
57211             
57212             var ret = new Roo.factory(cfg);
57213              
57214              ret.render && ret.render(false, ''); // render blank..
57215             this.view = ret;
57216             return ret;
57217         }
57218         return false;
57219     }
57220 });
57221
57222 /**
57223  * @class Roo.GridPanel
57224  * @extends Roo.ContentPanel
57225  * @constructor
57226  * Create a new GridPanel.
57227  * @param {Roo.grid.Grid} grid The grid for this panel
57228  * @param {String/Object} config A string to set only the panel's title, or a config object
57229  */
57230 Roo.GridPanel = function(grid, config){
57231     
57232   
57233     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57234         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57235         
57236     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57237     
57238     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57239     
57240     if(this.toolbar){
57241         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57242     }
57243     // xtype created footer. - not sure if will work as we normally have to render first..
57244     if (this.footer && !this.footer.el && this.footer.xtype) {
57245         
57246         this.footer.container = this.grid.getView().getFooterPanel(true);
57247         this.footer.dataSource = this.grid.dataSource;
57248         this.footer = Roo.factory(this.footer, Roo);
57249         
57250     }
57251     
57252     grid.monitorWindowResize = false; // turn off autosizing
57253     grid.autoHeight = false;
57254     grid.autoWidth = false;
57255     this.grid = grid;
57256     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57257 };
57258
57259 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57260     getId : function(){
57261         return this.grid.id;
57262     },
57263     
57264     /**
57265      * Returns the grid for this panel
57266      * @return {Roo.grid.Grid} 
57267      */
57268     getGrid : function(){
57269         return this.grid;    
57270     },
57271     
57272     setSize : function(width, height){
57273         if(!this.ignoreResize(width, height)){
57274             var grid = this.grid;
57275             var size = this.adjustForComponents(width, height);
57276             grid.getGridEl().setSize(size.width, size.height);
57277             grid.autoSize();
57278         }
57279     },
57280     
57281     beforeSlide : function(){
57282         this.grid.getView().scroller.clip();
57283     },
57284     
57285     afterSlide : function(){
57286         this.grid.getView().scroller.unclip();
57287     },
57288     
57289     destroy : function(){
57290         this.grid.destroy();
57291         delete this.grid;
57292         Roo.GridPanel.superclass.destroy.call(this); 
57293     }
57294 });
57295
57296
57297 /**
57298  * @class Roo.NestedLayoutPanel
57299  * @extends Roo.ContentPanel
57300  * @constructor
57301  * Create a new NestedLayoutPanel.
57302  * 
57303  * 
57304  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57305  * @param {String/Object} config A string to set only the title or a config object
57306  */
57307 Roo.NestedLayoutPanel = function(layout, config)
57308 {
57309     // construct with only one argument..
57310     /* FIXME - implement nicer consturctors
57311     if (layout.layout) {
57312         config = layout;
57313         layout = config.layout;
57314         delete config.layout;
57315     }
57316     if (layout.xtype && !layout.getEl) {
57317         // then layout needs constructing..
57318         layout = Roo.factory(layout, Roo);
57319     }
57320     */
57321     
57322     
57323     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57324     
57325     layout.monitorWindowResize = false; // turn off autosizing
57326     this.layout = layout;
57327     this.layout.getEl().addClass("x-layout-nested-layout");
57328     
57329     
57330     
57331     
57332 };
57333
57334 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57335
57336     setSize : function(width, height){
57337         if(!this.ignoreResize(width, height)){
57338             var size = this.adjustForComponents(width, height);
57339             var el = this.layout.getEl();
57340             el.setSize(size.width, size.height);
57341             var touch = el.dom.offsetWidth;
57342             this.layout.layout();
57343             // ie requires a double layout on the first pass
57344             if(Roo.isIE && !this.initialized){
57345                 this.initialized = true;
57346                 this.layout.layout();
57347             }
57348         }
57349     },
57350     
57351     // activate all subpanels if not currently active..
57352     
57353     setActiveState : function(active){
57354         this.active = active;
57355         if(!active){
57356             this.fireEvent("deactivate", this);
57357             return;
57358         }
57359         
57360         this.fireEvent("activate", this);
57361         // not sure if this should happen before or after..
57362         if (!this.layout) {
57363             return; // should not happen..
57364         }
57365         var reg = false;
57366         for (var r in this.layout.regions) {
57367             reg = this.layout.getRegion(r);
57368             if (reg.getActivePanel()) {
57369                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57370                 reg.setActivePanel(reg.getActivePanel());
57371                 continue;
57372             }
57373             if (!reg.panels.length) {
57374                 continue;
57375             }
57376             reg.showPanel(reg.getPanel(0));
57377         }
57378         
57379         
57380         
57381         
57382     },
57383     
57384     /**
57385      * Returns the nested BorderLayout for this panel
57386      * @return {Roo.BorderLayout} 
57387      */
57388     getLayout : function(){
57389         return this.layout;
57390     },
57391     
57392      /**
57393      * Adds a xtype elements to the layout of the nested panel
57394      * <pre><code>
57395
57396 panel.addxtype({
57397        xtype : 'ContentPanel',
57398        region: 'west',
57399        items: [ .... ]
57400    }
57401 );
57402
57403 panel.addxtype({
57404         xtype : 'NestedLayoutPanel',
57405         region: 'west',
57406         layout: {
57407            center: { },
57408            west: { }   
57409         },
57410         items : [ ... list of content panels or nested layout panels.. ]
57411    }
57412 );
57413 </code></pre>
57414      * @param {Object} cfg Xtype definition of item to add.
57415      */
57416     addxtype : function(cfg) {
57417         return this.layout.addxtype(cfg);
57418     
57419     }
57420 });
57421
57422 Roo.ScrollPanel = function(el, config, content){
57423     config = config || {};
57424     config.fitToFrame = true;
57425     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57426     
57427     this.el.dom.style.overflow = "hidden";
57428     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57429     this.el.removeClass("x-layout-inactive-content");
57430     this.el.on("mousewheel", this.onWheel, this);
57431
57432     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57433     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57434     up.unselectable(); down.unselectable();
57435     up.on("click", this.scrollUp, this);
57436     down.on("click", this.scrollDown, this);
57437     up.addClassOnOver("x-scroller-btn-over");
57438     down.addClassOnOver("x-scroller-btn-over");
57439     up.addClassOnClick("x-scroller-btn-click");
57440     down.addClassOnClick("x-scroller-btn-click");
57441     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57442
57443     this.resizeEl = this.el;
57444     this.el = wrap; this.up = up; this.down = down;
57445 };
57446
57447 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57448     increment : 100,
57449     wheelIncrement : 5,
57450     scrollUp : function(){
57451         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57452     },
57453
57454     scrollDown : function(){
57455         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57456     },
57457
57458     afterScroll : function(){
57459         var el = this.resizeEl;
57460         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57461         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57462         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57463     },
57464
57465     setSize : function(){
57466         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57467         this.afterScroll();
57468     },
57469
57470     onWheel : function(e){
57471         var d = e.getWheelDelta();
57472         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57473         this.afterScroll();
57474         e.stopEvent();
57475     },
57476
57477     setContent : function(content, loadScripts){
57478         this.resizeEl.update(content, loadScripts);
57479     }
57480
57481 });
57482
57483
57484
57485 /**
57486  * @class Roo.TreePanel
57487  * @extends Roo.ContentPanel
57488  * Treepanel component
57489  * 
57490  * @constructor
57491  * Create a new TreePanel. - defaults to fit/scoll contents.
57492  * @param {String/Object} config A string to set only the panel's title, or a config object
57493  */
57494 Roo.TreePanel = function(config){
57495     var el = config.el;
57496     var tree = config.tree;
57497     delete config.tree; 
57498     delete config.el; // hopefull!
57499     
57500     // wrapper for IE7 strict & safari scroll issue
57501     
57502     var treeEl = el.createChild();
57503     config.resizeEl = treeEl;
57504     
57505     
57506     
57507     Roo.TreePanel.superclass.constructor.call(this, el, config);
57508  
57509  
57510     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57511     //console.log(tree);
57512     this.on('activate', function()
57513     {
57514         if (this.tree.rendered) {
57515             return;
57516         }
57517         //console.log('render tree');
57518         this.tree.render();
57519     });
57520     // this should not be needed.. - it's actually the 'el' that resizes?
57521     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57522     
57523     //this.on('resize',  function (cp, w, h) {
57524     //        this.tree.innerCt.setWidth(w);
57525     //        this.tree.innerCt.setHeight(h);
57526     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57527     //});
57528
57529         
57530     
57531 };
57532
57533 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57534     fitToFrame : true,
57535     autoScroll : true,
57536     /*
57537      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57538      */
57539     tree : false
57540
57541 });
57542
57543
57544
57545
57546
57547
57548
57549
57550
57551
57552
57553 /*
57554  * Based on:
57555  * Ext JS Library 1.1.1
57556  * Copyright(c) 2006-2007, Ext JS, LLC.
57557  *
57558  * Originally Released Under LGPL - original licence link has changed is not relivant.
57559  *
57560  * Fork - LGPL
57561  * <script type="text/javascript">
57562  */
57563  
57564
57565 /**
57566  * @class Roo.ReaderLayout
57567  * @extends Roo.BorderLayout
57568  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57569  * center region containing two nested regions (a top one for a list view and one for item preview below),
57570  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57571  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57572  * expedites the setup of the overall layout and regions for this common application style.
57573  * Example:
57574  <pre><code>
57575 var reader = new Roo.ReaderLayout();
57576 var CP = Roo.ContentPanel;  // shortcut for adding
57577
57578 reader.beginUpdate();
57579 reader.add("north", new CP("north", "North"));
57580 reader.add("west", new CP("west", {title: "West"}));
57581 reader.add("east", new CP("east", {title: "East"}));
57582
57583 reader.regions.listView.add(new CP("listView", "List"));
57584 reader.regions.preview.add(new CP("preview", "Preview"));
57585 reader.endUpdate();
57586 </code></pre>
57587 * @constructor
57588 * Create a new ReaderLayout
57589 * @param {Object} config Configuration options
57590 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57591 * document.body if omitted)
57592 */
57593 Roo.ReaderLayout = function(config, renderTo){
57594     var c = config || {size:{}};
57595     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57596         north: c.north !== false ? Roo.apply({
57597             split:false,
57598             initialSize: 32,
57599             titlebar: false
57600         }, c.north) : false,
57601         west: c.west !== false ? Roo.apply({
57602             split:true,
57603             initialSize: 200,
57604             minSize: 175,
57605             maxSize: 400,
57606             titlebar: true,
57607             collapsible: true,
57608             animate: true,
57609             margins:{left:5,right:0,bottom:5,top:5},
57610             cmargins:{left:5,right:5,bottom:5,top:5}
57611         }, c.west) : false,
57612         east: c.east !== false ? Roo.apply({
57613             split:true,
57614             initialSize: 200,
57615             minSize: 175,
57616             maxSize: 400,
57617             titlebar: true,
57618             collapsible: true,
57619             animate: true,
57620             margins:{left:0,right:5,bottom:5,top:5},
57621             cmargins:{left:5,right:5,bottom:5,top:5}
57622         }, c.east) : false,
57623         center: Roo.apply({
57624             tabPosition: 'top',
57625             autoScroll:false,
57626             closeOnTab: true,
57627             titlebar:false,
57628             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57629         }, c.center)
57630     });
57631
57632     this.el.addClass('x-reader');
57633
57634     this.beginUpdate();
57635
57636     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57637         south: c.preview !== false ? Roo.apply({
57638             split:true,
57639             initialSize: 200,
57640             minSize: 100,
57641             autoScroll:true,
57642             collapsible:true,
57643             titlebar: true,
57644             cmargins:{top:5,left:0, right:0, bottom:0}
57645         }, c.preview) : false,
57646         center: Roo.apply({
57647             autoScroll:false,
57648             titlebar:false,
57649             minHeight:200
57650         }, c.listView)
57651     });
57652     this.add('center', new Roo.NestedLayoutPanel(inner,
57653             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57654
57655     this.endUpdate();
57656
57657     this.regions.preview = inner.getRegion('south');
57658     this.regions.listView = inner.getRegion('center');
57659 };
57660
57661 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57662  * Based on:
57663  * Ext JS Library 1.1.1
57664  * Copyright(c) 2006-2007, Ext JS, LLC.
57665  *
57666  * Originally Released Under LGPL - original licence link has changed is not relivant.
57667  *
57668  * Fork - LGPL
57669  * <script type="text/javascript">
57670  */
57671  
57672 /**
57673  * @class Roo.grid.Grid
57674  * @extends Roo.util.Observable
57675  * This class represents the primary interface of a component based grid control.
57676  * <br><br>Usage:<pre><code>
57677  var grid = new Roo.grid.Grid("my-container-id", {
57678      ds: myDataStore,
57679      cm: myColModel,
57680      selModel: mySelectionModel,
57681      autoSizeColumns: true,
57682      monitorWindowResize: false,
57683      trackMouseOver: true
57684  });
57685  // set any options
57686  grid.render();
57687  * </code></pre>
57688  * <b>Common Problems:</b><br/>
57689  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57690  * element will correct this<br/>
57691  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57692  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57693  * are unpredictable.<br/>
57694  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57695  * grid to calculate dimensions/offsets.<br/>
57696   * @constructor
57697  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57698  * The container MUST have some type of size defined for the grid to fill. The container will be
57699  * automatically set to position relative if it isn't already.
57700  * @param {Object} config A config object that sets properties on this grid.
57701  */
57702 Roo.grid.Grid = function(container, config){
57703         // initialize the container
57704         this.container = Roo.get(container);
57705         this.container.update("");
57706         this.container.setStyle("overflow", "hidden");
57707     this.container.addClass('x-grid-container');
57708
57709     this.id = this.container.id;
57710
57711     Roo.apply(this, config);
57712     // check and correct shorthanded configs
57713     if(this.ds){
57714         this.dataSource = this.ds;
57715         delete this.ds;
57716     }
57717     if(this.cm){
57718         this.colModel = this.cm;
57719         delete this.cm;
57720     }
57721     if(this.sm){
57722         this.selModel = this.sm;
57723         delete this.sm;
57724     }
57725
57726     if (this.selModel) {
57727         this.selModel = Roo.factory(this.selModel, Roo.grid);
57728         this.sm = this.selModel;
57729         this.sm.xmodule = this.xmodule || false;
57730     }
57731     if (typeof(this.colModel.config) == 'undefined') {
57732         this.colModel = new Roo.grid.ColumnModel(this.colModel);
57733         this.cm = this.colModel;
57734         this.cm.xmodule = this.xmodule || false;
57735     }
57736     if (this.dataSource) {
57737         this.dataSource= Roo.factory(this.dataSource, Roo.data);
57738         this.ds = this.dataSource;
57739         this.ds.xmodule = this.xmodule || false;
57740          
57741     }
57742     
57743     
57744     
57745     if(this.width){
57746         this.container.setWidth(this.width);
57747     }
57748
57749     if(this.height){
57750         this.container.setHeight(this.height);
57751     }
57752     /** @private */
57753         this.addEvents({
57754         // raw events
57755         /**
57756          * @event click
57757          * The raw click event for the entire grid.
57758          * @param {Roo.EventObject} e
57759          */
57760         "click" : true,
57761         /**
57762          * @event dblclick
57763          * The raw dblclick event for the entire grid.
57764          * @param {Roo.EventObject} e
57765          */
57766         "dblclick" : true,
57767         /**
57768          * @event contextmenu
57769          * The raw contextmenu event for the entire grid.
57770          * @param {Roo.EventObject} e
57771          */
57772         "contextmenu" : true,
57773         /**
57774          * @event mousedown
57775          * The raw mousedown event for the entire grid.
57776          * @param {Roo.EventObject} e
57777          */
57778         "mousedown" : true,
57779         /**
57780          * @event mouseup
57781          * The raw mouseup event for the entire grid.
57782          * @param {Roo.EventObject} e
57783          */
57784         "mouseup" : true,
57785         /**
57786          * @event mouseover
57787          * The raw mouseover event for the entire grid.
57788          * @param {Roo.EventObject} e
57789          */
57790         "mouseover" : true,
57791         /**
57792          * @event mouseout
57793          * The raw mouseout event for the entire grid.
57794          * @param {Roo.EventObject} e
57795          */
57796         "mouseout" : true,
57797         /**
57798          * @event keypress
57799          * The raw keypress event for the entire grid.
57800          * @param {Roo.EventObject} e
57801          */
57802         "keypress" : true,
57803         /**
57804          * @event keydown
57805          * The raw keydown event for the entire grid.
57806          * @param {Roo.EventObject} e
57807          */
57808         "keydown" : true,
57809
57810         // custom events
57811
57812         /**
57813          * @event cellclick
57814          * Fires when a cell is clicked
57815          * @param {Grid} this
57816          * @param {Number} rowIndex
57817          * @param {Number} columnIndex
57818          * @param {Roo.EventObject} e
57819          */
57820         "cellclick" : true,
57821         /**
57822          * @event celldblclick
57823          * Fires when a cell is double clicked
57824          * @param {Grid} this
57825          * @param {Number} rowIndex
57826          * @param {Number} columnIndex
57827          * @param {Roo.EventObject} e
57828          */
57829         "celldblclick" : true,
57830         /**
57831          * @event rowclick
57832          * Fires when a row is clicked
57833          * @param {Grid} this
57834          * @param {Number} rowIndex
57835          * @param {Roo.EventObject} e
57836          */
57837         "rowclick" : true,
57838         /**
57839          * @event rowdblclick
57840          * Fires when a row is double clicked
57841          * @param {Grid} this
57842          * @param {Number} rowIndex
57843          * @param {Roo.EventObject} e
57844          */
57845         "rowdblclick" : true,
57846         /**
57847          * @event headerclick
57848          * Fires when a header is clicked
57849          * @param {Grid} this
57850          * @param {Number} columnIndex
57851          * @param {Roo.EventObject} e
57852          */
57853         "headerclick" : true,
57854         /**
57855          * @event headerdblclick
57856          * Fires when a header cell is double clicked
57857          * @param {Grid} this
57858          * @param {Number} columnIndex
57859          * @param {Roo.EventObject} e
57860          */
57861         "headerdblclick" : true,
57862         /**
57863          * @event rowcontextmenu
57864          * Fires when a row is right clicked
57865          * @param {Grid} this
57866          * @param {Number} rowIndex
57867          * @param {Roo.EventObject} e
57868          */
57869         "rowcontextmenu" : true,
57870         /**
57871          * @event cellcontextmenu
57872          * Fires when a cell is right clicked
57873          * @param {Grid} this
57874          * @param {Number} rowIndex
57875          * @param {Number} cellIndex
57876          * @param {Roo.EventObject} e
57877          */
57878          "cellcontextmenu" : true,
57879         /**
57880          * @event headercontextmenu
57881          * Fires when a header is right clicked
57882          * @param {Grid} this
57883          * @param {Number} columnIndex
57884          * @param {Roo.EventObject} e
57885          */
57886         "headercontextmenu" : true,
57887         /**
57888          * @event bodyscroll
57889          * Fires when the body element is scrolled
57890          * @param {Number} scrollLeft
57891          * @param {Number} scrollTop
57892          */
57893         "bodyscroll" : true,
57894         /**
57895          * @event columnresize
57896          * Fires when the user resizes a column
57897          * @param {Number} columnIndex
57898          * @param {Number} newSize
57899          */
57900         "columnresize" : true,
57901         /**
57902          * @event columnmove
57903          * Fires when the user moves a column
57904          * @param {Number} oldIndex
57905          * @param {Number} newIndex
57906          */
57907         "columnmove" : true,
57908         /**
57909          * @event startdrag
57910          * Fires when row(s) start being dragged
57911          * @param {Grid} this
57912          * @param {Roo.GridDD} dd The drag drop object
57913          * @param {event} e The raw browser event
57914          */
57915         "startdrag" : true,
57916         /**
57917          * @event enddrag
57918          * Fires when a drag operation is complete
57919          * @param {Grid} this
57920          * @param {Roo.GridDD} dd The drag drop object
57921          * @param {event} e The raw browser event
57922          */
57923         "enddrag" : true,
57924         /**
57925          * @event dragdrop
57926          * Fires when dragged row(s) are dropped on a valid DD target
57927          * @param {Grid} this
57928          * @param {Roo.GridDD} dd The drag drop object
57929          * @param {String} targetId The target drag drop object
57930          * @param {event} e The raw browser event
57931          */
57932         "dragdrop" : true,
57933         /**
57934          * @event dragover
57935          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57936          * @param {Grid} this
57937          * @param {Roo.GridDD} dd The drag drop object
57938          * @param {String} targetId The target drag drop object
57939          * @param {event} e The raw browser event
57940          */
57941         "dragover" : true,
57942         /**
57943          * @event dragenter
57944          *  Fires when the dragged row(s) first cross another DD target while being dragged
57945          * @param {Grid} this
57946          * @param {Roo.GridDD} dd The drag drop object
57947          * @param {String} targetId The target drag drop object
57948          * @param {event} e The raw browser event
57949          */
57950         "dragenter" : true,
57951         /**
57952          * @event dragout
57953          * Fires when the dragged row(s) leave another DD target while being dragged
57954          * @param {Grid} this
57955          * @param {Roo.GridDD} dd The drag drop object
57956          * @param {String} targetId The target drag drop object
57957          * @param {event} e The raw browser event
57958          */
57959         "dragout" : true,
57960         /**
57961          * @event rowclass
57962          * Fires when a row is rendered, so you can change add a style to it.
57963          * @param {GridView} gridview   The grid view
57964          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57965          */
57966         'rowclass' : true,
57967
57968         /**
57969          * @event render
57970          * Fires when the grid is rendered
57971          * @param {Grid} grid
57972          */
57973         'render' : true
57974     });
57975
57976     Roo.grid.Grid.superclass.constructor.call(this);
57977 };
57978 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
57979     
57980     /**
57981          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
57982          */
57983         /**
57984          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
57985          */
57986         /**
57987          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
57988          */
57989         /**
57990          * @cfg {Roo.grid.Store} ds The data store for the grid
57991          */
57992         /**
57993          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
57994          */
57995         /**
57996      * @cfg {String} ddGroup - drag drop group.
57997      */
57998       /**
57999      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58000      */
58001
58002     /**
58003      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58004      */
58005     minColumnWidth : 25,
58006
58007     /**
58008      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58009      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58010      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58011      */
58012     autoSizeColumns : false,
58013
58014     /**
58015      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58016      */
58017     autoSizeHeaders : true,
58018
58019     /**
58020      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58021      */
58022     monitorWindowResize : true,
58023
58024     /**
58025      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58026      * rows measured to get a columns size. Default is 0 (all rows).
58027      */
58028     maxRowsToMeasure : 0,
58029
58030     /**
58031      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58032      */
58033     trackMouseOver : true,
58034
58035     /**
58036     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58037     */
58038       /**
58039     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58040     */
58041     
58042     /**
58043     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58044     */
58045     enableDragDrop : false,
58046     
58047     /**
58048     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58049     */
58050     enableColumnMove : true,
58051     
58052     /**
58053     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58054     */
58055     enableColumnHide : true,
58056     
58057     /**
58058     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58059     */
58060     enableRowHeightSync : false,
58061     
58062     /**
58063     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58064     */
58065     stripeRows : true,
58066     
58067     /**
58068     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58069     */
58070     autoHeight : false,
58071
58072     /**
58073      * @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.
58074      */
58075     autoExpandColumn : false,
58076
58077     /**
58078     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58079     * Default is 50.
58080     */
58081     autoExpandMin : 50,
58082
58083     /**
58084     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58085     */
58086     autoExpandMax : 1000,
58087
58088     /**
58089     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58090     */
58091     view : null,
58092
58093     /**
58094     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58095     */
58096     loadMask : false,
58097     /**
58098     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58099     */
58100     dropTarget: false,
58101     
58102    
58103     
58104     // private
58105     rendered : false,
58106
58107     /**
58108     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58109     * of a fixed width. Default is false.
58110     */
58111     /**
58112     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58113     */
58114     
58115     
58116     /**
58117     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58118     * %0 is replaced with the number of selected rows.
58119     */
58120     ddText : "{0} selected row{1}",
58121     
58122     
58123     /**
58124      * Called once after all setup has been completed and the grid is ready to be rendered.
58125      * @return {Roo.grid.Grid} this
58126      */
58127     render : function()
58128     {
58129         var c = this.container;
58130         // try to detect autoHeight/width mode
58131         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58132             this.autoHeight = true;
58133         }
58134         var view = this.getView();
58135         view.init(this);
58136
58137         c.on("click", this.onClick, this);
58138         c.on("dblclick", this.onDblClick, this);
58139         c.on("contextmenu", this.onContextMenu, this);
58140         c.on("keydown", this.onKeyDown, this);
58141         if (Roo.isTouch) {
58142             c.on("touchstart", this.onTouchStart, this);
58143         }
58144
58145         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58146
58147         this.getSelectionModel().init(this);
58148
58149         view.render();
58150
58151         if(this.loadMask){
58152             this.loadMask = new Roo.LoadMask(this.container,
58153                     Roo.apply({store:this.dataSource}, this.loadMask));
58154         }
58155         
58156         
58157         if (this.toolbar && this.toolbar.xtype) {
58158             this.toolbar.container = this.getView().getHeaderPanel(true);
58159             this.toolbar = new Roo.Toolbar(this.toolbar);
58160         }
58161         if (this.footer && this.footer.xtype) {
58162             this.footer.dataSource = this.getDataSource();
58163             this.footer.container = this.getView().getFooterPanel(true);
58164             this.footer = Roo.factory(this.footer, Roo);
58165         }
58166         if (this.dropTarget && this.dropTarget.xtype) {
58167             delete this.dropTarget.xtype;
58168             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58169         }
58170         
58171         
58172         this.rendered = true;
58173         this.fireEvent('render', this);
58174         return this;
58175     },
58176
58177     /**
58178      * Reconfigures the grid to use a different Store and Column Model.
58179      * The View will be bound to the new objects and refreshed.
58180      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58181      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58182      */
58183     reconfigure : function(dataSource, colModel){
58184         if(this.loadMask){
58185             this.loadMask.destroy();
58186             this.loadMask = new Roo.LoadMask(this.container,
58187                     Roo.apply({store:dataSource}, this.loadMask));
58188         }
58189         this.view.bind(dataSource, colModel);
58190         this.dataSource = dataSource;
58191         this.colModel = colModel;
58192         this.view.refresh(true);
58193     },
58194     /**
58195      * addColumns
58196      * Add's a column, default at the end..
58197      
58198      * @param {int} position to add (default end)
58199      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58200      */
58201     addColumns : function(pos, ar)
58202     {
58203         
58204         for (var i =0;i< ar.length;i++) {
58205             var cfg = ar[i];
58206             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58207             this.cm.lookup[cfg.id] = cfg;
58208         }
58209         
58210         
58211         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58212             pos = this.cm.config.length; //this.cm.config.push(cfg);
58213         } 
58214         pos = Math.max(0,pos);
58215         ar.unshift(0);
58216         ar.unshift(pos);
58217         this.cm.config.splice.apply(this.cm.config, ar);
58218         
58219         
58220         
58221         this.view.generateRules(this.cm);
58222         this.view.refresh(true);
58223         
58224     },
58225     
58226     
58227     
58228     
58229     // private
58230     onKeyDown : function(e){
58231         this.fireEvent("keydown", e);
58232     },
58233
58234     /**
58235      * Destroy this grid.
58236      * @param {Boolean} removeEl True to remove the element
58237      */
58238     destroy : function(removeEl, keepListeners){
58239         if(this.loadMask){
58240             this.loadMask.destroy();
58241         }
58242         var c = this.container;
58243         c.removeAllListeners();
58244         this.view.destroy();
58245         this.colModel.purgeListeners();
58246         if(!keepListeners){
58247             this.purgeListeners();
58248         }
58249         c.update("");
58250         if(removeEl === true){
58251             c.remove();
58252         }
58253     },
58254
58255     // private
58256     processEvent : function(name, e){
58257         // does this fire select???
58258         //Roo.log('grid:processEvent '  + name);
58259         
58260         if (name != 'touchstart' ) {
58261             this.fireEvent(name, e);    
58262         }
58263         
58264         var t = e.getTarget();
58265         var v = this.view;
58266         var header = v.findHeaderIndex(t);
58267         if(header !== false){
58268             var ename = name == 'touchstart' ? 'click' : name;
58269              
58270             this.fireEvent("header" + ename, this, header, e);
58271         }else{
58272             var row = v.findRowIndex(t);
58273             var cell = v.findCellIndex(t);
58274             if (name == 'touchstart') {
58275                 // first touch is always a click.
58276                 // hopefull this happens after selection is updated.?
58277                 name = false;
58278                 
58279                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58280                     var cs = this.selModel.getSelectedCell();
58281                     if (row == cs[0] && cell == cs[1]){
58282                         name = 'dblclick';
58283                     }
58284                 }
58285                 if (typeof(this.selModel.getSelections) != 'undefined') {
58286                     var cs = this.selModel.getSelections();
58287                     var ds = this.dataSource;
58288                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58289                         name = 'dblclick';
58290                     }
58291                 }
58292                 if (!name) {
58293                     return;
58294                 }
58295             }
58296             
58297             
58298             if(row !== false){
58299                 this.fireEvent("row" + name, this, row, e);
58300                 if(cell !== false){
58301                     this.fireEvent("cell" + name, this, row, cell, e);
58302                 }
58303             }
58304         }
58305     },
58306
58307     // private
58308     onClick : function(e){
58309         this.processEvent("click", e);
58310     },
58311    // private
58312     onTouchStart : function(e){
58313         this.processEvent("touchstart", e);
58314     },
58315
58316     // private
58317     onContextMenu : function(e, t){
58318         this.processEvent("contextmenu", e);
58319     },
58320
58321     // private
58322     onDblClick : function(e){
58323         this.processEvent("dblclick", e);
58324     },
58325
58326     // private
58327     walkCells : function(row, col, step, fn, scope){
58328         var cm = this.colModel, clen = cm.getColumnCount();
58329         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58330         if(step < 0){
58331             if(col < 0){
58332                 row--;
58333                 first = false;
58334             }
58335             while(row >= 0){
58336                 if(!first){
58337                     col = clen-1;
58338                 }
58339                 first = false;
58340                 while(col >= 0){
58341                     if(fn.call(scope || this, row, col, cm) === true){
58342                         return [row, col];
58343                     }
58344                     col--;
58345                 }
58346                 row--;
58347             }
58348         } else {
58349             if(col >= clen){
58350                 row++;
58351                 first = false;
58352             }
58353             while(row < rlen){
58354                 if(!first){
58355                     col = 0;
58356                 }
58357                 first = false;
58358                 while(col < clen){
58359                     if(fn.call(scope || this, row, col, cm) === true){
58360                         return [row, col];
58361                     }
58362                     col++;
58363                 }
58364                 row++;
58365             }
58366         }
58367         return null;
58368     },
58369
58370     // private
58371     getSelections : function(){
58372         return this.selModel.getSelections();
58373     },
58374
58375     /**
58376      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58377      * but if manual update is required this method will initiate it.
58378      */
58379     autoSize : function(){
58380         if(this.rendered){
58381             this.view.layout();
58382             if(this.view.adjustForScroll){
58383                 this.view.adjustForScroll();
58384             }
58385         }
58386     },
58387
58388     /**
58389      * Returns the grid's underlying element.
58390      * @return {Element} The element
58391      */
58392     getGridEl : function(){
58393         return this.container;
58394     },
58395
58396     // private for compatibility, overridden by editor grid
58397     stopEditing : function(){},
58398
58399     /**
58400      * Returns the grid's SelectionModel.
58401      * @return {SelectionModel}
58402      */
58403     getSelectionModel : function(){
58404         if(!this.selModel){
58405             this.selModel = new Roo.grid.RowSelectionModel();
58406         }
58407         return this.selModel;
58408     },
58409
58410     /**
58411      * Returns the grid's DataSource.
58412      * @return {DataSource}
58413      */
58414     getDataSource : function(){
58415         return this.dataSource;
58416     },
58417
58418     /**
58419      * Returns the grid's ColumnModel.
58420      * @return {ColumnModel}
58421      */
58422     getColumnModel : function(){
58423         return this.colModel;
58424     },
58425
58426     /**
58427      * Returns the grid's GridView object.
58428      * @return {GridView}
58429      */
58430     getView : function(){
58431         if(!this.view){
58432             this.view = new Roo.grid.GridView(this.viewConfig);
58433             this.relayEvents(this.view, [
58434                 "beforerowremoved", "beforerowsinserted",
58435                 "beforerefresh", "rowremoved",
58436                 "rowsinserted", "rowupdated" ,"refresh"
58437             ]);
58438         }
58439         return this.view;
58440     },
58441     /**
58442      * Called to get grid's drag proxy text, by default returns this.ddText.
58443      * Override this to put something different in the dragged text.
58444      * @return {String}
58445      */
58446     getDragDropText : function(){
58447         var count = this.selModel.getCount();
58448         return String.format(this.ddText, count, count == 1 ? '' : 's');
58449     }
58450 });
58451 /*
58452  * Based on:
58453  * Ext JS Library 1.1.1
58454  * Copyright(c) 2006-2007, Ext JS, LLC.
58455  *
58456  * Originally Released Under LGPL - original licence link has changed is not relivant.
58457  *
58458  * Fork - LGPL
58459  * <script type="text/javascript">
58460  */
58461  /**
58462  * @class Roo.grid.AbstractGridView
58463  * @extends Roo.util.Observable
58464  * @abstract
58465  * Abstract base class for grid Views
58466  * @constructor
58467  */
58468 Roo.grid.AbstractGridView = function(){
58469         this.grid = null;
58470         
58471         this.events = {
58472             "beforerowremoved" : true,
58473             "beforerowsinserted" : true,
58474             "beforerefresh" : true,
58475             "rowremoved" : true,
58476             "rowsinserted" : true,
58477             "rowupdated" : true,
58478             "refresh" : true
58479         };
58480     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58481 };
58482
58483 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58484     rowClass : "x-grid-row",
58485     cellClass : "x-grid-cell",
58486     tdClass : "x-grid-td",
58487     hdClass : "x-grid-hd",
58488     splitClass : "x-grid-hd-split",
58489     
58490     init: function(grid){
58491         this.grid = grid;
58492                 var cid = this.grid.getGridEl().id;
58493         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58494         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58495         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58496         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58497         },
58498         
58499     getColumnRenderers : function(){
58500         var renderers = [];
58501         var cm = this.grid.colModel;
58502         var colCount = cm.getColumnCount();
58503         for(var i = 0; i < colCount; i++){
58504             renderers[i] = cm.getRenderer(i);
58505         }
58506         return renderers;
58507     },
58508     
58509     getColumnIds : function(){
58510         var ids = [];
58511         var cm = this.grid.colModel;
58512         var colCount = cm.getColumnCount();
58513         for(var i = 0; i < colCount; i++){
58514             ids[i] = cm.getColumnId(i);
58515         }
58516         return ids;
58517     },
58518     
58519     getDataIndexes : function(){
58520         if(!this.indexMap){
58521             this.indexMap = this.buildIndexMap();
58522         }
58523         return this.indexMap.colToData;
58524     },
58525     
58526     getColumnIndexByDataIndex : function(dataIndex){
58527         if(!this.indexMap){
58528             this.indexMap = this.buildIndexMap();
58529         }
58530         return this.indexMap.dataToCol[dataIndex];
58531     },
58532     
58533     /**
58534      * Set a css style for a column dynamically. 
58535      * @param {Number} colIndex The index of the column
58536      * @param {String} name The css property name
58537      * @param {String} value The css value
58538      */
58539     setCSSStyle : function(colIndex, name, value){
58540         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58541         Roo.util.CSS.updateRule(selector, name, value);
58542     },
58543     
58544     generateRules : function(cm){
58545         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58546         Roo.util.CSS.removeStyleSheet(rulesId);
58547         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58548             var cid = cm.getColumnId(i);
58549             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58550                          this.tdSelector, cid, " {\n}\n",
58551                          this.hdSelector, cid, " {\n}\n",
58552                          this.splitSelector, cid, " {\n}\n");
58553         }
58554         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58555     }
58556 });/*
58557  * Based on:
58558  * Ext JS Library 1.1.1
58559  * Copyright(c) 2006-2007, Ext JS, LLC.
58560  *
58561  * Originally Released Under LGPL - original licence link has changed is not relivant.
58562  *
58563  * Fork - LGPL
58564  * <script type="text/javascript">
58565  */
58566
58567 // private
58568 // This is a support class used internally by the Grid components
58569 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58570     this.grid = grid;
58571     this.view = grid.getView();
58572     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58573     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58574     if(hd2){
58575         this.setHandleElId(Roo.id(hd));
58576         this.setOuterHandleElId(Roo.id(hd2));
58577     }
58578     this.scroll = false;
58579 };
58580 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58581     maxDragWidth: 120,
58582     getDragData : function(e){
58583         var t = Roo.lib.Event.getTarget(e);
58584         var h = this.view.findHeaderCell(t);
58585         if(h){
58586             return {ddel: h.firstChild, header:h};
58587         }
58588         return false;
58589     },
58590
58591     onInitDrag : function(e){
58592         this.view.headersDisabled = true;
58593         var clone = this.dragData.ddel.cloneNode(true);
58594         clone.id = Roo.id();
58595         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58596         this.proxy.update(clone);
58597         return true;
58598     },
58599
58600     afterValidDrop : function(){
58601         var v = this.view;
58602         setTimeout(function(){
58603             v.headersDisabled = false;
58604         }, 50);
58605     },
58606
58607     afterInvalidDrop : function(){
58608         var v = this.view;
58609         setTimeout(function(){
58610             v.headersDisabled = false;
58611         }, 50);
58612     }
58613 });
58614 /*
58615  * Based on:
58616  * Ext JS Library 1.1.1
58617  * Copyright(c) 2006-2007, Ext JS, LLC.
58618  *
58619  * Originally Released Under LGPL - original licence link has changed is not relivant.
58620  *
58621  * Fork - LGPL
58622  * <script type="text/javascript">
58623  */
58624 // private
58625 // This is a support class used internally by the Grid components
58626 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58627     this.grid = grid;
58628     this.view = grid.getView();
58629     // split the proxies so they don't interfere with mouse events
58630     this.proxyTop = Roo.DomHelper.append(document.body, {
58631         cls:"col-move-top", html:"&#160;"
58632     }, true);
58633     this.proxyBottom = Roo.DomHelper.append(document.body, {
58634         cls:"col-move-bottom", html:"&#160;"
58635     }, true);
58636     this.proxyTop.hide = this.proxyBottom.hide = function(){
58637         this.setLeftTop(-100,-100);
58638         this.setStyle("visibility", "hidden");
58639     };
58640     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58641     // temporarily disabled
58642     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58643     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58644 };
58645 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58646     proxyOffsets : [-4, -9],
58647     fly: Roo.Element.fly,
58648
58649     getTargetFromEvent : function(e){
58650         var t = Roo.lib.Event.getTarget(e);
58651         var cindex = this.view.findCellIndex(t);
58652         if(cindex !== false){
58653             return this.view.getHeaderCell(cindex);
58654         }
58655         return null;
58656     },
58657
58658     nextVisible : function(h){
58659         var v = this.view, cm = this.grid.colModel;
58660         h = h.nextSibling;
58661         while(h){
58662             if(!cm.isHidden(v.getCellIndex(h))){
58663                 return h;
58664             }
58665             h = h.nextSibling;
58666         }
58667         return null;
58668     },
58669
58670     prevVisible : function(h){
58671         var v = this.view, cm = this.grid.colModel;
58672         h = h.prevSibling;
58673         while(h){
58674             if(!cm.isHidden(v.getCellIndex(h))){
58675                 return h;
58676             }
58677             h = h.prevSibling;
58678         }
58679         return null;
58680     },
58681
58682     positionIndicator : function(h, n, e){
58683         var x = Roo.lib.Event.getPageX(e);
58684         var r = Roo.lib.Dom.getRegion(n.firstChild);
58685         var px, pt, py = r.top + this.proxyOffsets[1];
58686         if((r.right - x) <= (r.right-r.left)/2){
58687             px = r.right+this.view.borderWidth;
58688             pt = "after";
58689         }else{
58690             px = r.left;
58691             pt = "before";
58692         }
58693         var oldIndex = this.view.getCellIndex(h);
58694         var newIndex = this.view.getCellIndex(n);
58695
58696         if(this.grid.colModel.isFixed(newIndex)){
58697             return false;
58698         }
58699
58700         var locked = this.grid.colModel.isLocked(newIndex);
58701
58702         if(pt == "after"){
58703             newIndex++;
58704         }
58705         if(oldIndex < newIndex){
58706             newIndex--;
58707         }
58708         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58709             return false;
58710         }
58711         px +=  this.proxyOffsets[0];
58712         this.proxyTop.setLeftTop(px, py);
58713         this.proxyTop.show();
58714         if(!this.bottomOffset){
58715             this.bottomOffset = this.view.mainHd.getHeight();
58716         }
58717         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
58718         this.proxyBottom.show();
58719         return pt;
58720     },
58721
58722     onNodeEnter : function(n, dd, e, data){
58723         if(data.header != n){
58724             this.positionIndicator(data.header, n, e);
58725         }
58726     },
58727
58728     onNodeOver : function(n, dd, e, data){
58729         var result = false;
58730         if(data.header != n){
58731             result = this.positionIndicator(data.header, n, e);
58732         }
58733         if(!result){
58734             this.proxyTop.hide();
58735             this.proxyBottom.hide();
58736         }
58737         return result ? this.dropAllowed : this.dropNotAllowed;
58738     },
58739
58740     onNodeOut : function(n, dd, e, data){
58741         this.proxyTop.hide();
58742         this.proxyBottom.hide();
58743     },
58744
58745     onNodeDrop : function(n, dd, e, data){
58746         var h = data.header;
58747         if(h != n){
58748             var cm = this.grid.colModel;
58749             var x = Roo.lib.Event.getPageX(e);
58750             var r = Roo.lib.Dom.getRegion(n.firstChild);
58751             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
58752             var oldIndex = this.view.getCellIndex(h);
58753             var newIndex = this.view.getCellIndex(n);
58754             var locked = cm.isLocked(newIndex);
58755             if(pt == "after"){
58756                 newIndex++;
58757             }
58758             if(oldIndex < newIndex){
58759                 newIndex--;
58760             }
58761             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
58762                 return false;
58763             }
58764             cm.setLocked(oldIndex, locked, true);
58765             cm.moveColumn(oldIndex, newIndex);
58766             this.grid.fireEvent("columnmove", oldIndex, newIndex);
58767             return true;
58768         }
58769         return false;
58770     }
58771 });
58772 /*
58773  * Based on:
58774  * Ext JS Library 1.1.1
58775  * Copyright(c) 2006-2007, Ext JS, LLC.
58776  *
58777  * Originally Released Under LGPL - original licence link has changed is not relivant.
58778  *
58779  * Fork - LGPL
58780  * <script type="text/javascript">
58781  */
58782   
58783 /**
58784  * @class Roo.grid.GridView
58785  * @extends Roo.util.Observable
58786  *
58787  * @constructor
58788  * @param {Object} config
58789  */
58790 Roo.grid.GridView = function(config){
58791     Roo.grid.GridView.superclass.constructor.call(this);
58792     this.el = null;
58793
58794     Roo.apply(this, config);
58795 };
58796
58797 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
58798
58799     unselectable :  'unselectable="on"',
58800     unselectableCls :  'x-unselectable',
58801     
58802     
58803     rowClass : "x-grid-row",
58804
58805     cellClass : "x-grid-col",
58806
58807     tdClass : "x-grid-td",
58808
58809     hdClass : "x-grid-hd",
58810
58811     splitClass : "x-grid-split",
58812
58813     sortClasses : ["sort-asc", "sort-desc"],
58814
58815     enableMoveAnim : false,
58816
58817     hlColor: "C3DAF9",
58818
58819     dh : Roo.DomHelper,
58820
58821     fly : Roo.Element.fly,
58822
58823     css : Roo.util.CSS,
58824
58825     borderWidth: 1,
58826
58827     splitOffset: 3,
58828
58829     scrollIncrement : 22,
58830
58831     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
58832
58833     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
58834
58835     bind : function(ds, cm){
58836         if(this.ds){
58837             this.ds.un("load", this.onLoad, this);
58838             this.ds.un("datachanged", this.onDataChange, this);
58839             this.ds.un("add", this.onAdd, this);
58840             this.ds.un("remove", this.onRemove, this);
58841             this.ds.un("update", this.onUpdate, this);
58842             this.ds.un("clear", this.onClear, this);
58843         }
58844         if(ds){
58845             ds.on("load", this.onLoad, this);
58846             ds.on("datachanged", this.onDataChange, this);
58847             ds.on("add", this.onAdd, this);
58848             ds.on("remove", this.onRemove, this);
58849             ds.on("update", this.onUpdate, this);
58850             ds.on("clear", this.onClear, this);
58851         }
58852         this.ds = ds;
58853
58854         if(this.cm){
58855             this.cm.un("widthchange", this.onColWidthChange, this);
58856             this.cm.un("headerchange", this.onHeaderChange, this);
58857             this.cm.un("hiddenchange", this.onHiddenChange, this);
58858             this.cm.un("columnmoved", this.onColumnMove, this);
58859             this.cm.un("columnlockchange", this.onColumnLock, this);
58860         }
58861         if(cm){
58862             this.generateRules(cm);
58863             cm.on("widthchange", this.onColWidthChange, this);
58864             cm.on("headerchange", this.onHeaderChange, this);
58865             cm.on("hiddenchange", this.onHiddenChange, this);
58866             cm.on("columnmoved", this.onColumnMove, this);
58867             cm.on("columnlockchange", this.onColumnLock, this);
58868         }
58869         this.cm = cm;
58870     },
58871
58872     init: function(grid){
58873         Roo.grid.GridView.superclass.init.call(this, grid);
58874
58875         this.bind(grid.dataSource, grid.colModel);
58876
58877         grid.on("headerclick", this.handleHeaderClick, this);
58878
58879         if(grid.trackMouseOver){
58880             grid.on("mouseover", this.onRowOver, this);
58881             grid.on("mouseout", this.onRowOut, this);
58882         }
58883         grid.cancelTextSelection = function(){};
58884         this.gridId = grid.id;
58885
58886         var tpls = this.templates || {};
58887
58888         if(!tpls.master){
58889             tpls.master = new Roo.Template(
58890                '<div class="x-grid" hidefocus="true">',
58891                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
58892                   '<div class="x-grid-topbar"></div>',
58893                   '<div class="x-grid-scroller"><div></div></div>',
58894                   '<div class="x-grid-locked">',
58895                       '<div class="x-grid-header">{lockedHeader}</div>',
58896                       '<div class="x-grid-body">{lockedBody}</div>',
58897                   "</div>",
58898                   '<div class="x-grid-viewport">',
58899                       '<div class="x-grid-header">{header}</div>',
58900                       '<div class="x-grid-body">{body}</div>',
58901                   "</div>",
58902                   '<div class="x-grid-bottombar"></div>',
58903                  
58904                   '<div class="x-grid-resize-proxy">&#160;</div>',
58905                "</div>"
58906             );
58907             tpls.master.disableformats = true;
58908         }
58909
58910         if(!tpls.header){
58911             tpls.header = new Roo.Template(
58912                '<table border="0" cellspacing="0" cellpadding="0">',
58913                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
58914                "</table>{splits}"
58915             );
58916             tpls.header.disableformats = true;
58917         }
58918         tpls.header.compile();
58919
58920         if(!tpls.hcell){
58921             tpls.hcell = new Roo.Template(
58922                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
58923                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
58924                 "</div></td>"
58925              );
58926              tpls.hcell.disableFormats = true;
58927         }
58928         tpls.hcell.compile();
58929
58930         if(!tpls.hsplit){
58931             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
58932                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
58933             tpls.hsplit.disableFormats = true;
58934         }
58935         tpls.hsplit.compile();
58936
58937         if(!tpls.body){
58938             tpls.body = new Roo.Template(
58939                '<table border="0" cellspacing="0" cellpadding="0">',
58940                "<tbody>{rows}</tbody>",
58941                "</table>"
58942             );
58943             tpls.body.disableFormats = true;
58944         }
58945         tpls.body.compile();
58946
58947         if(!tpls.row){
58948             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
58949             tpls.row.disableFormats = true;
58950         }
58951         tpls.row.compile();
58952
58953         if(!tpls.cell){
58954             tpls.cell = new Roo.Template(
58955                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
58956                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
58957                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
58958                 "</td>"
58959             );
58960             tpls.cell.disableFormats = true;
58961         }
58962         tpls.cell.compile();
58963
58964         this.templates = tpls;
58965     },
58966
58967     // remap these for backwards compat
58968     onColWidthChange : function(){
58969         this.updateColumns.apply(this, arguments);
58970     },
58971     onHeaderChange : function(){
58972         this.updateHeaders.apply(this, arguments);
58973     }, 
58974     onHiddenChange : function(){
58975         this.handleHiddenChange.apply(this, arguments);
58976     },
58977     onColumnMove : function(){
58978         this.handleColumnMove.apply(this, arguments);
58979     },
58980     onColumnLock : function(){
58981         this.handleLockChange.apply(this, arguments);
58982     },
58983
58984     onDataChange : function(){
58985         this.refresh();
58986         this.updateHeaderSortState();
58987     },
58988
58989     onClear : function(){
58990         this.refresh();
58991     },
58992
58993     onUpdate : function(ds, record){
58994         this.refreshRow(record);
58995     },
58996
58997     refreshRow : function(record){
58998         var ds = this.ds, index;
58999         if(typeof record == 'number'){
59000             index = record;
59001             record = ds.getAt(index);
59002         }else{
59003             index = ds.indexOf(record);
59004         }
59005         this.insertRows(ds, index, index, true);
59006         this.onRemove(ds, record, index+1, true);
59007         this.syncRowHeights(index, index);
59008         this.layout();
59009         this.fireEvent("rowupdated", this, index, record);
59010     },
59011
59012     onAdd : function(ds, records, index){
59013         this.insertRows(ds, index, index + (records.length-1));
59014     },
59015
59016     onRemove : function(ds, record, index, isUpdate){
59017         if(isUpdate !== true){
59018             this.fireEvent("beforerowremoved", this, index, record);
59019         }
59020         var bt = this.getBodyTable(), lt = this.getLockedTable();
59021         if(bt.rows[index]){
59022             bt.firstChild.removeChild(bt.rows[index]);
59023         }
59024         if(lt.rows[index]){
59025             lt.firstChild.removeChild(lt.rows[index]);
59026         }
59027         if(isUpdate !== true){
59028             this.stripeRows(index);
59029             this.syncRowHeights(index, index);
59030             this.layout();
59031             this.fireEvent("rowremoved", this, index, record);
59032         }
59033     },
59034
59035     onLoad : function(){
59036         this.scrollToTop();
59037     },
59038
59039     /**
59040      * Scrolls the grid to the top
59041      */
59042     scrollToTop : function(){
59043         if(this.scroller){
59044             this.scroller.dom.scrollTop = 0;
59045             this.syncScroll();
59046         }
59047     },
59048
59049     /**
59050      * Gets a panel in the header of the grid that can be used for toolbars etc.
59051      * After modifying the contents of this panel a call to grid.autoSize() may be
59052      * required to register any changes in size.
59053      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59054      * @return Roo.Element
59055      */
59056     getHeaderPanel : function(doShow){
59057         if(doShow){
59058             this.headerPanel.show();
59059         }
59060         return this.headerPanel;
59061     },
59062
59063     /**
59064      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59065      * After modifying the contents of this panel a call to grid.autoSize() may be
59066      * required to register any changes in size.
59067      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59068      * @return Roo.Element
59069      */
59070     getFooterPanel : function(doShow){
59071         if(doShow){
59072             this.footerPanel.show();
59073         }
59074         return this.footerPanel;
59075     },
59076
59077     initElements : function(){
59078         var E = Roo.Element;
59079         var el = this.grid.getGridEl().dom.firstChild;
59080         var cs = el.childNodes;
59081
59082         this.el = new E(el);
59083         
59084          this.focusEl = new E(el.firstChild);
59085         this.focusEl.swallowEvent("click", true);
59086         
59087         this.headerPanel = new E(cs[1]);
59088         this.headerPanel.enableDisplayMode("block");
59089
59090         this.scroller = new E(cs[2]);
59091         this.scrollSizer = new E(this.scroller.dom.firstChild);
59092
59093         this.lockedWrap = new E(cs[3]);
59094         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59095         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59096
59097         this.mainWrap = new E(cs[4]);
59098         this.mainHd = new E(this.mainWrap.dom.firstChild);
59099         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59100
59101         this.footerPanel = new E(cs[5]);
59102         this.footerPanel.enableDisplayMode("block");
59103
59104         this.resizeProxy = new E(cs[6]);
59105
59106         this.headerSelector = String.format(
59107            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59108            this.lockedHd.id, this.mainHd.id
59109         );
59110
59111         this.splitterSelector = String.format(
59112            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59113            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59114         );
59115     },
59116     idToCssName : function(s)
59117     {
59118         return s.replace(/[^a-z0-9]+/ig, '-');
59119     },
59120
59121     getHeaderCell : function(index){
59122         return Roo.DomQuery.select(this.headerSelector)[index];
59123     },
59124
59125     getHeaderCellMeasure : function(index){
59126         return this.getHeaderCell(index).firstChild;
59127     },
59128
59129     getHeaderCellText : function(index){
59130         return this.getHeaderCell(index).firstChild.firstChild;
59131     },
59132
59133     getLockedTable : function(){
59134         return this.lockedBody.dom.firstChild;
59135     },
59136
59137     getBodyTable : function(){
59138         return this.mainBody.dom.firstChild;
59139     },
59140
59141     getLockedRow : function(index){
59142         return this.getLockedTable().rows[index];
59143     },
59144
59145     getRow : function(index){
59146         return this.getBodyTable().rows[index];
59147     },
59148
59149     getRowComposite : function(index){
59150         if(!this.rowEl){
59151             this.rowEl = new Roo.CompositeElementLite();
59152         }
59153         var els = [], lrow, mrow;
59154         if(lrow = this.getLockedRow(index)){
59155             els.push(lrow);
59156         }
59157         if(mrow = this.getRow(index)){
59158             els.push(mrow);
59159         }
59160         this.rowEl.elements = els;
59161         return this.rowEl;
59162     },
59163     /**
59164      * Gets the 'td' of the cell
59165      * 
59166      * @param {Integer} rowIndex row to select
59167      * @param {Integer} colIndex column to select
59168      * 
59169      * @return {Object} 
59170      */
59171     getCell : function(rowIndex, colIndex){
59172         var locked = this.cm.getLockedCount();
59173         var source;
59174         if(colIndex < locked){
59175             source = this.lockedBody.dom.firstChild;
59176         }else{
59177             source = this.mainBody.dom.firstChild;
59178             colIndex -= locked;
59179         }
59180         return source.rows[rowIndex].childNodes[colIndex];
59181     },
59182
59183     getCellText : function(rowIndex, colIndex){
59184         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59185     },
59186
59187     getCellBox : function(cell){
59188         var b = this.fly(cell).getBox();
59189         if(Roo.isOpera){ // opera fails to report the Y
59190             b.y = cell.offsetTop + this.mainBody.getY();
59191         }
59192         return b;
59193     },
59194
59195     getCellIndex : function(cell){
59196         var id = String(cell.className).match(this.cellRE);
59197         if(id){
59198             return parseInt(id[1], 10);
59199         }
59200         return 0;
59201     },
59202
59203     findHeaderIndex : function(n){
59204         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59205         return r ? this.getCellIndex(r) : false;
59206     },
59207
59208     findHeaderCell : function(n){
59209         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59210         return r ? r : false;
59211     },
59212
59213     findRowIndex : function(n){
59214         if(!n){
59215             return false;
59216         }
59217         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59218         return r ? r.rowIndex : false;
59219     },
59220
59221     findCellIndex : function(node){
59222         var stop = this.el.dom;
59223         while(node && node != stop){
59224             if(this.findRE.test(node.className)){
59225                 return this.getCellIndex(node);
59226             }
59227             node = node.parentNode;
59228         }
59229         return false;
59230     },
59231
59232     getColumnId : function(index){
59233         return this.cm.getColumnId(index);
59234     },
59235
59236     getSplitters : function()
59237     {
59238         if(this.splitterSelector){
59239            return Roo.DomQuery.select(this.splitterSelector);
59240         }else{
59241             return null;
59242       }
59243     },
59244
59245     getSplitter : function(index){
59246         return this.getSplitters()[index];
59247     },
59248
59249     onRowOver : function(e, t){
59250         var row;
59251         if((row = this.findRowIndex(t)) !== false){
59252             this.getRowComposite(row).addClass("x-grid-row-over");
59253         }
59254     },
59255
59256     onRowOut : function(e, t){
59257         var row;
59258         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59259             this.getRowComposite(row).removeClass("x-grid-row-over");
59260         }
59261     },
59262
59263     renderHeaders : function(){
59264         var cm = this.cm;
59265         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59266         var cb = [], lb = [], sb = [], lsb = [], p = {};
59267         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59268             p.cellId = "x-grid-hd-0-" + i;
59269             p.splitId = "x-grid-csplit-0-" + i;
59270             p.id = cm.getColumnId(i);
59271             p.value = cm.getColumnHeader(i) || "";
59272             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59273             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59274             if(!cm.isLocked(i)){
59275                 cb[cb.length] = ct.apply(p);
59276                 sb[sb.length] = st.apply(p);
59277             }else{
59278                 lb[lb.length] = ct.apply(p);
59279                 lsb[lsb.length] = st.apply(p);
59280             }
59281         }
59282         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59283                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59284     },
59285
59286     updateHeaders : function(){
59287         var html = this.renderHeaders();
59288         this.lockedHd.update(html[0]);
59289         this.mainHd.update(html[1]);
59290     },
59291
59292     /**
59293      * Focuses the specified row.
59294      * @param {Number} row The row index
59295      */
59296     focusRow : function(row)
59297     {
59298         //Roo.log('GridView.focusRow');
59299         var x = this.scroller.dom.scrollLeft;
59300         this.focusCell(row, 0, false);
59301         this.scroller.dom.scrollLeft = x;
59302     },
59303
59304     /**
59305      * Focuses the specified cell.
59306      * @param {Number} row The row index
59307      * @param {Number} col The column index
59308      * @param {Boolean} hscroll false to disable horizontal scrolling
59309      */
59310     focusCell : function(row, col, hscroll)
59311     {
59312         //Roo.log('GridView.focusCell');
59313         var el = this.ensureVisible(row, col, hscroll);
59314         this.focusEl.alignTo(el, "tl-tl");
59315         if(Roo.isGecko){
59316             this.focusEl.focus();
59317         }else{
59318             this.focusEl.focus.defer(1, this.focusEl);
59319         }
59320     },
59321
59322     /**
59323      * Scrolls the specified cell into view
59324      * @param {Number} row The row index
59325      * @param {Number} col The column index
59326      * @param {Boolean} hscroll false to disable horizontal scrolling
59327      */
59328     ensureVisible : function(row, col, hscroll)
59329     {
59330         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59331         //return null; //disable for testing.
59332         if(typeof row != "number"){
59333             row = row.rowIndex;
59334         }
59335         if(row < 0 && row >= this.ds.getCount()){
59336             return  null;
59337         }
59338         col = (col !== undefined ? col : 0);
59339         var cm = this.grid.colModel;
59340         while(cm.isHidden(col)){
59341             col++;
59342         }
59343
59344         var el = this.getCell(row, col);
59345         if(!el){
59346             return null;
59347         }
59348         var c = this.scroller.dom;
59349
59350         var ctop = parseInt(el.offsetTop, 10);
59351         var cleft = parseInt(el.offsetLeft, 10);
59352         var cbot = ctop + el.offsetHeight;
59353         var cright = cleft + el.offsetWidth;
59354         
59355         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59356         var stop = parseInt(c.scrollTop, 10);
59357         var sleft = parseInt(c.scrollLeft, 10);
59358         var sbot = stop + ch;
59359         var sright = sleft + c.clientWidth;
59360         /*
59361         Roo.log('GridView.ensureVisible:' +
59362                 ' ctop:' + ctop +
59363                 ' c.clientHeight:' + c.clientHeight +
59364                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59365                 ' stop:' + stop +
59366                 ' cbot:' + cbot +
59367                 ' sbot:' + sbot +
59368                 ' ch:' + ch  
59369                 );
59370         */
59371         if(ctop < stop){
59372             c.scrollTop = ctop;
59373             //Roo.log("set scrolltop to ctop DISABLE?");
59374         }else if(cbot > sbot){
59375             //Roo.log("set scrolltop to cbot-ch");
59376             c.scrollTop = cbot-ch;
59377         }
59378         
59379         if(hscroll !== false){
59380             if(cleft < sleft){
59381                 c.scrollLeft = cleft;
59382             }else if(cright > sright){
59383                 c.scrollLeft = cright-c.clientWidth;
59384             }
59385         }
59386          
59387         return el;
59388     },
59389
59390     updateColumns : function(){
59391         this.grid.stopEditing();
59392         var cm = this.grid.colModel, colIds = this.getColumnIds();
59393         //var totalWidth = cm.getTotalWidth();
59394         var pos = 0;
59395         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59396             //if(cm.isHidden(i)) continue;
59397             var w = cm.getColumnWidth(i);
59398             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59399             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59400         }
59401         this.updateSplitters();
59402     },
59403
59404     generateRules : function(cm){
59405         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59406         Roo.util.CSS.removeStyleSheet(rulesId);
59407         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59408             var cid = cm.getColumnId(i);
59409             var align = '';
59410             if(cm.config[i].align){
59411                 align = 'text-align:'+cm.config[i].align+';';
59412             }
59413             var hidden = '';
59414             if(cm.isHidden(i)){
59415                 hidden = 'display:none;';
59416             }
59417             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59418             ruleBuf.push(
59419                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59420                     this.hdSelector, cid, " {\n", align, width, "}\n",
59421                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59422                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59423         }
59424         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59425     },
59426
59427     updateSplitters : function(){
59428         var cm = this.cm, s = this.getSplitters();
59429         if(s){ // splitters not created yet
59430             var pos = 0, locked = true;
59431             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59432                 if(cm.isHidden(i)) {
59433                     continue;
59434                 }
59435                 var w = cm.getColumnWidth(i); // make sure it's a number
59436                 if(!cm.isLocked(i) && locked){
59437                     pos = 0;
59438                     locked = false;
59439                 }
59440                 pos += w;
59441                 s[i].style.left = (pos-this.splitOffset) + "px";
59442             }
59443         }
59444     },
59445
59446     handleHiddenChange : function(colModel, colIndex, hidden){
59447         if(hidden){
59448             this.hideColumn(colIndex);
59449         }else{
59450             this.unhideColumn(colIndex);
59451         }
59452     },
59453
59454     hideColumn : function(colIndex){
59455         var cid = this.getColumnId(colIndex);
59456         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59457         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59458         if(Roo.isSafari){
59459             this.updateHeaders();
59460         }
59461         this.updateSplitters();
59462         this.layout();
59463     },
59464
59465     unhideColumn : function(colIndex){
59466         var cid = this.getColumnId(colIndex);
59467         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59468         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59469
59470         if(Roo.isSafari){
59471             this.updateHeaders();
59472         }
59473         this.updateSplitters();
59474         this.layout();
59475     },
59476
59477     insertRows : function(dm, firstRow, lastRow, isUpdate){
59478         if(firstRow == 0 && lastRow == dm.getCount()-1){
59479             this.refresh();
59480         }else{
59481             if(!isUpdate){
59482                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59483             }
59484             var s = this.getScrollState();
59485             var markup = this.renderRows(firstRow, lastRow);
59486             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59487             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59488             this.restoreScroll(s);
59489             if(!isUpdate){
59490                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59491                 this.syncRowHeights(firstRow, lastRow);
59492                 this.stripeRows(firstRow);
59493                 this.layout();
59494             }
59495         }
59496     },
59497
59498     bufferRows : function(markup, target, index){
59499         var before = null, trows = target.rows, tbody = target.tBodies[0];
59500         if(index < trows.length){
59501             before = trows[index];
59502         }
59503         var b = document.createElement("div");
59504         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59505         var rows = b.firstChild.rows;
59506         for(var i = 0, len = rows.length; i < len; i++){
59507             if(before){
59508                 tbody.insertBefore(rows[0], before);
59509             }else{
59510                 tbody.appendChild(rows[0]);
59511             }
59512         }
59513         b.innerHTML = "";
59514         b = null;
59515     },
59516
59517     deleteRows : function(dm, firstRow, lastRow){
59518         if(dm.getRowCount()<1){
59519             this.fireEvent("beforerefresh", this);
59520             this.mainBody.update("");
59521             this.lockedBody.update("");
59522             this.fireEvent("refresh", this);
59523         }else{
59524             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59525             var bt = this.getBodyTable();
59526             var tbody = bt.firstChild;
59527             var rows = bt.rows;
59528             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59529                 tbody.removeChild(rows[firstRow]);
59530             }
59531             this.stripeRows(firstRow);
59532             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59533         }
59534     },
59535
59536     updateRows : function(dataSource, firstRow, lastRow){
59537         var s = this.getScrollState();
59538         this.refresh();
59539         this.restoreScroll(s);
59540     },
59541
59542     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59543         if(!noRefresh){
59544            this.refresh();
59545         }
59546         this.updateHeaderSortState();
59547     },
59548
59549     getScrollState : function(){
59550         
59551         var sb = this.scroller.dom;
59552         return {left: sb.scrollLeft, top: sb.scrollTop};
59553     },
59554
59555     stripeRows : function(startRow){
59556         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59557             return;
59558         }
59559         startRow = startRow || 0;
59560         var rows = this.getBodyTable().rows;
59561         var lrows = this.getLockedTable().rows;
59562         var cls = ' x-grid-row-alt ';
59563         for(var i = startRow, len = rows.length; i < len; i++){
59564             var row = rows[i], lrow = lrows[i];
59565             var isAlt = ((i+1) % 2 == 0);
59566             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59567             if(isAlt == hasAlt){
59568                 continue;
59569             }
59570             if(isAlt){
59571                 row.className += " x-grid-row-alt";
59572             }else{
59573                 row.className = row.className.replace("x-grid-row-alt", "");
59574             }
59575             if(lrow){
59576                 lrow.className = row.className;
59577             }
59578         }
59579     },
59580
59581     restoreScroll : function(state){
59582         //Roo.log('GridView.restoreScroll');
59583         var sb = this.scroller.dom;
59584         sb.scrollLeft = state.left;
59585         sb.scrollTop = state.top;
59586         this.syncScroll();
59587     },
59588
59589     syncScroll : function(){
59590         //Roo.log('GridView.syncScroll');
59591         var sb = this.scroller.dom;
59592         var sh = this.mainHd.dom;
59593         var bs = this.mainBody.dom;
59594         var lv = this.lockedBody.dom;
59595         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59596         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59597     },
59598
59599     handleScroll : function(e){
59600         this.syncScroll();
59601         var sb = this.scroller.dom;
59602         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59603         e.stopEvent();
59604     },
59605
59606     handleWheel : function(e){
59607         var d = e.getWheelDelta();
59608         this.scroller.dom.scrollTop -= d*22;
59609         // set this here to prevent jumpy scrolling on large tables
59610         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59611         e.stopEvent();
59612     },
59613
59614     renderRows : function(startRow, endRow){
59615         // pull in all the crap needed to render rows
59616         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59617         var colCount = cm.getColumnCount();
59618
59619         if(ds.getCount() < 1){
59620             return ["", ""];
59621         }
59622
59623         // build a map for all the columns
59624         var cs = [];
59625         for(var i = 0; i < colCount; i++){
59626             var name = cm.getDataIndex(i);
59627             cs[i] = {
59628                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59629                 renderer : cm.getRenderer(i),
59630                 id : cm.getColumnId(i),
59631                 locked : cm.isLocked(i),
59632                 has_editor : cm.isCellEditable(i)
59633             };
59634         }
59635
59636         startRow = startRow || 0;
59637         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59638
59639         // records to render
59640         var rs = ds.getRange(startRow, endRow);
59641
59642         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59643     },
59644
59645     // As much as I hate to duplicate code, this was branched because FireFox really hates
59646     // [].join("") on strings. The performance difference was substantial enough to
59647     // branch this function
59648     doRender : Roo.isGecko ?
59649             function(cs, rs, ds, startRow, colCount, stripe){
59650                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59651                 // buffers
59652                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59653                 
59654                 var hasListener = this.grid.hasListener('rowclass');
59655                 var rowcfg = {};
59656                 for(var j = 0, len = rs.length; j < len; j++){
59657                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59658                     for(var i = 0; i < colCount; i++){
59659                         c = cs[i];
59660                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59661                         p.id = c.id;
59662                         p.css = p.attr = "";
59663                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59664                         if(p.value == undefined || p.value === "") {
59665                             p.value = "&#160;";
59666                         }
59667                         if(c.has_editor){
59668                             p.css += ' x-grid-editable-cell';
59669                         }
59670                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59671                             p.css +=  ' x-grid-dirty-cell';
59672                         }
59673                         var markup = ct.apply(p);
59674                         if(!c.locked){
59675                             cb+= markup;
59676                         }else{
59677                             lcb+= markup;
59678                         }
59679                     }
59680                     var alt = [];
59681                     if(stripe && ((rowIndex+1) % 2 == 0)){
59682                         alt.push("x-grid-row-alt")
59683                     }
59684                     if(r.dirty){
59685                         alt.push(  " x-grid-dirty-row");
59686                     }
59687                     rp.cells = lcb;
59688                     if(this.getRowClass){
59689                         alt.push(this.getRowClass(r, rowIndex));
59690                     }
59691                     if (hasListener) {
59692                         rowcfg = {
59693                              
59694                             record: r,
59695                             rowIndex : rowIndex,
59696                             rowClass : ''
59697                         };
59698                         this.grid.fireEvent('rowclass', this, rowcfg);
59699                         alt.push(rowcfg.rowClass);
59700                     }
59701                     rp.alt = alt.join(" ");
59702                     lbuf+= rt.apply(rp);
59703                     rp.cells = cb;
59704                     buf+=  rt.apply(rp);
59705                 }
59706                 return [lbuf, buf];
59707             } :
59708             function(cs, rs, ds, startRow, colCount, stripe){
59709                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59710                 // buffers
59711                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59712                 var hasListener = this.grid.hasListener('rowclass');
59713  
59714                 var rowcfg = {};
59715                 for(var j = 0, len = rs.length; j < len; j++){
59716                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
59717                     for(var i = 0; i < colCount; i++){
59718                         c = cs[i];
59719                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59720                         p.id = c.id;
59721                         p.css = p.attr = "";
59722                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59723                         if(p.value == undefined || p.value === "") {
59724                             p.value = "&#160;";
59725                         }
59726                         //Roo.log(c);
59727                          if(c.has_editor){
59728                             p.css += ' x-grid-editable-cell';
59729                         }
59730                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
59731                             p.css += ' x-grid-dirty-cell' 
59732                         }
59733                         
59734                         var markup = ct.apply(p);
59735                         if(!c.locked){
59736                             cb[cb.length] = markup;
59737                         }else{
59738                             lcb[lcb.length] = markup;
59739                         }
59740                     }
59741                     var alt = [];
59742                     if(stripe && ((rowIndex+1) % 2 == 0)){
59743                         alt.push( "x-grid-row-alt");
59744                     }
59745                     if(r.dirty){
59746                         alt.push(" x-grid-dirty-row");
59747                     }
59748                     rp.cells = lcb;
59749                     if(this.getRowClass){
59750                         alt.push( this.getRowClass(r, rowIndex));
59751                     }
59752                     if (hasListener) {
59753                         rowcfg = {
59754                              
59755                             record: r,
59756                             rowIndex : rowIndex,
59757                             rowClass : ''
59758                         };
59759                         this.grid.fireEvent('rowclass', this, rowcfg);
59760                         alt.push(rowcfg.rowClass);
59761                     }
59762                     
59763                     rp.alt = alt.join(" ");
59764                     rp.cells = lcb.join("");
59765                     lbuf[lbuf.length] = rt.apply(rp);
59766                     rp.cells = cb.join("");
59767                     buf[buf.length] =  rt.apply(rp);
59768                 }
59769                 return [lbuf.join(""), buf.join("")];
59770             },
59771
59772     renderBody : function(){
59773         var markup = this.renderRows();
59774         var bt = this.templates.body;
59775         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
59776     },
59777
59778     /**
59779      * Refreshes the grid
59780      * @param {Boolean} headersToo
59781      */
59782     refresh : function(headersToo){
59783         this.fireEvent("beforerefresh", this);
59784         this.grid.stopEditing();
59785         var result = this.renderBody();
59786         this.lockedBody.update(result[0]);
59787         this.mainBody.update(result[1]);
59788         if(headersToo === true){
59789             this.updateHeaders();
59790             this.updateColumns();
59791             this.updateSplitters();
59792             this.updateHeaderSortState();
59793         }
59794         this.syncRowHeights();
59795         this.layout();
59796         this.fireEvent("refresh", this);
59797     },
59798
59799     handleColumnMove : function(cm, oldIndex, newIndex){
59800         this.indexMap = null;
59801         var s = this.getScrollState();
59802         this.refresh(true);
59803         this.restoreScroll(s);
59804         this.afterMove(newIndex);
59805     },
59806
59807     afterMove : function(colIndex){
59808         if(this.enableMoveAnim && Roo.enableFx){
59809             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
59810         }
59811         // if multisort - fix sortOrder, and reload..
59812         if (this.grid.dataSource.multiSort) {
59813             // the we can call sort again..
59814             var dm = this.grid.dataSource;
59815             var cm = this.grid.colModel;
59816             var so = [];
59817             for(var i = 0; i < cm.config.length; i++ ) {
59818                 
59819                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
59820                     continue; // dont' bother, it's not in sort list or being set.
59821                 }
59822                 
59823                 so.push(cm.config[i].dataIndex);
59824             };
59825             dm.sortOrder = so;
59826             dm.load(dm.lastOptions);
59827             
59828             
59829         }
59830         
59831     },
59832
59833     updateCell : function(dm, rowIndex, dataIndex){
59834         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
59835         if(typeof colIndex == "undefined"){ // not present in grid
59836             return;
59837         }
59838         var cm = this.grid.colModel;
59839         var cell = this.getCell(rowIndex, colIndex);
59840         var cellText = this.getCellText(rowIndex, colIndex);
59841
59842         var p = {
59843             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
59844             id : cm.getColumnId(colIndex),
59845             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
59846         };
59847         var renderer = cm.getRenderer(colIndex);
59848         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
59849         if(typeof val == "undefined" || val === "") {
59850             val = "&#160;";
59851         }
59852         cellText.innerHTML = val;
59853         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
59854         this.syncRowHeights(rowIndex, rowIndex);
59855     },
59856
59857     calcColumnWidth : function(colIndex, maxRowsToMeasure){
59858         var maxWidth = 0;
59859         if(this.grid.autoSizeHeaders){
59860             var h = this.getHeaderCellMeasure(colIndex);
59861             maxWidth = Math.max(maxWidth, h.scrollWidth);
59862         }
59863         var tb, index;
59864         if(this.cm.isLocked(colIndex)){
59865             tb = this.getLockedTable();
59866             index = colIndex;
59867         }else{
59868             tb = this.getBodyTable();
59869             index = colIndex - this.cm.getLockedCount();
59870         }
59871         if(tb && tb.rows){
59872             var rows = tb.rows;
59873             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
59874             for(var i = 0; i < stopIndex; i++){
59875                 var cell = rows[i].childNodes[index].firstChild;
59876                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
59877             }
59878         }
59879         return maxWidth + /*margin for error in IE*/ 5;
59880     },
59881     /**
59882      * Autofit a column to its content.
59883      * @param {Number} colIndex
59884      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
59885      */
59886      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
59887          if(this.cm.isHidden(colIndex)){
59888              return; // can't calc a hidden column
59889          }
59890         if(forceMinSize){
59891             var cid = this.cm.getColumnId(colIndex);
59892             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
59893            if(this.grid.autoSizeHeaders){
59894                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
59895            }
59896         }
59897         var newWidth = this.calcColumnWidth(colIndex);
59898         this.cm.setColumnWidth(colIndex,
59899             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
59900         if(!suppressEvent){
59901             this.grid.fireEvent("columnresize", colIndex, newWidth);
59902         }
59903     },
59904
59905     /**
59906      * Autofits all columns to their content and then expands to fit any extra space in the grid
59907      */
59908      autoSizeColumns : function(){
59909         var cm = this.grid.colModel;
59910         var colCount = cm.getColumnCount();
59911         for(var i = 0; i < colCount; i++){
59912             this.autoSizeColumn(i, true, true);
59913         }
59914         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
59915             this.fitColumns();
59916         }else{
59917             this.updateColumns();
59918             this.layout();
59919         }
59920     },
59921
59922     /**
59923      * Autofits all columns to the grid's width proportionate with their current size
59924      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
59925      */
59926     fitColumns : function(reserveScrollSpace){
59927         var cm = this.grid.colModel;
59928         var colCount = cm.getColumnCount();
59929         var cols = [];
59930         var width = 0;
59931         var i, w;
59932         for (i = 0; i < colCount; i++){
59933             if(!cm.isHidden(i) && !cm.isFixed(i)){
59934                 w = cm.getColumnWidth(i);
59935                 cols.push(i);
59936                 cols.push(w);
59937                 width += w;
59938             }
59939         }
59940         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
59941         if(reserveScrollSpace){
59942             avail -= 17;
59943         }
59944         var frac = (avail - cm.getTotalWidth())/width;
59945         while (cols.length){
59946             w = cols.pop();
59947             i = cols.pop();
59948             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
59949         }
59950         this.updateColumns();
59951         this.layout();
59952     },
59953
59954     onRowSelect : function(rowIndex){
59955         var row = this.getRowComposite(rowIndex);
59956         row.addClass("x-grid-row-selected");
59957     },
59958
59959     onRowDeselect : function(rowIndex){
59960         var row = this.getRowComposite(rowIndex);
59961         row.removeClass("x-grid-row-selected");
59962     },
59963
59964     onCellSelect : function(row, col){
59965         var cell = this.getCell(row, col);
59966         if(cell){
59967             Roo.fly(cell).addClass("x-grid-cell-selected");
59968         }
59969     },
59970
59971     onCellDeselect : function(row, col){
59972         var cell = this.getCell(row, col);
59973         if(cell){
59974             Roo.fly(cell).removeClass("x-grid-cell-selected");
59975         }
59976     },
59977
59978     updateHeaderSortState : function(){
59979         
59980         // sort state can be single { field: xxx, direction : yyy}
59981         // or   { xxx=>ASC , yyy : DESC ..... }
59982         
59983         var mstate = {};
59984         if (!this.ds.multiSort) { 
59985             var state = this.ds.getSortState();
59986             if(!state){
59987                 return;
59988             }
59989             mstate[state.field] = state.direction;
59990             // FIXME... - this is not used here.. but might be elsewhere..
59991             this.sortState = state;
59992             
59993         } else {
59994             mstate = this.ds.sortToggle;
59995         }
59996         //remove existing sort classes..
59997         
59998         var sc = this.sortClasses;
59999         var hds = this.el.select(this.headerSelector).removeClass(sc);
60000         
60001         for(var f in mstate) {
60002         
60003             var sortColumn = this.cm.findColumnIndex(f);
60004             
60005             if(sortColumn != -1){
60006                 var sortDir = mstate[f];        
60007                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60008             }
60009         }
60010         
60011          
60012         
60013     },
60014
60015
60016     handleHeaderClick : function(g, index,e){
60017         
60018         Roo.log("header click");
60019         
60020         if (Roo.isTouch) {
60021             // touch events on header are handled by context
60022             this.handleHdCtx(g,index,e);
60023             return;
60024         }
60025         
60026         
60027         if(this.headersDisabled){
60028             return;
60029         }
60030         var dm = g.dataSource, cm = g.colModel;
60031         if(!cm.isSortable(index)){
60032             return;
60033         }
60034         g.stopEditing();
60035         
60036         if (dm.multiSort) {
60037             // update the sortOrder
60038             var so = [];
60039             for(var i = 0; i < cm.config.length; i++ ) {
60040                 
60041                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60042                     continue; // dont' bother, it's not in sort list or being set.
60043                 }
60044                 
60045                 so.push(cm.config[i].dataIndex);
60046             };
60047             dm.sortOrder = so;
60048         }
60049         
60050         
60051         dm.sort(cm.getDataIndex(index));
60052     },
60053
60054
60055     destroy : function(){
60056         if(this.colMenu){
60057             this.colMenu.removeAll();
60058             Roo.menu.MenuMgr.unregister(this.colMenu);
60059             this.colMenu.getEl().remove();
60060             delete this.colMenu;
60061         }
60062         if(this.hmenu){
60063             this.hmenu.removeAll();
60064             Roo.menu.MenuMgr.unregister(this.hmenu);
60065             this.hmenu.getEl().remove();
60066             delete this.hmenu;
60067         }
60068         if(this.grid.enableColumnMove){
60069             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60070             if(dds){
60071                 for(var dd in dds){
60072                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60073                         var elid = dds[dd].dragElId;
60074                         dds[dd].unreg();
60075                         Roo.get(elid).remove();
60076                     } else if(dds[dd].config.isTarget){
60077                         dds[dd].proxyTop.remove();
60078                         dds[dd].proxyBottom.remove();
60079                         dds[dd].unreg();
60080                     }
60081                     if(Roo.dd.DDM.locationCache[dd]){
60082                         delete Roo.dd.DDM.locationCache[dd];
60083                     }
60084                 }
60085                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60086             }
60087         }
60088         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60089         this.bind(null, null);
60090         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60091     },
60092
60093     handleLockChange : function(){
60094         this.refresh(true);
60095     },
60096
60097     onDenyColumnLock : function(){
60098
60099     },
60100
60101     onDenyColumnHide : function(){
60102
60103     },
60104
60105     handleHdMenuClick : function(item){
60106         var index = this.hdCtxIndex;
60107         var cm = this.cm, ds = this.ds;
60108         switch(item.id){
60109             case "asc":
60110                 ds.sort(cm.getDataIndex(index), "ASC");
60111                 break;
60112             case "desc":
60113                 ds.sort(cm.getDataIndex(index), "DESC");
60114                 break;
60115             case "lock":
60116                 var lc = cm.getLockedCount();
60117                 if(cm.getColumnCount(true) <= lc+1){
60118                     this.onDenyColumnLock();
60119                     return;
60120                 }
60121                 if(lc != index){
60122                     cm.setLocked(index, true, true);
60123                     cm.moveColumn(index, lc);
60124                     this.grid.fireEvent("columnmove", index, lc);
60125                 }else{
60126                     cm.setLocked(index, true);
60127                 }
60128             break;
60129             case "unlock":
60130                 var lc = cm.getLockedCount();
60131                 if((lc-1) != index){
60132                     cm.setLocked(index, false, true);
60133                     cm.moveColumn(index, lc-1);
60134                     this.grid.fireEvent("columnmove", index, lc-1);
60135                 }else{
60136                     cm.setLocked(index, false);
60137                 }
60138             break;
60139             case 'wider': // used to expand cols on touch..
60140             case 'narrow':
60141                 var cw = cm.getColumnWidth(index);
60142                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60143                 cw = Math.max(0, cw);
60144                 cw = Math.min(cw,4000);
60145                 cm.setColumnWidth(index, cw);
60146                 break;
60147                 
60148             default:
60149                 index = cm.getIndexById(item.id.substr(4));
60150                 if(index != -1){
60151                     if(item.checked && cm.getColumnCount(true) <= 1){
60152                         this.onDenyColumnHide();
60153                         return false;
60154                     }
60155                     cm.setHidden(index, item.checked);
60156                 }
60157         }
60158         return true;
60159     },
60160
60161     beforeColMenuShow : function(){
60162         var cm = this.cm,  colCount = cm.getColumnCount();
60163         this.colMenu.removeAll();
60164         for(var i = 0; i < colCount; i++){
60165             this.colMenu.add(new Roo.menu.CheckItem({
60166                 id: "col-"+cm.getColumnId(i),
60167                 text: cm.getColumnHeader(i),
60168                 checked: !cm.isHidden(i),
60169                 hideOnClick:false
60170             }));
60171         }
60172     },
60173
60174     handleHdCtx : function(g, index, e){
60175         e.stopEvent();
60176         var hd = this.getHeaderCell(index);
60177         this.hdCtxIndex = index;
60178         var ms = this.hmenu.items, cm = this.cm;
60179         ms.get("asc").setDisabled(!cm.isSortable(index));
60180         ms.get("desc").setDisabled(!cm.isSortable(index));
60181         if(this.grid.enableColLock !== false){
60182             ms.get("lock").setDisabled(cm.isLocked(index));
60183             ms.get("unlock").setDisabled(!cm.isLocked(index));
60184         }
60185         this.hmenu.show(hd, "tl-bl");
60186     },
60187
60188     handleHdOver : function(e){
60189         var hd = this.findHeaderCell(e.getTarget());
60190         if(hd && !this.headersDisabled){
60191             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60192                this.fly(hd).addClass("x-grid-hd-over");
60193             }
60194         }
60195     },
60196
60197     handleHdOut : function(e){
60198         var hd = this.findHeaderCell(e.getTarget());
60199         if(hd){
60200             this.fly(hd).removeClass("x-grid-hd-over");
60201         }
60202     },
60203
60204     handleSplitDblClick : function(e, t){
60205         var i = this.getCellIndex(t);
60206         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60207             this.autoSizeColumn(i, true);
60208             this.layout();
60209         }
60210     },
60211
60212     render : function(){
60213
60214         var cm = this.cm;
60215         var colCount = cm.getColumnCount();
60216
60217         if(this.grid.monitorWindowResize === true){
60218             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60219         }
60220         var header = this.renderHeaders();
60221         var body = this.templates.body.apply({rows:""});
60222         var html = this.templates.master.apply({
60223             lockedBody: body,
60224             body: body,
60225             lockedHeader: header[0],
60226             header: header[1]
60227         });
60228
60229         //this.updateColumns();
60230
60231         this.grid.getGridEl().dom.innerHTML = html;
60232
60233         this.initElements();
60234         
60235         // a kludge to fix the random scolling effect in webkit
60236         this.el.on("scroll", function() {
60237             this.el.dom.scrollTop=0; // hopefully not recursive..
60238         },this);
60239
60240         this.scroller.on("scroll", this.handleScroll, this);
60241         this.lockedBody.on("mousewheel", this.handleWheel, this);
60242         this.mainBody.on("mousewheel", this.handleWheel, this);
60243
60244         this.mainHd.on("mouseover", this.handleHdOver, this);
60245         this.mainHd.on("mouseout", this.handleHdOut, this);
60246         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60247                 {delegate: "."+this.splitClass});
60248
60249         this.lockedHd.on("mouseover", this.handleHdOver, this);
60250         this.lockedHd.on("mouseout", this.handleHdOut, this);
60251         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60252                 {delegate: "."+this.splitClass});
60253
60254         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60255             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60256         }
60257
60258         this.updateSplitters();
60259
60260         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60261             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60262             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60263         }
60264
60265         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60266             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60267             this.hmenu.add(
60268                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60269                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60270             );
60271             if(this.grid.enableColLock !== false){
60272                 this.hmenu.add('-',
60273                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60274                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60275                 );
60276             }
60277             if (Roo.isTouch) {
60278                  this.hmenu.add('-',
60279                     {id:"wider", text: this.columnsWiderText},
60280                     {id:"narrow", text: this.columnsNarrowText }
60281                 );
60282                 
60283                  
60284             }
60285             
60286             if(this.grid.enableColumnHide !== false){
60287
60288                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60289                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60290                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60291
60292                 this.hmenu.add('-',
60293                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60294                 );
60295             }
60296             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60297
60298             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60299         }
60300
60301         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60302             this.dd = new Roo.grid.GridDragZone(this.grid, {
60303                 ddGroup : this.grid.ddGroup || 'GridDD'
60304             });
60305             
60306         }
60307
60308         /*
60309         for(var i = 0; i < colCount; i++){
60310             if(cm.isHidden(i)){
60311                 this.hideColumn(i);
60312             }
60313             if(cm.config[i].align){
60314                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60315                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60316             }
60317         }*/
60318         
60319         this.updateHeaderSortState();
60320
60321         this.beforeInitialResize();
60322         this.layout(true);
60323
60324         // two part rendering gives faster view to the user
60325         this.renderPhase2.defer(1, this);
60326     },
60327
60328     renderPhase2 : function(){
60329         // render the rows now
60330         this.refresh();
60331         if(this.grid.autoSizeColumns){
60332             this.autoSizeColumns();
60333         }
60334     },
60335
60336     beforeInitialResize : function(){
60337
60338     },
60339
60340     onColumnSplitterMoved : function(i, w){
60341         this.userResized = true;
60342         var cm = this.grid.colModel;
60343         cm.setColumnWidth(i, w, true);
60344         var cid = cm.getColumnId(i);
60345         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60346         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60347         this.updateSplitters();
60348         this.layout();
60349         this.grid.fireEvent("columnresize", i, w);
60350     },
60351
60352     syncRowHeights : function(startIndex, endIndex){
60353         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60354             startIndex = startIndex || 0;
60355             var mrows = this.getBodyTable().rows;
60356             var lrows = this.getLockedTable().rows;
60357             var len = mrows.length-1;
60358             endIndex = Math.min(endIndex || len, len);
60359             for(var i = startIndex; i <= endIndex; i++){
60360                 var m = mrows[i], l = lrows[i];
60361                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60362                 m.style.height = l.style.height = h + "px";
60363             }
60364         }
60365     },
60366
60367     layout : function(initialRender, is2ndPass)
60368     {
60369         var g = this.grid;
60370         var auto = g.autoHeight;
60371         var scrollOffset = 16;
60372         var c = g.getGridEl(), cm = this.cm,
60373                 expandCol = g.autoExpandColumn,
60374                 gv = this;
60375         //c.beginMeasure();
60376
60377         if(!c.dom.offsetWidth){ // display:none?
60378             if(initialRender){
60379                 this.lockedWrap.show();
60380                 this.mainWrap.show();
60381             }
60382             return;
60383         }
60384
60385         var hasLock = this.cm.isLocked(0);
60386
60387         var tbh = this.headerPanel.getHeight();
60388         var bbh = this.footerPanel.getHeight();
60389
60390         if(auto){
60391             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60392             var newHeight = ch + c.getBorderWidth("tb");
60393             if(g.maxHeight){
60394                 newHeight = Math.min(g.maxHeight, newHeight);
60395             }
60396             c.setHeight(newHeight);
60397         }
60398
60399         if(g.autoWidth){
60400             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60401         }
60402
60403         var s = this.scroller;
60404
60405         var csize = c.getSize(true);
60406
60407         this.el.setSize(csize.width, csize.height);
60408
60409         this.headerPanel.setWidth(csize.width);
60410         this.footerPanel.setWidth(csize.width);
60411
60412         var hdHeight = this.mainHd.getHeight();
60413         var vw = csize.width;
60414         var vh = csize.height - (tbh + bbh);
60415
60416         s.setSize(vw, vh);
60417
60418         var bt = this.getBodyTable();
60419         
60420         if(cm.getLockedCount() == cm.config.length){
60421             bt = this.getLockedTable();
60422         }
60423         
60424         var ltWidth = hasLock ?
60425                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60426
60427         var scrollHeight = bt.offsetHeight;
60428         var scrollWidth = ltWidth + bt.offsetWidth;
60429         var vscroll = false, hscroll = false;
60430
60431         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60432
60433         var lw = this.lockedWrap, mw = this.mainWrap;
60434         var lb = this.lockedBody, mb = this.mainBody;
60435
60436         setTimeout(function(){
60437             var t = s.dom.offsetTop;
60438             var w = s.dom.clientWidth,
60439                 h = s.dom.clientHeight;
60440
60441             lw.setTop(t);
60442             lw.setSize(ltWidth, h);
60443
60444             mw.setLeftTop(ltWidth, t);
60445             mw.setSize(w-ltWidth, h);
60446
60447             lb.setHeight(h-hdHeight);
60448             mb.setHeight(h-hdHeight);
60449
60450             if(is2ndPass !== true && !gv.userResized && expandCol){
60451                 // high speed resize without full column calculation
60452                 
60453                 var ci = cm.getIndexById(expandCol);
60454                 if (ci < 0) {
60455                     ci = cm.findColumnIndex(expandCol);
60456                 }
60457                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60458                 var expandId = cm.getColumnId(ci);
60459                 var  tw = cm.getTotalWidth(false);
60460                 var currentWidth = cm.getColumnWidth(ci);
60461                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60462                 if(currentWidth != cw){
60463                     cm.setColumnWidth(ci, cw, true);
60464                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60465                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60466                     gv.updateSplitters();
60467                     gv.layout(false, true);
60468                 }
60469             }
60470
60471             if(initialRender){
60472                 lw.show();
60473                 mw.show();
60474             }
60475             //c.endMeasure();
60476         }, 10);
60477     },
60478
60479     onWindowResize : function(){
60480         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60481             return;
60482         }
60483         this.layout();
60484     },
60485
60486     appendFooter : function(parentEl){
60487         return null;
60488     },
60489
60490     sortAscText : "Sort Ascending",
60491     sortDescText : "Sort Descending",
60492     lockText : "Lock Column",
60493     unlockText : "Unlock Column",
60494     columnsText : "Columns",
60495  
60496     columnsWiderText : "Wider",
60497     columnsNarrowText : "Thinner"
60498 });
60499
60500
60501 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60502     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60503     this.proxy.el.addClass('x-grid3-col-dd');
60504 };
60505
60506 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60507     handleMouseDown : function(e){
60508
60509     },
60510
60511     callHandleMouseDown : function(e){
60512         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60513     }
60514 });
60515 /*
60516  * Based on:
60517  * Ext JS Library 1.1.1
60518  * Copyright(c) 2006-2007, Ext JS, LLC.
60519  *
60520  * Originally Released Under LGPL - original licence link has changed is not relivant.
60521  *
60522  * Fork - LGPL
60523  * <script type="text/javascript">
60524  */
60525  /**
60526  * @extends Roo.dd.DDProxy
60527  * @class Roo.grid.SplitDragZone
60528  * Support for Column Header resizing
60529  * @constructor
60530  * @param {Object} config
60531  */
60532 // private
60533 // This is a support class used internally by the Grid components
60534 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60535     this.grid = grid;
60536     this.view = grid.getView();
60537     this.proxy = this.view.resizeProxy;
60538     Roo.grid.SplitDragZone.superclass.constructor.call(
60539         this,
60540         hd, // ID
60541         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60542         {  // CONFIG
60543             dragElId : Roo.id(this.proxy.dom),
60544             resizeFrame:false
60545         }
60546     );
60547     
60548     this.setHandleElId(Roo.id(hd));
60549     if (hd2 !== false) {
60550         this.setOuterHandleElId(Roo.id(hd2));
60551     }
60552     
60553     this.scroll = false;
60554 };
60555 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60556     fly: Roo.Element.fly,
60557
60558     b4StartDrag : function(x, y){
60559         this.view.headersDisabled = true;
60560         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60561                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60562         );
60563         this.proxy.setHeight(h);
60564         
60565         // for old system colWidth really stored the actual width?
60566         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60567         // which in reality did not work.. - it worked only for fixed sizes
60568         // for resizable we need to use actual sizes.
60569         var w = this.cm.getColumnWidth(this.cellIndex);
60570         if (!this.view.mainWrap) {
60571             // bootstrap.
60572             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60573         }
60574         
60575         
60576         
60577         // this was w-this.grid.minColumnWidth;
60578         // doesnt really make sense? - w = thie curren width or the rendered one?
60579         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60580         this.resetConstraints();
60581         this.setXConstraint(minw, 1000);
60582         this.setYConstraint(0, 0);
60583         this.minX = x - minw;
60584         this.maxX = x + 1000;
60585         this.startPos = x;
60586         if (!this.view.mainWrap) { // this is Bootstrap code..
60587             this.getDragEl().style.display='block';
60588         }
60589         
60590         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60591     },
60592
60593
60594     handleMouseDown : function(e){
60595         ev = Roo.EventObject.setEvent(e);
60596         var t = this.fly(ev.getTarget());
60597         if(t.hasClass("x-grid-split")){
60598             this.cellIndex = this.view.getCellIndex(t.dom);
60599             this.split = t.dom;
60600             this.cm = this.grid.colModel;
60601             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60602                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60603             }
60604         }
60605     },
60606
60607     endDrag : function(e){
60608         this.view.headersDisabled = false;
60609         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60610         var diff = endX - this.startPos;
60611         // 
60612         var w = this.cm.getColumnWidth(this.cellIndex);
60613         if (!this.view.mainWrap) {
60614             w = 0;
60615         }
60616         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60617     },
60618
60619     autoOffset : function(){
60620         this.setDelta(0,0);
60621     }
60622 });/*
60623  * Based on:
60624  * Ext JS Library 1.1.1
60625  * Copyright(c) 2006-2007, Ext JS, LLC.
60626  *
60627  * Originally Released Under LGPL - original licence link has changed is not relivant.
60628  *
60629  * Fork - LGPL
60630  * <script type="text/javascript">
60631  */
60632  
60633 // private
60634 // This is a support class used internally by the Grid components
60635 Roo.grid.GridDragZone = function(grid, config){
60636     this.view = grid.getView();
60637     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60638     if(this.view.lockedBody){
60639         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60640         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60641     }
60642     this.scroll = false;
60643     this.grid = grid;
60644     this.ddel = document.createElement('div');
60645     this.ddel.className = 'x-grid-dd-wrap';
60646 };
60647
60648 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60649     ddGroup : "GridDD",
60650
60651     getDragData : function(e){
60652         var t = Roo.lib.Event.getTarget(e);
60653         var rowIndex = this.view.findRowIndex(t);
60654         var sm = this.grid.selModel;
60655             
60656         //Roo.log(rowIndex);
60657         
60658         if (sm.getSelectedCell) {
60659             // cell selection..
60660             if (!sm.getSelectedCell()) {
60661                 return false;
60662             }
60663             if (rowIndex != sm.getSelectedCell()[0]) {
60664                 return false;
60665             }
60666         
60667         }
60668         if (sm.getSelections && sm.getSelections().length < 1) {
60669             return false;
60670         }
60671         
60672         
60673         // before it used to all dragging of unseleted... - now we dont do that.
60674         if(rowIndex !== false){
60675             
60676             // if editorgrid.. 
60677             
60678             
60679             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60680                
60681             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60682               //  
60683             //}
60684             if (e.hasModifier()){
60685                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60686             }
60687             
60688             Roo.log("getDragData");
60689             
60690             return {
60691                 grid: this.grid,
60692                 ddel: this.ddel,
60693                 rowIndex: rowIndex,
60694                 selections: sm.getSelections ? sm.getSelections() : (
60695                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60696             };
60697         }
60698         return false;
60699     },
60700     
60701     
60702     onInitDrag : function(e){
60703         var data = this.dragData;
60704         this.ddel.innerHTML = this.grid.getDragDropText();
60705         this.proxy.update(this.ddel);
60706         // fire start drag?
60707     },
60708
60709     afterRepair : function(){
60710         this.dragging = false;
60711     },
60712
60713     getRepairXY : function(e, data){
60714         return false;
60715     },
60716
60717     onEndDrag : function(data, e){
60718         // fire end drag?
60719     },
60720
60721     onValidDrop : function(dd, e, id){
60722         // fire drag drop?
60723         this.hideProxy();
60724     },
60725
60726     beforeInvalidDrop : function(e, id){
60727
60728     }
60729 });/*
60730  * Based on:
60731  * Ext JS Library 1.1.1
60732  * Copyright(c) 2006-2007, Ext JS, LLC.
60733  *
60734  * Originally Released Under LGPL - original licence link has changed is not relivant.
60735  *
60736  * Fork - LGPL
60737  * <script type="text/javascript">
60738  */
60739  
60740
60741 /**
60742  * @class Roo.grid.ColumnModel
60743  * @extends Roo.util.Observable
60744  * This is the default implementation of a ColumnModel used by the Grid. It defines
60745  * the columns in the grid.
60746  * <br>Usage:<br>
60747  <pre><code>
60748  var colModel = new Roo.grid.ColumnModel([
60749         {header: "Ticker", width: 60, sortable: true, locked: true},
60750         {header: "Company Name", width: 150, sortable: true},
60751         {header: "Market Cap.", width: 100, sortable: true},
60752         {header: "$ Sales", width: 100, sortable: true, renderer: money},
60753         {header: "Employees", width: 100, sortable: true, resizable: false}
60754  ]);
60755  </code></pre>
60756  * <p>
60757  
60758  * The config options listed for this class are options which may appear in each
60759  * individual column definition.
60760  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
60761  * @constructor
60762  * @param {Object} config An Array of column config objects. See this class's
60763  * config objects for details.
60764 */
60765 Roo.grid.ColumnModel = function(config){
60766         /**
60767      * The config passed into the constructor
60768      */
60769     this.config = []; //config;
60770     this.lookup = {};
60771
60772     // if no id, create one
60773     // if the column does not have a dataIndex mapping,
60774     // map it to the order it is in the config
60775     for(var i = 0, len = config.length; i < len; i++){
60776         this.addColumn(config[i]);
60777         
60778     }
60779
60780     /**
60781      * The width of columns which have no width specified (defaults to 100)
60782      * @type Number
60783      */
60784     this.defaultWidth = 100;
60785
60786     /**
60787      * Default sortable of columns which have no sortable specified (defaults to false)
60788      * @type Boolean
60789      */
60790     this.defaultSortable = false;
60791
60792     this.addEvents({
60793         /**
60794              * @event widthchange
60795              * Fires when the width of a column changes.
60796              * @param {ColumnModel} this
60797              * @param {Number} columnIndex The column index
60798              * @param {Number} newWidth The new width
60799              */
60800             "widthchange": true,
60801         /**
60802              * @event headerchange
60803              * Fires when the text of a header changes.
60804              * @param {ColumnModel} this
60805              * @param {Number} columnIndex The column index
60806              * @param {Number} newText The new header text
60807              */
60808             "headerchange": true,
60809         /**
60810              * @event hiddenchange
60811              * Fires when a column is hidden or "unhidden".
60812              * @param {ColumnModel} this
60813              * @param {Number} columnIndex The column index
60814              * @param {Boolean} hidden true if hidden, false otherwise
60815              */
60816             "hiddenchange": true,
60817             /**
60818          * @event columnmoved
60819          * Fires when a column is moved.
60820          * @param {ColumnModel} this
60821          * @param {Number} oldIndex
60822          * @param {Number} newIndex
60823          */
60824         "columnmoved" : true,
60825         /**
60826          * @event columlockchange
60827          * Fires when a column's locked state is changed
60828          * @param {ColumnModel} this
60829          * @param {Number} colIndex
60830          * @param {Boolean} locked true if locked
60831          */
60832         "columnlockchange" : true
60833     });
60834     Roo.grid.ColumnModel.superclass.constructor.call(this);
60835 };
60836 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
60837     /**
60838      * @cfg {String} header The header text to display in the Grid view.
60839      */
60840         /**
60841      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
60842      */
60843         /**
60844      * @cfg {String} smHeader Header at Bootsrap Small width
60845      */
60846         /**
60847      * @cfg {String} mdHeader Header at Bootsrap Medium width
60848      */
60849         /**
60850      * @cfg {String} lgHeader Header at Bootsrap Large width
60851      */
60852         /**
60853      * @cfg {String} xlHeader Header at Bootsrap extra Large width
60854      */
60855     /**
60856      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
60857      * {@link Roo.data.Record} definition from which to draw the column's value. If not
60858      * specified, the column's index is used as an index into the Record's data Array.
60859      */
60860     /**
60861      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
60862      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
60863      */
60864     /**
60865      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
60866      * Defaults to the value of the {@link #defaultSortable} property.
60867      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
60868      */
60869     /**
60870      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
60871      */
60872     /**
60873      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
60874      */
60875     /**
60876      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
60877      */
60878     /**
60879      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
60880      */
60881     /**
60882      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
60883      * given the cell's data value. See {@link #setRenderer}. If not specified, the
60884      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
60885      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
60886      */
60887        /**
60888      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
60889      */
60890     /**
60891      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
60892      */
60893     /**
60894      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
60895      */
60896     /**
60897      * @cfg {String} cursor (Optional)
60898      */
60899     /**
60900      * @cfg {String} tooltip (Optional)
60901      */
60902     /**
60903      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
60904      */
60905     /**
60906      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
60907      */
60908     /**
60909      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
60910      */
60911     /**
60912      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
60913      */
60914         /**
60915      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
60916      */
60917     /**
60918      * Returns the id of the column at the specified index.
60919      * @param {Number} index The column index
60920      * @return {String} the id
60921      */
60922     getColumnId : function(index){
60923         return this.config[index].id;
60924     },
60925
60926     /**
60927      * Returns the column for a specified id.
60928      * @param {String} id The column id
60929      * @return {Object} the column
60930      */
60931     getColumnById : function(id){
60932         return this.lookup[id];
60933     },
60934
60935     
60936     /**
60937      * Returns the column Object for a specified dataIndex.
60938      * @param {String} dataIndex The column dataIndex
60939      * @return {Object|Boolean} the column or false if not found
60940      */
60941     getColumnByDataIndex: function(dataIndex){
60942         var index = this.findColumnIndex(dataIndex);
60943         return index > -1 ? this.config[index] : false;
60944     },
60945     
60946     /**
60947      * Returns the index for a specified column id.
60948      * @param {String} id The column id
60949      * @return {Number} the index, or -1 if not found
60950      */
60951     getIndexById : function(id){
60952         for(var i = 0, len = this.config.length; i < len; i++){
60953             if(this.config[i].id == id){
60954                 return i;
60955             }
60956         }
60957         return -1;
60958     },
60959     
60960     /**
60961      * Returns the index for a specified column dataIndex.
60962      * @param {String} dataIndex The column dataIndex
60963      * @return {Number} the index, or -1 if not found
60964      */
60965     
60966     findColumnIndex : function(dataIndex){
60967         for(var i = 0, len = this.config.length; i < len; i++){
60968             if(this.config[i].dataIndex == dataIndex){
60969                 return i;
60970             }
60971         }
60972         return -1;
60973     },
60974     
60975     
60976     moveColumn : function(oldIndex, newIndex){
60977         var c = this.config[oldIndex];
60978         this.config.splice(oldIndex, 1);
60979         this.config.splice(newIndex, 0, c);
60980         this.dataMap = null;
60981         this.fireEvent("columnmoved", this, oldIndex, newIndex);
60982     },
60983
60984     isLocked : function(colIndex){
60985         return this.config[colIndex].locked === true;
60986     },
60987
60988     setLocked : function(colIndex, value, suppressEvent){
60989         if(this.isLocked(colIndex) == value){
60990             return;
60991         }
60992         this.config[colIndex].locked = value;
60993         if(!suppressEvent){
60994             this.fireEvent("columnlockchange", this, colIndex, value);
60995         }
60996     },
60997
60998     getTotalLockedWidth : function(){
60999         var totalWidth = 0;
61000         for(var i = 0; i < this.config.length; i++){
61001             if(this.isLocked(i) && !this.isHidden(i)){
61002                 this.totalWidth += this.getColumnWidth(i);
61003             }
61004         }
61005         return totalWidth;
61006     },
61007
61008     getLockedCount : function(){
61009         for(var i = 0, len = this.config.length; i < len; i++){
61010             if(!this.isLocked(i)){
61011                 return i;
61012             }
61013         }
61014         
61015         return this.config.length;
61016     },
61017
61018     /**
61019      * Returns the number of columns.
61020      * @return {Number}
61021      */
61022     getColumnCount : function(visibleOnly){
61023         if(visibleOnly === true){
61024             var c = 0;
61025             for(var i = 0, len = this.config.length; i < len; i++){
61026                 if(!this.isHidden(i)){
61027                     c++;
61028                 }
61029             }
61030             return c;
61031         }
61032         return this.config.length;
61033     },
61034
61035     /**
61036      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61037      * @param {Function} fn
61038      * @param {Object} scope (optional)
61039      * @return {Array} result
61040      */
61041     getColumnsBy : function(fn, scope){
61042         var r = [];
61043         for(var i = 0, len = this.config.length; i < len; i++){
61044             var c = this.config[i];
61045             if(fn.call(scope||this, c, i) === true){
61046                 r[r.length] = c;
61047             }
61048         }
61049         return r;
61050     },
61051
61052     /**
61053      * Returns true if the specified column is sortable.
61054      * @param {Number} col The column index
61055      * @return {Boolean}
61056      */
61057     isSortable : function(col){
61058         if(typeof this.config[col].sortable == "undefined"){
61059             return this.defaultSortable;
61060         }
61061         return this.config[col].sortable;
61062     },
61063
61064     /**
61065      * Returns the rendering (formatting) function defined for the column.
61066      * @param {Number} col The column index.
61067      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61068      */
61069     getRenderer : function(col){
61070         if(!this.config[col].renderer){
61071             return Roo.grid.ColumnModel.defaultRenderer;
61072         }
61073         return this.config[col].renderer;
61074     },
61075
61076     /**
61077      * Sets the rendering (formatting) function for a column.
61078      * @param {Number} col The column index
61079      * @param {Function} fn The function to use to process the cell's raw data
61080      * to return HTML markup for the grid view. The render function is called with
61081      * the following parameters:<ul>
61082      * <li>Data value.</li>
61083      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61084      * <li>css A CSS style string to apply to the table cell.</li>
61085      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61086      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61087      * <li>Row index</li>
61088      * <li>Column index</li>
61089      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61090      */
61091     setRenderer : function(col, fn){
61092         this.config[col].renderer = fn;
61093     },
61094
61095     /**
61096      * Returns the width for the specified column.
61097      * @param {Number} col The column index
61098      * @param (optional) {String} gridSize bootstrap width size.
61099      * @return {Number}
61100      */
61101     getColumnWidth : function(col, gridSize)
61102         {
61103                 var cfg = this.config[col];
61104                 
61105                 if (typeof(gridSize) == 'undefined') {
61106                         return cfg.width * 1 || this.defaultWidth;
61107                 }
61108                 if (gridSize === false) { // if we set it..
61109                         return cfg.width || false;
61110                 }
61111                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61112                 
61113                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61114                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61115                                 continue;
61116                         }
61117                         return cfg[ sizes[i] ];
61118                 }
61119                 return 1;
61120                 
61121     },
61122
61123     /**
61124      * Sets the width for a column.
61125      * @param {Number} col The column index
61126      * @param {Number} width The new width
61127      */
61128     setColumnWidth : function(col, width, suppressEvent){
61129         this.config[col].width = width;
61130         this.totalWidth = null;
61131         if(!suppressEvent){
61132              this.fireEvent("widthchange", this, col, width);
61133         }
61134     },
61135
61136     /**
61137      * Returns the total width of all columns.
61138      * @param {Boolean} includeHidden True to include hidden column widths
61139      * @return {Number}
61140      */
61141     getTotalWidth : function(includeHidden){
61142         if(!this.totalWidth){
61143             this.totalWidth = 0;
61144             for(var i = 0, len = this.config.length; i < len; i++){
61145                 if(includeHidden || !this.isHidden(i)){
61146                     this.totalWidth += this.getColumnWidth(i);
61147                 }
61148             }
61149         }
61150         return this.totalWidth;
61151     },
61152
61153     /**
61154      * Returns the header for the specified column.
61155      * @param {Number} col The column index
61156      * @return {String}
61157      */
61158     getColumnHeader : function(col){
61159         return this.config[col].header;
61160     },
61161
61162     /**
61163      * Sets the header for a column.
61164      * @param {Number} col The column index
61165      * @param {String} header The new header
61166      */
61167     setColumnHeader : function(col, header){
61168         this.config[col].header = header;
61169         this.fireEvent("headerchange", this, col, header);
61170     },
61171
61172     /**
61173      * Returns the tooltip for the specified column.
61174      * @param {Number} col The column index
61175      * @return {String}
61176      */
61177     getColumnTooltip : function(col){
61178             return this.config[col].tooltip;
61179     },
61180     /**
61181      * Sets the tooltip for a column.
61182      * @param {Number} col The column index
61183      * @param {String} tooltip The new tooltip
61184      */
61185     setColumnTooltip : function(col, tooltip){
61186             this.config[col].tooltip = tooltip;
61187     },
61188
61189     /**
61190      * Returns the dataIndex for the specified column.
61191      * @param {Number} col The column index
61192      * @return {Number}
61193      */
61194     getDataIndex : function(col){
61195         return this.config[col].dataIndex;
61196     },
61197
61198     /**
61199      * Sets the dataIndex for a column.
61200      * @param {Number} col The column index
61201      * @param {Number} dataIndex The new dataIndex
61202      */
61203     setDataIndex : function(col, dataIndex){
61204         this.config[col].dataIndex = dataIndex;
61205     },
61206
61207     
61208     
61209     /**
61210      * Returns true if the cell is editable.
61211      * @param {Number} colIndex The column index
61212      * @param {Number} rowIndex The row index - this is nto actually used..?
61213      * @return {Boolean}
61214      */
61215     isCellEditable : function(colIndex, rowIndex){
61216         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61217     },
61218
61219     /**
61220      * Returns the editor defined for the cell/column.
61221      * return false or null to disable editing.
61222      * @param {Number} colIndex The column index
61223      * @param {Number} rowIndex The row index
61224      * @return {Object}
61225      */
61226     getCellEditor : function(colIndex, rowIndex){
61227         return this.config[colIndex].editor;
61228     },
61229
61230     /**
61231      * Sets if a column is editable.
61232      * @param {Number} col The column index
61233      * @param {Boolean} editable True if the column is editable
61234      */
61235     setEditable : function(col, editable){
61236         this.config[col].editable = editable;
61237     },
61238
61239
61240     /**
61241      * Returns true if the column is hidden.
61242      * @param {Number} colIndex The column index
61243      * @return {Boolean}
61244      */
61245     isHidden : function(colIndex){
61246         return this.config[colIndex].hidden;
61247     },
61248
61249
61250     /**
61251      * Returns true if the column width cannot be changed
61252      */
61253     isFixed : function(colIndex){
61254         return this.config[colIndex].fixed;
61255     },
61256
61257     /**
61258      * Returns true if the column can be resized
61259      * @return {Boolean}
61260      */
61261     isResizable : function(colIndex){
61262         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61263     },
61264     /**
61265      * Sets if a column is hidden.
61266      * @param {Number} colIndex The column index
61267      * @param {Boolean} hidden True if the column is hidden
61268      */
61269     setHidden : function(colIndex, hidden){
61270         this.config[colIndex].hidden = hidden;
61271         this.totalWidth = null;
61272         this.fireEvent("hiddenchange", this, colIndex, hidden);
61273     },
61274
61275     /**
61276      * Sets the editor for a column.
61277      * @param {Number} col The column index
61278      * @param {Object} editor The editor object
61279      */
61280     setEditor : function(col, editor){
61281         this.config[col].editor = editor;
61282     },
61283     /**
61284      * Add a column (experimental...) - defaults to adding to the end..
61285      * @param {Object} config 
61286     */
61287     addColumn : function(c)
61288     {
61289     
61290         var i = this.config.length;
61291         this.config[i] = c;
61292         
61293         if(typeof c.dataIndex == "undefined"){
61294             c.dataIndex = i;
61295         }
61296         if(typeof c.renderer == "string"){
61297             c.renderer = Roo.util.Format[c.renderer];
61298         }
61299         if(typeof c.id == "undefined"){
61300             c.id = Roo.id();
61301         }
61302         if(c.editor && c.editor.xtype){
61303             c.editor  = Roo.factory(c.editor, Roo.grid);
61304         }
61305         if(c.editor && c.editor.isFormField){
61306             c.editor = new Roo.grid.GridEditor(c.editor);
61307         }
61308         this.lookup[c.id] = c;
61309     }
61310     
61311 });
61312
61313 Roo.grid.ColumnModel.defaultRenderer = function(value)
61314 {
61315     if(typeof value == "object") {
61316         return value;
61317     }
61318         if(typeof value == "string" && value.length < 1){
61319             return "&#160;";
61320         }
61321     
61322         return String.format("{0}", value);
61323 };
61324
61325 // Alias for backwards compatibility
61326 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61327 /*
61328  * Based on:
61329  * Ext JS Library 1.1.1
61330  * Copyright(c) 2006-2007, Ext JS, LLC.
61331  *
61332  * Originally Released Under LGPL - original licence link has changed is not relivant.
61333  *
61334  * Fork - LGPL
61335  * <script type="text/javascript">
61336  */
61337
61338 /**
61339  * @class Roo.grid.AbstractSelectionModel
61340  * @extends Roo.util.Observable
61341  * @abstract
61342  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61343  * implemented by descendant classes.  This class should not be directly instantiated.
61344  * @constructor
61345  */
61346 Roo.grid.AbstractSelectionModel = function(){
61347     this.locked = false;
61348     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61349 };
61350
61351 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61352     /** @ignore Called by the grid automatically. Do not call directly. */
61353     init : function(grid){
61354         this.grid = grid;
61355         this.initEvents();
61356     },
61357
61358     /**
61359      * Locks the selections.
61360      */
61361     lock : function(){
61362         this.locked = true;
61363     },
61364
61365     /**
61366      * Unlocks the selections.
61367      */
61368     unlock : function(){
61369         this.locked = false;
61370     },
61371
61372     /**
61373      * Returns true if the selections are locked.
61374      * @return {Boolean}
61375      */
61376     isLocked : function(){
61377         return this.locked;
61378     }
61379 });/*
61380  * Based on:
61381  * Ext JS Library 1.1.1
61382  * Copyright(c) 2006-2007, Ext JS, LLC.
61383  *
61384  * Originally Released Under LGPL - original licence link has changed is not relivant.
61385  *
61386  * Fork - LGPL
61387  * <script type="text/javascript">
61388  */
61389 /**
61390  * @extends Roo.grid.AbstractSelectionModel
61391  * @class Roo.grid.RowSelectionModel
61392  * The default SelectionModel used by {@link Roo.grid.Grid}.
61393  * It supports multiple selections and keyboard selection/navigation. 
61394  * @constructor
61395  * @param {Object} config
61396  */
61397 Roo.grid.RowSelectionModel = function(config){
61398     Roo.apply(this, config);
61399     this.selections = new Roo.util.MixedCollection(false, function(o){
61400         return o.id;
61401     });
61402
61403     this.last = false;
61404     this.lastActive = false;
61405
61406     this.addEvents({
61407         /**
61408         * @event selectionchange
61409         * Fires when the selection changes
61410         * @param {SelectionModel} this
61411         */
61412        "selectionchange" : true,
61413        /**
61414         * @event afterselectionchange
61415         * Fires after the selection changes (eg. by key press or clicking)
61416         * @param {SelectionModel} this
61417         */
61418        "afterselectionchange" : true,
61419        /**
61420         * @event beforerowselect
61421         * Fires when a row is selected being selected, return false to cancel.
61422         * @param {SelectionModel} this
61423         * @param {Number} rowIndex The selected index
61424         * @param {Boolean} keepExisting False if other selections will be cleared
61425         */
61426        "beforerowselect" : true,
61427        /**
61428         * @event rowselect
61429         * Fires when a row is selected.
61430         * @param {SelectionModel} this
61431         * @param {Number} rowIndex The selected index
61432         * @param {Roo.data.Record} r The record
61433         */
61434        "rowselect" : true,
61435        /**
61436         * @event rowdeselect
61437         * Fires when a row is deselected.
61438         * @param {SelectionModel} this
61439         * @param {Number} rowIndex The selected index
61440         */
61441         "rowdeselect" : true
61442     });
61443     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61444     this.locked = false;
61445 };
61446
61447 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61448     /**
61449      * @cfg {Boolean} singleSelect
61450      * True to allow selection of only one row at a time (defaults to false)
61451      */
61452     singleSelect : false,
61453
61454     // private
61455     initEvents : function(){
61456
61457         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61458             this.grid.on("mousedown", this.handleMouseDown, this);
61459         }else{ // allow click to work like normal
61460             this.grid.on("rowclick", this.handleDragableRowClick, this);
61461         }
61462         // bootstrap does not have a view..
61463         var view = this.grid.view ? this.grid.view : this.grid;
61464         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61465             "up" : function(e){
61466                 if(!e.shiftKey){
61467                     this.selectPrevious(e.shiftKey);
61468                 }else if(this.last !== false && this.lastActive !== false){
61469                     var last = this.last;
61470                     this.selectRange(this.last,  this.lastActive-1);
61471                     view.focusRow(this.lastActive);
61472                     if(last !== false){
61473                         this.last = last;
61474                     }
61475                 }else{
61476                     this.selectFirstRow();
61477                 }
61478                 this.fireEvent("afterselectionchange", this);
61479             },
61480             "down" : function(e){
61481                 if(!e.shiftKey){
61482                     this.selectNext(e.shiftKey);
61483                 }else if(this.last !== false && this.lastActive !== false){
61484                     var last = this.last;
61485                     this.selectRange(this.last,  this.lastActive+1);
61486                     view.focusRow(this.lastActive);
61487                     if(last !== false){
61488                         this.last = last;
61489                     }
61490                 }else{
61491                     this.selectFirstRow();
61492                 }
61493                 this.fireEvent("afterselectionchange", this);
61494             },
61495             scope: this
61496         });
61497
61498          
61499         view.on("refresh", this.onRefresh, this);
61500         view.on("rowupdated", this.onRowUpdated, this);
61501         view.on("rowremoved", this.onRemove, this);
61502     },
61503
61504     // private
61505     onRefresh : function(){
61506         var ds = this.grid.ds, i, v = this.grid.view;
61507         var s = this.selections;
61508         s.each(function(r){
61509             if((i = ds.indexOfId(r.id)) != -1){
61510                 v.onRowSelect(i);
61511                 s.add(ds.getAt(i)); // updating the selection relate data
61512             }else{
61513                 s.remove(r);
61514             }
61515         });
61516     },
61517
61518     // private
61519     onRemove : function(v, index, r){
61520         this.selections.remove(r);
61521     },
61522
61523     // private
61524     onRowUpdated : function(v, index, r){
61525         if(this.isSelected(r)){
61526             v.onRowSelect(index);
61527         }
61528     },
61529
61530     /**
61531      * Select records.
61532      * @param {Array} records The records to select
61533      * @param {Boolean} keepExisting (optional) True to keep existing selections
61534      */
61535     selectRecords : function(records, keepExisting){
61536         if(!keepExisting){
61537             this.clearSelections();
61538         }
61539         var ds = this.grid.ds;
61540         for(var i = 0, len = records.length; i < len; i++){
61541             this.selectRow(ds.indexOf(records[i]), true);
61542         }
61543     },
61544
61545     /**
61546      * Gets the number of selected rows.
61547      * @return {Number}
61548      */
61549     getCount : function(){
61550         return this.selections.length;
61551     },
61552
61553     /**
61554      * Selects the first row in the grid.
61555      */
61556     selectFirstRow : function(){
61557         this.selectRow(0);
61558     },
61559
61560     /**
61561      * Select the last row.
61562      * @param {Boolean} keepExisting (optional) True to keep existing selections
61563      */
61564     selectLastRow : function(keepExisting){
61565         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61566     },
61567
61568     /**
61569      * Selects the row immediately following the last selected row.
61570      * @param {Boolean} keepExisting (optional) True to keep existing selections
61571      */
61572     selectNext : function(keepExisting){
61573         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61574             this.selectRow(this.last+1, keepExisting);
61575             var view = this.grid.view ? this.grid.view : this.grid;
61576             view.focusRow(this.last);
61577         }
61578     },
61579
61580     /**
61581      * Selects the row that precedes the last selected row.
61582      * @param {Boolean} keepExisting (optional) True to keep existing selections
61583      */
61584     selectPrevious : function(keepExisting){
61585         if(this.last){
61586             this.selectRow(this.last-1, keepExisting);
61587             var view = this.grid.view ? this.grid.view : this.grid;
61588             view.focusRow(this.last);
61589         }
61590     },
61591
61592     /**
61593      * Returns the selected records
61594      * @return {Array} Array of selected records
61595      */
61596     getSelections : function(){
61597         return [].concat(this.selections.items);
61598     },
61599
61600     /**
61601      * Returns the first selected record.
61602      * @return {Record}
61603      */
61604     getSelected : function(){
61605         return this.selections.itemAt(0);
61606     },
61607
61608
61609     /**
61610      * Clears all selections.
61611      */
61612     clearSelections : function(fast){
61613         if(this.locked) {
61614             return;
61615         }
61616         if(fast !== true){
61617             var ds = this.grid.ds;
61618             var s = this.selections;
61619             s.each(function(r){
61620                 this.deselectRow(ds.indexOfId(r.id));
61621             }, this);
61622             s.clear();
61623         }else{
61624             this.selections.clear();
61625         }
61626         this.last = false;
61627     },
61628
61629
61630     /**
61631      * Selects all rows.
61632      */
61633     selectAll : function(){
61634         if(this.locked) {
61635             return;
61636         }
61637         this.selections.clear();
61638         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61639             this.selectRow(i, true);
61640         }
61641     },
61642
61643     /**
61644      * Returns True if there is a selection.
61645      * @return {Boolean}
61646      */
61647     hasSelection : function(){
61648         return this.selections.length > 0;
61649     },
61650
61651     /**
61652      * Returns True if the specified row is selected.
61653      * @param {Number/Record} record The record or index of the record to check
61654      * @return {Boolean}
61655      */
61656     isSelected : function(index){
61657         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61658         return (r && this.selections.key(r.id) ? true : false);
61659     },
61660
61661     /**
61662      * Returns True if the specified record id is selected.
61663      * @param {String} id The id of record to check
61664      * @return {Boolean}
61665      */
61666     isIdSelected : function(id){
61667         return (this.selections.key(id) ? true : false);
61668     },
61669
61670     // private
61671     handleMouseDown : function(e, t)
61672     {
61673         var view = this.grid.view ? this.grid.view : this.grid;
61674         var rowIndex;
61675         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61676             return;
61677         };
61678         if(e.shiftKey && this.last !== false){
61679             var last = this.last;
61680             this.selectRange(last, rowIndex, e.ctrlKey);
61681             this.last = last; // reset the last
61682             view.focusRow(rowIndex);
61683         }else{
61684             var isSelected = this.isSelected(rowIndex);
61685             if(e.button !== 0 && isSelected){
61686                 view.focusRow(rowIndex);
61687             }else if(e.ctrlKey && isSelected){
61688                 this.deselectRow(rowIndex);
61689             }else if(!isSelected){
61690                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61691                 view.focusRow(rowIndex);
61692             }
61693         }
61694         this.fireEvent("afterselectionchange", this);
61695     },
61696     // private
61697     handleDragableRowClick :  function(grid, rowIndex, e) 
61698     {
61699         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61700             this.selectRow(rowIndex, false);
61701             var view = this.grid.view ? this.grid.view : this.grid;
61702             view.focusRow(rowIndex);
61703              this.fireEvent("afterselectionchange", this);
61704         }
61705     },
61706     
61707     /**
61708      * Selects multiple rows.
61709      * @param {Array} rows Array of the indexes of the row to select
61710      * @param {Boolean} keepExisting (optional) True to keep existing selections
61711      */
61712     selectRows : function(rows, keepExisting){
61713         if(!keepExisting){
61714             this.clearSelections();
61715         }
61716         for(var i = 0, len = rows.length; i < len; i++){
61717             this.selectRow(rows[i], true);
61718         }
61719     },
61720
61721     /**
61722      * Selects a range of rows. All rows in between startRow and endRow are also selected.
61723      * @param {Number} startRow The index of the first row in the range
61724      * @param {Number} endRow The index of the last row in the range
61725      * @param {Boolean} keepExisting (optional) True to retain existing selections
61726      */
61727     selectRange : function(startRow, endRow, keepExisting){
61728         if(this.locked) {
61729             return;
61730         }
61731         if(!keepExisting){
61732             this.clearSelections();
61733         }
61734         if(startRow <= endRow){
61735             for(var i = startRow; i <= endRow; i++){
61736                 this.selectRow(i, true);
61737             }
61738         }else{
61739             for(var i = startRow; i >= endRow; i--){
61740                 this.selectRow(i, true);
61741             }
61742         }
61743     },
61744
61745     /**
61746      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
61747      * @param {Number} startRow The index of the first row in the range
61748      * @param {Number} endRow The index of the last row in the range
61749      */
61750     deselectRange : function(startRow, endRow, preventViewNotify){
61751         if(this.locked) {
61752             return;
61753         }
61754         for(var i = startRow; i <= endRow; i++){
61755             this.deselectRow(i, preventViewNotify);
61756         }
61757     },
61758
61759     /**
61760      * Selects a row.
61761      * @param {Number} row The index of the row to select
61762      * @param {Boolean} keepExisting (optional) True to keep existing selections
61763      */
61764     selectRow : function(index, keepExisting, preventViewNotify){
61765         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
61766             return;
61767         }
61768         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
61769             if(!keepExisting || this.singleSelect){
61770                 this.clearSelections();
61771             }
61772             var r = this.grid.ds.getAt(index);
61773             this.selections.add(r);
61774             this.last = this.lastActive = index;
61775             if(!preventViewNotify){
61776                 var view = this.grid.view ? this.grid.view : this.grid;
61777                 view.onRowSelect(index);
61778             }
61779             this.fireEvent("rowselect", this, index, r);
61780             this.fireEvent("selectionchange", this);
61781         }
61782     },
61783
61784     /**
61785      * Deselects a row.
61786      * @param {Number} row The index of the row to deselect
61787      */
61788     deselectRow : function(index, preventViewNotify){
61789         if(this.locked) {
61790             return;
61791         }
61792         if(this.last == index){
61793             this.last = false;
61794         }
61795         if(this.lastActive == index){
61796             this.lastActive = false;
61797         }
61798         var r = this.grid.ds.getAt(index);
61799         this.selections.remove(r);
61800         if(!preventViewNotify){
61801             var view = this.grid.view ? this.grid.view : this.grid;
61802             view.onRowDeselect(index);
61803         }
61804         this.fireEvent("rowdeselect", this, index);
61805         this.fireEvent("selectionchange", this);
61806     },
61807
61808     // private
61809     restoreLast : function(){
61810         if(this._last){
61811             this.last = this._last;
61812         }
61813     },
61814
61815     // private
61816     acceptsNav : function(row, col, cm){
61817         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61818     },
61819
61820     // private
61821     onEditorKey : function(field, e){
61822         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
61823         if(k == e.TAB){
61824             e.stopEvent();
61825             ed.completeEdit();
61826             if(e.shiftKey){
61827                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61828             }else{
61829                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61830             }
61831         }else if(k == e.ENTER && !e.ctrlKey){
61832             e.stopEvent();
61833             ed.completeEdit();
61834             if(e.shiftKey){
61835                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
61836             }else{
61837                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
61838             }
61839         }else if(k == e.ESC){
61840             ed.cancelEdit();
61841         }
61842         if(newCell){
61843             g.startEditing(newCell[0], newCell[1]);
61844         }
61845     }
61846 });/*
61847  * Based on:
61848  * Ext JS Library 1.1.1
61849  * Copyright(c) 2006-2007, Ext JS, LLC.
61850  *
61851  * Originally Released Under LGPL - original licence link has changed is not relivant.
61852  *
61853  * Fork - LGPL
61854  * <script type="text/javascript">
61855  */
61856 /**
61857  * @class Roo.grid.CellSelectionModel
61858  * @extends Roo.grid.AbstractSelectionModel
61859  * This class provides the basic implementation for cell selection in a grid.
61860  * @constructor
61861  * @param {Object} config The object containing the configuration of this model.
61862  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
61863  */
61864 Roo.grid.CellSelectionModel = function(config){
61865     Roo.apply(this, config);
61866
61867     this.selection = null;
61868
61869     this.addEvents({
61870         /**
61871              * @event beforerowselect
61872              * Fires before a cell is selected.
61873              * @param {SelectionModel} this
61874              * @param {Number} rowIndex The selected row index
61875              * @param {Number} colIndex The selected cell index
61876              */
61877             "beforecellselect" : true,
61878         /**
61879              * @event cellselect
61880              * Fires when a cell is selected.
61881              * @param {SelectionModel} this
61882              * @param {Number} rowIndex The selected row index
61883              * @param {Number} colIndex The selected cell index
61884              */
61885             "cellselect" : true,
61886         /**
61887              * @event selectionchange
61888              * Fires when the active selection changes.
61889              * @param {SelectionModel} this
61890              * @param {Object} selection null for no selection or an object (o) with two properties
61891                 <ul>
61892                 <li>o.record: the record object for the row the selection is in</li>
61893                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
61894                 </ul>
61895              */
61896             "selectionchange" : true,
61897         /**
61898              * @event tabend
61899              * Fires when the tab (or enter) was pressed on the last editable cell
61900              * You can use this to trigger add new row.
61901              * @param {SelectionModel} this
61902              */
61903             "tabend" : true,
61904          /**
61905              * @event beforeeditnext
61906              * Fires before the next editable sell is made active
61907              * You can use this to skip to another cell or fire the tabend
61908              *    if you set cell to false
61909              * @param {Object} eventdata object : { cell : [ row, col ] } 
61910              */
61911             "beforeeditnext" : true
61912     });
61913     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
61914 };
61915
61916 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
61917     
61918     enter_is_tab: false,
61919
61920     /** @ignore */
61921     initEvents : function(){
61922         this.grid.on("mousedown", this.handleMouseDown, this);
61923         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
61924         var view = this.grid.view;
61925         view.on("refresh", this.onViewChange, this);
61926         view.on("rowupdated", this.onRowUpdated, this);
61927         view.on("beforerowremoved", this.clearSelections, this);
61928         view.on("beforerowsinserted", this.clearSelections, this);
61929         if(this.grid.isEditor){
61930             this.grid.on("beforeedit", this.beforeEdit,  this);
61931         }
61932     },
61933
61934         //private
61935     beforeEdit : function(e){
61936         this.select(e.row, e.column, false, true, e.record);
61937     },
61938
61939         //private
61940     onRowUpdated : function(v, index, r){
61941         if(this.selection && this.selection.record == r){
61942             v.onCellSelect(index, this.selection.cell[1]);
61943         }
61944     },
61945
61946         //private
61947     onViewChange : function(){
61948         this.clearSelections(true);
61949     },
61950
61951         /**
61952          * Returns the currently selected cell,.
61953          * @return {Array} The selected cell (row, column) or null if none selected.
61954          */
61955     getSelectedCell : function(){
61956         return this.selection ? this.selection.cell : null;
61957     },
61958
61959     /**
61960      * Clears all selections.
61961      * @param {Boolean} true to prevent the gridview from being notified about the change.
61962      */
61963     clearSelections : function(preventNotify){
61964         var s = this.selection;
61965         if(s){
61966             if(preventNotify !== true){
61967                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
61968             }
61969             this.selection = null;
61970             this.fireEvent("selectionchange", this, null);
61971         }
61972     },
61973
61974     /**
61975      * Returns true if there is a selection.
61976      * @return {Boolean}
61977      */
61978     hasSelection : function(){
61979         return this.selection ? true : false;
61980     },
61981
61982     /** @ignore */
61983     handleMouseDown : function(e, t){
61984         var v = this.grid.getView();
61985         if(this.isLocked()){
61986             return;
61987         };
61988         var row = v.findRowIndex(t);
61989         var cell = v.findCellIndex(t);
61990         if(row !== false && cell !== false){
61991             this.select(row, cell);
61992         }
61993     },
61994
61995     /**
61996      * Selects a cell.
61997      * @param {Number} rowIndex
61998      * @param {Number} collIndex
61999      */
62000     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62001         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62002             this.clearSelections();
62003             r = r || this.grid.dataSource.getAt(rowIndex);
62004             this.selection = {
62005                 record : r,
62006                 cell : [rowIndex, colIndex]
62007             };
62008             if(!preventViewNotify){
62009                 var v = this.grid.getView();
62010                 v.onCellSelect(rowIndex, colIndex);
62011                 if(preventFocus !== true){
62012                     v.focusCell(rowIndex, colIndex);
62013                 }
62014             }
62015             this.fireEvent("cellselect", this, rowIndex, colIndex);
62016             this.fireEvent("selectionchange", this, this.selection);
62017         }
62018     },
62019
62020         //private
62021     isSelectable : function(rowIndex, colIndex, cm){
62022         return !cm.isHidden(colIndex);
62023     },
62024
62025     /** @ignore */
62026     handleKeyDown : function(e){
62027         //Roo.log('Cell Sel Model handleKeyDown');
62028         if(!e.isNavKeyPress()){
62029             return;
62030         }
62031         var g = this.grid, s = this.selection;
62032         if(!s){
62033             e.stopEvent();
62034             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62035             if(cell){
62036                 this.select(cell[0], cell[1]);
62037             }
62038             return;
62039         }
62040         var sm = this;
62041         var walk = function(row, col, step){
62042             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62043         };
62044         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62045         var newCell;
62046
62047       
62048
62049         switch(k){
62050             case e.TAB:
62051                 // handled by onEditorKey
62052                 if (g.isEditor && g.editing) {
62053                     return;
62054                 }
62055                 if(e.shiftKey) {
62056                     newCell = walk(r, c-1, -1);
62057                 } else {
62058                     newCell = walk(r, c+1, 1);
62059                 }
62060                 break;
62061             
62062             case e.DOWN:
62063                newCell = walk(r+1, c, 1);
62064                 break;
62065             
62066             case e.UP:
62067                 newCell = walk(r-1, c, -1);
62068                 break;
62069             
62070             case e.RIGHT:
62071                 newCell = walk(r, c+1, 1);
62072                 break;
62073             
62074             case e.LEFT:
62075                 newCell = walk(r, c-1, -1);
62076                 break;
62077             
62078             case e.ENTER:
62079                 
62080                 if(g.isEditor && !g.editing){
62081                    g.startEditing(r, c);
62082                    e.stopEvent();
62083                    return;
62084                 }
62085                 
62086                 
62087              break;
62088         };
62089         if(newCell){
62090             this.select(newCell[0], newCell[1]);
62091             e.stopEvent();
62092             
62093         }
62094     },
62095
62096     acceptsNav : function(row, col, cm){
62097         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62098     },
62099     /**
62100      * Selects a cell.
62101      * @param {Number} field (not used) - as it's normally used as a listener
62102      * @param {Number} e - event - fake it by using
62103      *
62104      * var e = Roo.EventObjectImpl.prototype;
62105      * e.keyCode = e.TAB
62106      *
62107      * 
62108      */
62109     onEditorKey : function(field, e){
62110         
62111         var k = e.getKey(),
62112             newCell,
62113             g = this.grid,
62114             ed = g.activeEditor,
62115             forward = false;
62116         ///Roo.log('onEditorKey' + k);
62117         
62118         
62119         if (this.enter_is_tab && k == e.ENTER) {
62120             k = e.TAB;
62121         }
62122         
62123         if(k == e.TAB){
62124             if(e.shiftKey){
62125                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62126             }else{
62127                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62128                 forward = true;
62129             }
62130             
62131             e.stopEvent();
62132             
62133         } else if(k == e.ENTER &&  !e.ctrlKey){
62134             ed.completeEdit();
62135             e.stopEvent();
62136             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62137         
62138                 } else if(k == e.ESC){
62139             ed.cancelEdit();
62140         }
62141                 
62142         if (newCell) {
62143             var ecall = { cell : newCell, forward : forward };
62144             this.fireEvent('beforeeditnext', ecall );
62145             newCell = ecall.cell;
62146                         forward = ecall.forward;
62147         }
62148                 
62149         if(newCell){
62150             //Roo.log('next cell after edit');
62151             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62152         } else if (forward) {
62153             // tabbed past last
62154             this.fireEvent.defer(100, this, ['tabend',this]);
62155         }
62156     }
62157 });/*
62158  * Based on:
62159  * Ext JS Library 1.1.1
62160  * Copyright(c) 2006-2007, Ext JS, LLC.
62161  *
62162  * Originally Released Under LGPL - original licence link has changed is not relivant.
62163  *
62164  * Fork - LGPL
62165  * <script type="text/javascript">
62166  */
62167  
62168 /**
62169  * @class Roo.grid.EditorGrid
62170  * @extends Roo.grid.Grid
62171  * Class for creating and editable grid.
62172  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62173  * The container MUST have some type of size defined for the grid to fill. The container will be 
62174  * automatically set to position relative if it isn't already.
62175  * @param {Object} dataSource The data model to bind to
62176  * @param {Object} colModel The column model with info about this grid's columns
62177  */
62178 Roo.grid.EditorGrid = function(container, config){
62179     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62180     this.getGridEl().addClass("xedit-grid");
62181
62182     if(!this.selModel){
62183         this.selModel = new Roo.grid.CellSelectionModel();
62184     }
62185
62186     this.activeEditor = null;
62187
62188         this.addEvents({
62189             /**
62190              * @event beforeedit
62191              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62192              * <ul style="padding:5px;padding-left:16px;">
62193              * <li>grid - This grid</li>
62194              * <li>record - The record being edited</li>
62195              * <li>field - The field name being edited</li>
62196              * <li>value - The value for the field being edited.</li>
62197              * <li>row - The grid row index</li>
62198              * <li>column - The grid column index</li>
62199              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62200              * </ul>
62201              * @param {Object} e An edit event (see above for description)
62202              */
62203             "beforeedit" : true,
62204             /**
62205              * @event afteredit
62206              * Fires after a cell is edited. <br />
62207              * <ul style="padding:5px;padding-left:16px;">
62208              * <li>grid - This grid</li>
62209              * <li>record - The record being edited</li>
62210              * <li>field - The field name being edited</li>
62211              * <li>value - The value being set</li>
62212              * <li>originalValue - The original value for the field, before the edit.</li>
62213              * <li>row - The grid row index</li>
62214              * <li>column - The grid column index</li>
62215              * </ul>
62216              * @param {Object} e An edit event (see above for description)
62217              */
62218             "afteredit" : true,
62219             /**
62220              * @event validateedit
62221              * Fires after a cell is edited, but before the value is set in the record. 
62222          * You can use this to modify the value being set in the field, Return false
62223              * to cancel the change. The edit event object has the following properties <br />
62224              * <ul style="padding:5px;padding-left:16px;">
62225          * <li>editor - This editor</li>
62226              * <li>grid - This grid</li>
62227              * <li>record - The record being edited</li>
62228              * <li>field - The field name being edited</li>
62229              * <li>value - The value being set</li>
62230              * <li>originalValue - The original value for the field, before the edit.</li>
62231              * <li>row - The grid row index</li>
62232              * <li>column - The grid column index</li>
62233              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62234              * </ul>
62235              * @param {Object} e An edit event (see above for description)
62236              */
62237             "validateedit" : true
62238         });
62239     this.on("bodyscroll", this.stopEditing,  this);
62240     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62241 };
62242
62243 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62244     /**
62245      * @cfg {Number} clicksToEdit
62246      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62247      */
62248     clicksToEdit: 2,
62249
62250     // private
62251     isEditor : true,
62252     // private
62253     trackMouseOver: false, // causes very odd FF errors
62254
62255     onCellDblClick : function(g, row, col){
62256         this.startEditing(row, col);
62257     },
62258
62259     onEditComplete : function(ed, value, startValue){
62260         this.editing = false;
62261         this.activeEditor = null;
62262         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62263         var r = ed.record;
62264         var field = this.colModel.getDataIndex(ed.col);
62265         var e = {
62266             grid: this,
62267             record: r,
62268             field: field,
62269             originalValue: startValue,
62270             value: value,
62271             row: ed.row,
62272             column: ed.col,
62273             cancel:false,
62274             editor: ed
62275         };
62276         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62277         cell.show();
62278           
62279         if(String(value) !== String(startValue)){
62280             
62281             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62282                 r.set(field, e.value);
62283                 // if we are dealing with a combo box..
62284                 // then we also set the 'name' colum to be the displayField
62285                 if (ed.field.displayField && ed.field.name) {
62286                     r.set(ed.field.name, ed.field.el.dom.value);
62287                 }
62288                 
62289                 delete e.cancel; //?? why!!!
62290                 this.fireEvent("afteredit", e);
62291             }
62292         } else {
62293             this.fireEvent("afteredit", e); // always fire it!
62294         }
62295         this.view.focusCell(ed.row, ed.col);
62296     },
62297
62298     /**
62299      * Starts editing the specified for the specified row/column
62300      * @param {Number} rowIndex
62301      * @param {Number} colIndex
62302      */
62303     startEditing : function(row, col){
62304         this.stopEditing();
62305         if(this.colModel.isCellEditable(col, row)){
62306             this.view.ensureVisible(row, col, true);
62307           
62308             var r = this.dataSource.getAt(row);
62309             var field = this.colModel.getDataIndex(col);
62310             var cell = Roo.get(this.view.getCell(row,col));
62311             var e = {
62312                 grid: this,
62313                 record: r,
62314                 field: field,
62315                 value: r.data[field],
62316                 row: row,
62317                 column: col,
62318                 cancel:false 
62319             };
62320             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62321                 this.editing = true;
62322                 var ed = this.colModel.getCellEditor(col, row);
62323                 
62324                 if (!ed) {
62325                     return;
62326                 }
62327                 if(!ed.rendered){
62328                     ed.render(ed.parentEl || document.body);
62329                 }
62330                 ed.field.reset();
62331                
62332                 cell.hide();
62333                 
62334                 (function(){ // complex but required for focus issues in safari, ie and opera
62335                     ed.row = row;
62336                     ed.col = col;
62337                     ed.record = r;
62338                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62339                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62340                     this.activeEditor = ed;
62341                     var v = r.data[field];
62342                     ed.startEdit(this.view.getCell(row, col), v);
62343                     // combo's with 'displayField and name set
62344                     if (ed.field.displayField && ed.field.name) {
62345                         ed.field.el.dom.value = r.data[ed.field.name];
62346                     }
62347                     
62348                     
62349                 }).defer(50, this);
62350             }
62351         }
62352     },
62353         
62354     /**
62355      * Stops any active editing
62356      */
62357     stopEditing : function(){
62358         if(this.activeEditor){
62359             this.activeEditor.completeEdit();
62360         }
62361         this.activeEditor = null;
62362     },
62363         
62364          /**
62365      * Called to get grid's drag proxy text, by default returns this.ddText.
62366      * @return {String}
62367      */
62368     getDragDropText : function(){
62369         var count = this.selModel.getSelectedCell() ? 1 : 0;
62370         return String.format(this.ddText, count, count == 1 ? '' : 's');
62371     }
62372         
62373 });/*
62374  * Based on:
62375  * Ext JS Library 1.1.1
62376  * Copyright(c) 2006-2007, Ext JS, LLC.
62377  *
62378  * Originally Released Under LGPL - original licence link has changed is not relivant.
62379  *
62380  * Fork - LGPL
62381  * <script type="text/javascript">
62382  */
62383
62384 // private - not really -- you end up using it !
62385 // This is a support class used internally by the Grid components
62386
62387 /**
62388  * @class Roo.grid.GridEditor
62389  * @extends Roo.Editor
62390  * Class for creating and editable grid elements.
62391  * @param {Object} config any settings (must include field)
62392  */
62393 Roo.grid.GridEditor = function(field, config){
62394     if (!config && field.field) {
62395         config = field;
62396         field = Roo.factory(config.field, Roo.form);
62397     }
62398     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62399     field.monitorTab = false;
62400 };
62401
62402 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62403     
62404     /**
62405      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62406      */
62407     
62408     alignment: "tl-tl",
62409     autoSize: "width",
62410     hideEl : false,
62411     cls: "x-small-editor x-grid-editor",
62412     shim:false,
62413     shadow:"frame"
62414 });/*
62415  * Based on:
62416  * Ext JS Library 1.1.1
62417  * Copyright(c) 2006-2007, Ext JS, LLC.
62418  *
62419  * Originally Released Under LGPL - original licence link has changed is not relivant.
62420  *
62421  * Fork - LGPL
62422  * <script type="text/javascript">
62423  */
62424   
62425
62426   
62427 Roo.grid.PropertyRecord = Roo.data.Record.create([
62428     {name:'name',type:'string'},  'value'
62429 ]);
62430
62431
62432 Roo.grid.PropertyStore = function(grid, source){
62433     this.grid = grid;
62434     this.store = new Roo.data.Store({
62435         recordType : Roo.grid.PropertyRecord
62436     });
62437     this.store.on('update', this.onUpdate,  this);
62438     if(source){
62439         this.setSource(source);
62440     }
62441     Roo.grid.PropertyStore.superclass.constructor.call(this);
62442 };
62443
62444
62445
62446 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62447     setSource : function(o){
62448         this.source = o;
62449         this.store.removeAll();
62450         var data = [];
62451         for(var k in o){
62452             if(this.isEditableValue(o[k])){
62453                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62454             }
62455         }
62456         this.store.loadRecords({records: data}, {}, true);
62457     },
62458
62459     onUpdate : function(ds, record, type){
62460         if(type == Roo.data.Record.EDIT){
62461             var v = record.data['value'];
62462             var oldValue = record.modified['value'];
62463             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62464                 this.source[record.id] = v;
62465                 record.commit();
62466                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62467             }else{
62468                 record.reject();
62469             }
62470         }
62471     },
62472
62473     getProperty : function(row){
62474        return this.store.getAt(row);
62475     },
62476
62477     isEditableValue: function(val){
62478         if(val && val instanceof Date){
62479             return true;
62480         }else if(typeof val == 'object' || typeof val == 'function'){
62481             return false;
62482         }
62483         return true;
62484     },
62485
62486     setValue : function(prop, value){
62487         this.source[prop] = value;
62488         this.store.getById(prop).set('value', value);
62489     },
62490
62491     getSource : function(){
62492         return this.source;
62493     }
62494 });
62495
62496 Roo.grid.PropertyColumnModel = function(grid, store){
62497     this.grid = grid;
62498     var g = Roo.grid;
62499     g.PropertyColumnModel.superclass.constructor.call(this, [
62500         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62501         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62502     ]);
62503     this.store = store;
62504     this.bselect = Roo.DomHelper.append(document.body, {
62505         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62506             {tag: 'option', value: 'true', html: 'true'},
62507             {tag: 'option', value: 'false', html: 'false'}
62508         ]
62509     });
62510     Roo.id(this.bselect);
62511     var f = Roo.form;
62512     this.editors = {
62513         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62514         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62515         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62516         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62517         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62518     };
62519     this.renderCellDelegate = this.renderCell.createDelegate(this);
62520     this.renderPropDelegate = this.renderProp.createDelegate(this);
62521 };
62522
62523 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62524     
62525     
62526     nameText : 'Name',
62527     valueText : 'Value',
62528     
62529     dateFormat : 'm/j/Y',
62530     
62531     
62532     renderDate : function(dateVal){
62533         return dateVal.dateFormat(this.dateFormat);
62534     },
62535
62536     renderBool : function(bVal){
62537         return bVal ? 'true' : 'false';
62538     },
62539
62540     isCellEditable : function(colIndex, rowIndex){
62541         return colIndex == 1;
62542     },
62543
62544     getRenderer : function(col){
62545         return col == 1 ?
62546             this.renderCellDelegate : this.renderPropDelegate;
62547     },
62548
62549     renderProp : function(v){
62550         return this.getPropertyName(v);
62551     },
62552
62553     renderCell : function(val){
62554         var rv = val;
62555         if(val instanceof Date){
62556             rv = this.renderDate(val);
62557         }else if(typeof val == 'boolean'){
62558             rv = this.renderBool(val);
62559         }
62560         return Roo.util.Format.htmlEncode(rv);
62561     },
62562
62563     getPropertyName : function(name){
62564         var pn = this.grid.propertyNames;
62565         return pn && pn[name] ? pn[name] : name;
62566     },
62567
62568     getCellEditor : function(colIndex, rowIndex){
62569         var p = this.store.getProperty(rowIndex);
62570         var n = p.data['name'], val = p.data['value'];
62571         
62572         if(typeof(this.grid.customEditors[n]) == 'string'){
62573             return this.editors[this.grid.customEditors[n]];
62574         }
62575         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62576             return this.grid.customEditors[n];
62577         }
62578         if(val instanceof Date){
62579             return this.editors['date'];
62580         }else if(typeof val == 'number'){
62581             return this.editors['number'];
62582         }else if(typeof val == 'boolean'){
62583             return this.editors['boolean'];
62584         }else{
62585             return this.editors['string'];
62586         }
62587     }
62588 });
62589
62590 /**
62591  * @class Roo.grid.PropertyGrid
62592  * @extends Roo.grid.EditorGrid
62593  * This class represents the  interface of a component based property grid control.
62594  * <br><br>Usage:<pre><code>
62595  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62596       
62597  });
62598  // set any options
62599  grid.render();
62600  * </code></pre>
62601   
62602  * @constructor
62603  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62604  * The container MUST have some type of size defined for the grid to fill. The container will be
62605  * automatically set to position relative if it isn't already.
62606  * @param {Object} config A config object that sets properties on this grid.
62607  */
62608 Roo.grid.PropertyGrid = function(container, config){
62609     config = config || {};
62610     var store = new Roo.grid.PropertyStore(this);
62611     this.store = store;
62612     var cm = new Roo.grid.PropertyColumnModel(this, store);
62613     store.store.sort('name', 'ASC');
62614     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62615         ds: store.store,
62616         cm: cm,
62617         enableColLock:false,
62618         enableColumnMove:false,
62619         stripeRows:false,
62620         trackMouseOver: false,
62621         clicksToEdit:1
62622     }, config));
62623     this.getGridEl().addClass('x-props-grid');
62624     this.lastEditRow = null;
62625     this.on('columnresize', this.onColumnResize, this);
62626     this.addEvents({
62627          /**
62628              * @event beforepropertychange
62629              * Fires before a property changes (return false to stop?)
62630              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62631              * @param {String} id Record Id
62632              * @param {String} newval New Value
62633          * @param {String} oldval Old Value
62634              */
62635         "beforepropertychange": true,
62636         /**
62637              * @event propertychange
62638              * Fires after a property changes
62639              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62640              * @param {String} id Record Id
62641              * @param {String} newval New Value
62642          * @param {String} oldval Old Value
62643              */
62644         "propertychange": true
62645     });
62646     this.customEditors = this.customEditors || {};
62647 };
62648 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62649     
62650      /**
62651      * @cfg {Object} customEditors map of colnames=> custom editors.
62652      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62653      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62654      * false disables editing of the field.
62655          */
62656     
62657       /**
62658      * @cfg {Object} propertyNames map of property Names to their displayed value
62659          */
62660     
62661     render : function(){
62662         Roo.grid.PropertyGrid.superclass.render.call(this);
62663         this.autoSize.defer(100, this);
62664     },
62665
62666     autoSize : function(){
62667         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62668         if(this.view){
62669             this.view.fitColumns();
62670         }
62671     },
62672
62673     onColumnResize : function(){
62674         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62675         this.autoSize();
62676     },
62677     /**
62678      * Sets the data for the Grid
62679      * accepts a Key => Value object of all the elements avaiable.
62680      * @param {Object} data  to appear in grid.
62681      */
62682     setSource : function(source){
62683         this.store.setSource(source);
62684         //this.autoSize();
62685     },
62686     /**
62687      * Gets all the data from the grid.
62688      * @return {Object} data  data stored in grid
62689      */
62690     getSource : function(){
62691         return this.store.getSource();
62692     }
62693 });/*
62694   
62695  * Licence LGPL
62696  
62697  */
62698  
62699 /**
62700  * @class Roo.grid.Calendar
62701  * @extends Roo.grid.Grid
62702  * This class extends the Grid to provide a calendar widget
62703  * <br><br>Usage:<pre><code>
62704  var grid = new Roo.grid.Calendar("my-container-id", {
62705      ds: myDataStore,
62706      cm: myColModel,
62707      selModel: mySelectionModel,
62708      autoSizeColumns: true,
62709      monitorWindowResize: false,
62710      trackMouseOver: true
62711      eventstore : real data store..
62712  });
62713  // set any options
62714  grid.render();
62715   
62716   * @constructor
62717  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62718  * The container MUST have some type of size defined for the grid to fill. The container will be
62719  * automatically set to position relative if it isn't already.
62720  * @param {Object} config A config object that sets properties on this grid.
62721  */
62722 Roo.grid.Calendar = function(container, config){
62723         // initialize the container
62724         this.container = Roo.get(container);
62725         this.container.update("");
62726         this.container.setStyle("overflow", "hidden");
62727     this.container.addClass('x-grid-container');
62728
62729     this.id = this.container.id;
62730
62731     Roo.apply(this, config);
62732     // check and correct shorthanded configs
62733     
62734     var rows = [];
62735     var d =1;
62736     for (var r = 0;r < 6;r++) {
62737         
62738         rows[r]=[];
62739         for (var c =0;c < 7;c++) {
62740             rows[r][c]= '';
62741         }
62742     }
62743     if (this.eventStore) {
62744         this.eventStore= Roo.factory(this.eventStore, Roo.data);
62745         this.eventStore.on('load',this.onLoad, this);
62746         this.eventStore.on('beforeload',this.clearEvents, this);
62747          
62748     }
62749     
62750     this.dataSource = new Roo.data.Store({
62751             proxy: new Roo.data.MemoryProxy(rows),
62752             reader: new Roo.data.ArrayReader({}, [
62753                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
62754     });
62755
62756     this.dataSource.load();
62757     this.ds = this.dataSource;
62758     this.ds.xmodule = this.xmodule || false;
62759     
62760     
62761     var cellRender = function(v,x,r)
62762     {
62763         return String.format(
62764             '<div class="fc-day  fc-widget-content"><div>' +
62765                 '<div class="fc-event-container"></div>' +
62766                 '<div class="fc-day-number">{0}</div>'+
62767                 
62768                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
62769             '</div></div>', v);
62770     
62771     }
62772     
62773     
62774     this.colModel = new Roo.grid.ColumnModel( [
62775         {
62776             xtype: 'ColumnModel',
62777             xns: Roo.grid,
62778             dataIndex : 'weekday0',
62779             header : 'Sunday',
62780             renderer : cellRender
62781         },
62782         {
62783             xtype: 'ColumnModel',
62784             xns: Roo.grid,
62785             dataIndex : 'weekday1',
62786             header : 'Monday',
62787             renderer : cellRender
62788         },
62789         {
62790             xtype: 'ColumnModel',
62791             xns: Roo.grid,
62792             dataIndex : 'weekday2',
62793             header : 'Tuesday',
62794             renderer : cellRender
62795         },
62796         {
62797             xtype: 'ColumnModel',
62798             xns: Roo.grid,
62799             dataIndex : 'weekday3',
62800             header : 'Wednesday',
62801             renderer : cellRender
62802         },
62803         {
62804             xtype: 'ColumnModel',
62805             xns: Roo.grid,
62806             dataIndex : 'weekday4',
62807             header : 'Thursday',
62808             renderer : cellRender
62809         },
62810         {
62811             xtype: 'ColumnModel',
62812             xns: Roo.grid,
62813             dataIndex : 'weekday5',
62814             header : 'Friday',
62815             renderer : cellRender
62816         },
62817         {
62818             xtype: 'ColumnModel',
62819             xns: Roo.grid,
62820             dataIndex : 'weekday6',
62821             header : 'Saturday',
62822             renderer : cellRender
62823         }
62824     ]);
62825     this.cm = this.colModel;
62826     this.cm.xmodule = this.xmodule || false;
62827  
62828         
62829           
62830     //this.selModel = new Roo.grid.CellSelectionModel();
62831     //this.sm = this.selModel;
62832     //this.selModel.init(this);
62833     
62834     
62835     if(this.width){
62836         this.container.setWidth(this.width);
62837     }
62838
62839     if(this.height){
62840         this.container.setHeight(this.height);
62841     }
62842     /** @private */
62843         this.addEvents({
62844         // raw events
62845         /**
62846          * @event click
62847          * The raw click event for the entire grid.
62848          * @param {Roo.EventObject} e
62849          */
62850         "click" : true,
62851         /**
62852          * @event dblclick
62853          * The raw dblclick event for the entire grid.
62854          * @param {Roo.EventObject} e
62855          */
62856         "dblclick" : true,
62857         /**
62858          * @event contextmenu
62859          * The raw contextmenu event for the entire grid.
62860          * @param {Roo.EventObject} e
62861          */
62862         "contextmenu" : true,
62863         /**
62864          * @event mousedown
62865          * The raw mousedown event for the entire grid.
62866          * @param {Roo.EventObject} e
62867          */
62868         "mousedown" : true,
62869         /**
62870          * @event mouseup
62871          * The raw mouseup event for the entire grid.
62872          * @param {Roo.EventObject} e
62873          */
62874         "mouseup" : true,
62875         /**
62876          * @event mouseover
62877          * The raw mouseover event for the entire grid.
62878          * @param {Roo.EventObject} e
62879          */
62880         "mouseover" : true,
62881         /**
62882          * @event mouseout
62883          * The raw mouseout event for the entire grid.
62884          * @param {Roo.EventObject} e
62885          */
62886         "mouseout" : true,
62887         /**
62888          * @event keypress
62889          * The raw keypress event for the entire grid.
62890          * @param {Roo.EventObject} e
62891          */
62892         "keypress" : true,
62893         /**
62894          * @event keydown
62895          * The raw keydown event for the entire grid.
62896          * @param {Roo.EventObject} e
62897          */
62898         "keydown" : true,
62899
62900         // custom events
62901
62902         /**
62903          * @event cellclick
62904          * Fires when a cell is clicked
62905          * @param {Grid} this
62906          * @param {Number} rowIndex
62907          * @param {Number} columnIndex
62908          * @param {Roo.EventObject} e
62909          */
62910         "cellclick" : true,
62911         /**
62912          * @event celldblclick
62913          * Fires when a cell is double clicked
62914          * @param {Grid} this
62915          * @param {Number} rowIndex
62916          * @param {Number} columnIndex
62917          * @param {Roo.EventObject} e
62918          */
62919         "celldblclick" : true,
62920         /**
62921          * @event rowclick
62922          * Fires when a row is clicked
62923          * @param {Grid} this
62924          * @param {Number} rowIndex
62925          * @param {Roo.EventObject} e
62926          */
62927         "rowclick" : true,
62928         /**
62929          * @event rowdblclick
62930          * Fires when a row is double clicked
62931          * @param {Grid} this
62932          * @param {Number} rowIndex
62933          * @param {Roo.EventObject} e
62934          */
62935         "rowdblclick" : true,
62936         /**
62937          * @event headerclick
62938          * Fires when a header is clicked
62939          * @param {Grid} this
62940          * @param {Number} columnIndex
62941          * @param {Roo.EventObject} e
62942          */
62943         "headerclick" : true,
62944         /**
62945          * @event headerdblclick
62946          * Fires when a header cell is double clicked
62947          * @param {Grid} this
62948          * @param {Number} columnIndex
62949          * @param {Roo.EventObject} e
62950          */
62951         "headerdblclick" : true,
62952         /**
62953          * @event rowcontextmenu
62954          * Fires when a row is right clicked
62955          * @param {Grid} this
62956          * @param {Number} rowIndex
62957          * @param {Roo.EventObject} e
62958          */
62959         "rowcontextmenu" : true,
62960         /**
62961          * @event cellcontextmenu
62962          * Fires when a cell is right clicked
62963          * @param {Grid} this
62964          * @param {Number} rowIndex
62965          * @param {Number} cellIndex
62966          * @param {Roo.EventObject} e
62967          */
62968          "cellcontextmenu" : true,
62969         /**
62970          * @event headercontextmenu
62971          * Fires when a header is right clicked
62972          * @param {Grid} this
62973          * @param {Number} columnIndex
62974          * @param {Roo.EventObject} e
62975          */
62976         "headercontextmenu" : true,
62977         /**
62978          * @event bodyscroll
62979          * Fires when the body element is scrolled
62980          * @param {Number} scrollLeft
62981          * @param {Number} scrollTop
62982          */
62983         "bodyscroll" : true,
62984         /**
62985          * @event columnresize
62986          * Fires when the user resizes a column
62987          * @param {Number} columnIndex
62988          * @param {Number} newSize
62989          */
62990         "columnresize" : true,
62991         /**
62992          * @event columnmove
62993          * Fires when the user moves a column
62994          * @param {Number} oldIndex
62995          * @param {Number} newIndex
62996          */
62997         "columnmove" : true,
62998         /**
62999          * @event startdrag
63000          * Fires when row(s) start being dragged
63001          * @param {Grid} this
63002          * @param {Roo.GridDD} dd The drag drop object
63003          * @param {event} e The raw browser event
63004          */
63005         "startdrag" : true,
63006         /**
63007          * @event enddrag
63008          * Fires when a drag operation is complete
63009          * @param {Grid} this
63010          * @param {Roo.GridDD} dd The drag drop object
63011          * @param {event} e The raw browser event
63012          */
63013         "enddrag" : true,
63014         /**
63015          * @event dragdrop
63016          * Fires when dragged row(s) are dropped on a valid DD target
63017          * @param {Grid} this
63018          * @param {Roo.GridDD} dd The drag drop object
63019          * @param {String} targetId The target drag drop object
63020          * @param {event} e The raw browser event
63021          */
63022         "dragdrop" : true,
63023         /**
63024          * @event dragover
63025          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63026          * @param {Grid} this
63027          * @param {Roo.GridDD} dd The drag drop object
63028          * @param {String} targetId The target drag drop object
63029          * @param {event} e The raw browser event
63030          */
63031         "dragover" : true,
63032         /**
63033          * @event dragenter
63034          *  Fires when the dragged row(s) first cross another DD target while being dragged
63035          * @param {Grid} this
63036          * @param {Roo.GridDD} dd The drag drop object
63037          * @param {String} targetId The target drag drop object
63038          * @param {event} e The raw browser event
63039          */
63040         "dragenter" : true,
63041         /**
63042          * @event dragout
63043          * Fires when the dragged row(s) leave another DD target while being dragged
63044          * @param {Grid} this
63045          * @param {Roo.GridDD} dd The drag drop object
63046          * @param {String} targetId The target drag drop object
63047          * @param {event} e The raw browser event
63048          */
63049         "dragout" : true,
63050         /**
63051          * @event rowclass
63052          * Fires when a row is rendered, so you can change add a style to it.
63053          * @param {GridView} gridview   The grid view
63054          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63055          */
63056         'rowclass' : true,
63057
63058         /**
63059          * @event render
63060          * Fires when the grid is rendered
63061          * @param {Grid} grid
63062          */
63063         'render' : true,
63064             /**
63065              * @event select
63066              * Fires when a date is selected
63067              * @param {DatePicker} this
63068              * @param {Date} date The selected date
63069              */
63070         'select': true,
63071         /**
63072              * @event monthchange
63073              * Fires when the displayed month changes 
63074              * @param {DatePicker} this
63075              * @param {Date} date The selected month
63076              */
63077         'monthchange': true,
63078         /**
63079              * @event evententer
63080              * Fires when mouse over an event
63081              * @param {Calendar} this
63082              * @param {event} Event
63083              */
63084         'evententer': true,
63085         /**
63086              * @event eventleave
63087              * Fires when the mouse leaves an
63088              * @param {Calendar} this
63089              * @param {event}
63090              */
63091         'eventleave': true,
63092         /**
63093              * @event eventclick
63094              * Fires when the mouse click an
63095              * @param {Calendar} this
63096              * @param {event}
63097              */
63098         'eventclick': true,
63099         /**
63100              * @event eventrender
63101              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63102              * @param {Calendar} this
63103              * @param {data} data to be modified
63104              */
63105         'eventrender': true
63106         
63107     });
63108
63109     Roo.grid.Grid.superclass.constructor.call(this);
63110     this.on('render', function() {
63111         this.view.el.addClass('x-grid-cal'); 
63112         
63113         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63114
63115     },this);
63116     
63117     if (!Roo.grid.Calendar.style) {
63118         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63119             
63120             
63121             '.x-grid-cal .x-grid-col' :  {
63122                 height: 'auto !important',
63123                 'vertical-align': 'top'
63124             },
63125             '.x-grid-cal  .fc-event-hori' : {
63126                 height: '14px'
63127             }
63128              
63129             
63130         }, Roo.id());
63131     }
63132
63133     
63134     
63135 };
63136 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63137     /**
63138      * @cfg {Store} eventStore The store that loads events.
63139      */
63140     eventStore : 25,
63141
63142      
63143     activeDate : false,
63144     startDay : 0,
63145     autoWidth : true,
63146     monitorWindowResize : false,
63147
63148     
63149     resizeColumns : function() {
63150         var col = (this.view.el.getWidth() / 7) - 3;
63151         // loop through cols, and setWidth
63152         for(var i =0 ; i < 7 ; i++){
63153             this.cm.setColumnWidth(i, col);
63154         }
63155     },
63156      setDate :function(date) {
63157         
63158         Roo.log('setDate?');
63159         
63160         this.resizeColumns();
63161         var vd = this.activeDate;
63162         this.activeDate = date;
63163 //        if(vd && this.el){
63164 //            var t = date.getTime();
63165 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63166 //                Roo.log('using add remove');
63167 //                
63168 //                this.fireEvent('monthchange', this, date);
63169 //                
63170 //                this.cells.removeClass("fc-state-highlight");
63171 //                this.cells.each(function(c){
63172 //                   if(c.dateValue == t){
63173 //                       c.addClass("fc-state-highlight");
63174 //                       setTimeout(function(){
63175 //                            try{c.dom.firstChild.focus();}catch(e){}
63176 //                       }, 50);
63177 //                       return false;
63178 //                   }
63179 //                   return true;
63180 //                });
63181 //                return;
63182 //            }
63183 //        }
63184         
63185         var days = date.getDaysInMonth();
63186         
63187         var firstOfMonth = date.getFirstDateOfMonth();
63188         var startingPos = firstOfMonth.getDay()-this.startDay;
63189         
63190         if(startingPos < this.startDay){
63191             startingPos += 7;
63192         }
63193         
63194         var pm = date.add(Date.MONTH, -1);
63195         var prevStart = pm.getDaysInMonth()-startingPos;
63196 //        
63197         
63198         
63199         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63200         
63201         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63202         //this.cells.addClassOnOver('fc-state-hover');
63203         
63204         var cells = this.cells.elements;
63205         var textEls = this.textNodes;
63206         
63207         //Roo.each(cells, function(cell){
63208         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63209         //});
63210         
63211         days += startingPos;
63212
63213         // convert everything to numbers so it's fast
63214         var day = 86400000;
63215         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63216         //Roo.log(d);
63217         //Roo.log(pm);
63218         //Roo.log(prevStart);
63219         
63220         var today = new Date().clearTime().getTime();
63221         var sel = date.clearTime().getTime();
63222         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63223         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63224         var ddMatch = this.disabledDatesRE;
63225         var ddText = this.disabledDatesText;
63226         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63227         var ddaysText = this.disabledDaysText;
63228         var format = this.format;
63229         
63230         var setCellClass = function(cal, cell){
63231             
63232             //Roo.log('set Cell Class');
63233             cell.title = "";
63234             var t = d.getTime();
63235             
63236             //Roo.log(d);
63237             
63238             
63239             cell.dateValue = t;
63240             if(t == today){
63241                 cell.className += " fc-today";
63242                 cell.className += " fc-state-highlight";
63243                 cell.title = cal.todayText;
63244             }
63245             if(t == sel){
63246                 // disable highlight in other month..
63247                 cell.className += " fc-state-highlight";
63248                 
63249             }
63250             // disabling
63251             if(t < min) {
63252                 //cell.className = " fc-state-disabled";
63253                 cell.title = cal.minText;
63254                 return;
63255             }
63256             if(t > max) {
63257                 //cell.className = " fc-state-disabled";
63258                 cell.title = cal.maxText;
63259                 return;
63260             }
63261             if(ddays){
63262                 if(ddays.indexOf(d.getDay()) != -1){
63263                     // cell.title = ddaysText;
63264                    // cell.className = " fc-state-disabled";
63265                 }
63266             }
63267             if(ddMatch && format){
63268                 var fvalue = d.dateFormat(format);
63269                 if(ddMatch.test(fvalue)){
63270                     cell.title = ddText.replace("%0", fvalue);
63271                    cell.className = " fc-state-disabled";
63272                 }
63273             }
63274             
63275             if (!cell.initialClassName) {
63276                 cell.initialClassName = cell.dom.className;
63277             }
63278             
63279             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63280         };
63281
63282         var i = 0;
63283         
63284         for(; i < startingPos; i++) {
63285             cells[i].dayName =  (++prevStart);
63286             Roo.log(textEls[i]);
63287             d.setDate(d.getDate()+1);
63288             
63289             //cells[i].className = "fc-past fc-other-month";
63290             setCellClass(this, cells[i]);
63291         }
63292         
63293         var intDay = 0;
63294         
63295         for(; i < days; i++){
63296             intDay = i - startingPos + 1;
63297             cells[i].dayName =  (intDay);
63298             d.setDate(d.getDate()+1);
63299             
63300             cells[i].className = ''; // "x-date-active";
63301             setCellClass(this, cells[i]);
63302         }
63303         var extraDays = 0;
63304         
63305         for(; i < 42; i++) {
63306             //textEls[i].innerHTML = (++extraDays);
63307             
63308             d.setDate(d.getDate()+1);
63309             cells[i].dayName = (++extraDays);
63310             cells[i].className = "fc-future fc-other-month";
63311             setCellClass(this, cells[i]);
63312         }
63313         
63314         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63315         
63316         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63317         
63318         // this will cause all the cells to mis
63319         var rows= [];
63320         var i =0;
63321         for (var r = 0;r < 6;r++) {
63322             for (var c =0;c < 7;c++) {
63323                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63324             }    
63325         }
63326         
63327         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63328         for(i=0;i<cells.length;i++) {
63329             
63330             this.cells.elements[i].dayName = cells[i].dayName ;
63331             this.cells.elements[i].className = cells[i].className;
63332             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63333             this.cells.elements[i].title = cells[i].title ;
63334             this.cells.elements[i].dateValue = cells[i].dateValue ;
63335         }
63336         
63337         
63338         
63339         
63340         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63341         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63342         
63343         ////if(totalRows != 6){
63344             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63345            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63346        // }
63347         
63348         this.fireEvent('monthchange', this, date);
63349         
63350         
63351     },
63352  /**
63353      * Returns the grid's SelectionModel.
63354      * @return {SelectionModel}
63355      */
63356     getSelectionModel : function(){
63357         if(!this.selModel){
63358             this.selModel = new Roo.grid.CellSelectionModel();
63359         }
63360         return this.selModel;
63361     },
63362
63363     load: function() {
63364         this.eventStore.load()
63365         
63366         
63367         
63368     },
63369     
63370     findCell : function(dt) {
63371         dt = dt.clearTime().getTime();
63372         var ret = false;
63373         this.cells.each(function(c){
63374             //Roo.log("check " +c.dateValue + '?=' + dt);
63375             if(c.dateValue == dt){
63376                 ret = c;
63377                 return false;
63378             }
63379             return true;
63380         });
63381         
63382         return ret;
63383     },
63384     
63385     findCells : function(rec) {
63386         var s = rec.data.start_dt.clone().clearTime().getTime();
63387        // Roo.log(s);
63388         var e= rec.data.end_dt.clone().clearTime().getTime();
63389        // Roo.log(e);
63390         var ret = [];
63391         this.cells.each(function(c){
63392              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63393             
63394             if(c.dateValue > e){
63395                 return ;
63396             }
63397             if(c.dateValue < s){
63398                 return ;
63399             }
63400             ret.push(c);
63401         });
63402         
63403         return ret;    
63404     },
63405     
63406     findBestRow: function(cells)
63407     {
63408         var ret = 0;
63409         
63410         for (var i =0 ; i < cells.length;i++) {
63411             ret  = Math.max(cells[i].rows || 0,ret);
63412         }
63413         return ret;
63414         
63415     },
63416     
63417     
63418     addItem : function(rec)
63419     {
63420         // look for vertical location slot in
63421         var cells = this.findCells(rec);
63422         
63423         rec.row = this.findBestRow(cells);
63424         
63425         // work out the location.
63426         
63427         var crow = false;
63428         var rows = [];
63429         for(var i =0; i < cells.length; i++) {
63430             if (!crow) {
63431                 crow = {
63432                     start : cells[i],
63433                     end :  cells[i]
63434                 };
63435                 continue;
63436             }
63437             if (crow.start.getY() == cells[i].getY()) {
63438                 // on same row.
63439                 crow.end = cells[i];
63440                 continue;
63441             }
63442             // different row.
63443             rows.push(crow);
63444             crow = {
63445                 start: cells[i],
63446                 end : cells[i]
63447             };
63448             
63449         }
63450         
63451         rows.push(crow);
63452         rec.els = [];
63453         rec.rows = rows;
63454         rec.cells = cells;
63455         for (var i = 0; i < cells.length;i++) {
63456             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63457             
63458         }
63459         
63460         
63461     },
63462     
63463     clearEvents: function() {
63464         
63465         if (!this.eventStore.getCount()) {
63466             return;
63467         }
63468         // reset number of rows in cells.
63469         Roo.each(this.cells.elements, function(c){
63470             c.rows = 0;
63471         });
63472         
63473         this.eventStore.each(function(e) {
63474             this.clearEvent(e);
63475         },this);
63476         
63477     },
63478     
63479     clearEvent : function(ev)
63480     {
63481         if (ev.els) {
63482             Roo.each(ev.els, function(el) {
63483                 el.un('mouseenter' ,this.onEventEnter, this);
63484                 el.un('mouseleave' ,this.onEventLeave, this);
63485                 el.remove();
63486             },this);
63487             ev.els = [];
63488         }
63489     },
63490     
63491     
63492     renderEvent : function(ev,ctr) {
63493         if (!ctr) {
63494              ctr = this.view.el.select('.fc-event-container',true).first();
63495         }
63496         
63497          
63498         this.clearEvent(ev);
63499             //code
63500        
63501         
63502         
63503         ev.els = [];
63504         var cells = ev.cells;
63505         var rows = ev.rows;
63506         this.fireEvent('eventrender', this, ev);
63507         
63508         for(var i =0; i < rows.length; i++) {
63509             
63510             cls = '';
63511             if (i == 0) {
63512                 cls += ' fc-event-start';
63513             }
63514             if ((i+1) == rows.length) {
63515                 cls += ' fc-event-end';
63516             }
63517             
63518             //Roo.log(ev.data);
63519             // how many rows should it span..
63520             var cg = this.eventTmpl.append(ctr,Roo.apply({
63521                 fccls : cls
63522                 
63523             }, ev.data) , true);
63524             
63525             
63526             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63527             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63528             cg.on('click', this.onEventClick, this, ev);
63529             
63530             ev.els.push(cg);
63531             
63532             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63533             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63534             //Roo.log(cg);
63535              
63536             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63537             cg.setWidth(ebox.right - sbox.x -2);
63538         }
63539     },
63540     
63541     renderEvents: function()
63542     {   
63543         // first make sure there is enough space..
63544         
63545         if (!this.eventTmpl) {
63546             this.eventTmpl = new Roo.Template(
63547                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63548                     '<div class="fc-event-inner">' +
63549                         '<span class="fc-event-time">{time}</span>' +
63550                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63551                     '</div>' +
63552                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63553                 '</div>'
63554             );
63555                 
63556         }
63557                
63558         
63559         
63560         this.cells.each(function(c) {
63561             //Roo.log(c.select('.fc-day-content div',true).first());
63562             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63563         });
63564         
63565         var ctr = this.view.el.select('.fc-event-container',true).first();
63566         
63567         var cls;
63568         this.eventStore.each(function(ev){
63569             
63570             this.renderEvent(ev);
63571              
63572              
63573         }, this);
63574         this.view.layout();
63575         
63576     },
63577     
63578     onEventEnter: function (e, el,event,d) {
63579         this.fireEvent('evententer', this, el, event);
63580     },
63581     
63582     onEventLeave: function (e, el,event,d) {
63583         this.fireEvent('eventleave', this, el, event);
63584     },
63585     
63586     onEventClick: function (e, el,event,d) {
63587         this.fireEvent('eventclick', this, el, event);
63588     },
63589     
63590     onMonthChange: function () {
63591         this.store.load();
63592     },
63593     
63594     onLoad: function () {
63595         
63596         //Roo.log('calendar onload');
63597 //         
63598         if(this.eventStore.getCount() > 0){
63599             
63600            
63601             
63602             this.eventStore.each(function(d){
63603                 
63604                 
63605                 // FIXME..
63606                 var add =   d.data;
63607                 if (typeof(add.end_dt) == 'undefined')  {
63608                     Roo.log("Missing End time in calendar data: ");
63609                     Roo.log(d);
63610                     return;
63611                 }
63612                 if (typeof(add.start_dt) == 'undefined')  {
63613                     Roo.log("Missing Start time in calendar data: ");
63614                     Roo.log(d);
63615                     return;
63616                 }
63617                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63618                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63619                 add.id = add.id || d.id;
63620                 add.title = add.title || '??';
63621                 
63622                 this.addItem(d);
63623                 
63624              
63625             },this);
63626         }
63627         
63628         this.renderEvents();
63629     }
63630     
63631
63632 });
63633 /*
63634  grid : {
63635                 xtype: 'Grid',
63636                 xns: Roo.grid,
63637                 listeners : {
63638                     render : function ()
63639                     {
63640                         _this.grid = this;
63641                         
63642                         if (!this.view.el.hasClass('course-timesheet')) {
63643                             this.view.el.addClass('course-timesheet');
63644                         }
63645                         if (this.tsStyle) {
63646                             this.ds.load({});
63647                             return; 
63648                         }
63649                         Roo.log('width');
63650                         Roo.log(_this.grid.view.el.getWidth());
63651                         
63652                         
63653                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63654                             '.course-timesheet .x-grid-row' : {
63655                                 height: '80px'
63656                             },
63657                             '.x-grid-row td' : {
63658                                 'vertical-align' : 0
63659                             },
63660                             '.course-edit-link' : {
63661                                 'color' : 'blue',
63662                                 'text-overflow' : 'ellipsis',
63663                                 'overflow' : 'hidden',
63664                                 'white-space' : 'nowrap',
63665                                 'cursor' : 'pointer'
63666                             },
63667                             '.sub-link' : {
63668                                 'color' : 'green'
63669                             },
63670                             '.de-act-sup-link' : {
63671                                 'color' : 'purple',
63672                                 'text-decoration' : 'line-through'
63673                             },
63674                             '.de-act-link' : {
63675                                 'color' : 'red',
63676                                 'text-decoration' : 'line-through'
63677                             },
63678                             '.course-timesheet .course-highlight' : {
63679                                 'border-top-style': 'dashed !important',
63680                                 'border-bottom-bottom': 'dashed !important'
63681                             },
63682                             '.course-timesheet .course-item' : {
63683                                 'font-family'   : 'tahoma, arial, helvetica',
63684                                 'font-size'     : '11px',
63685                                 'overflow'      : 'hidden',
63686                                 'padding-left'  : '10px',
63687                                 'padding-right' : '10px',
63688                                 'padding-top' : '10px' 
63689                             }
63690                             
63691                         }, Roo.id());
63692                                 this.ds.load({});
63693                     }
63694                 },
63695                 autoWidth : true,
63696                 monitorWindowResize : false,
63697                 cellrenderer : function(v,x,r)
63698                 {
63699                     return v;
63700                 },
63701                 sm : {
63702                     xtype: 'CellSelectionModel',
63703                     xns: Roo.grid
63704                 },
63705                 dataSource : {
63706                     xtype: 'Store',
63707                     xns: Roo.data,
63708                     listeners : {
63709                         beforeload : function (_self, options)
63710                         {
63711                             options.params = options.params || {};
63712                             options.params._month = _this.monthField.getValue();
63713                             options.params.limit = 9999;
63714                             options.params['sort'] = 'when_dt';    
63715                             options.params['dir'] = 'ASC';    
63716                             this.proxy.loadResponse = this.loadResponse;
63717                             Roo.log("load?");
63718                             //this.addColumns();
63719                         },
63720                         load : function (_self, records, options)
63721                         {
63722                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
63723                                 // if you click on the translation.. you can edit it...
63724                                 var el = Roo.get(this);
63725                                 var id = el.dom.getAttribute('data-id');
63726                                 var d = el.dom.getAttribute('data-date');
63727                                 var t = el.dom.getAttribute('data-time');
63728                                 //var id = this.child('span').dom.textContent;
63729                                 
63730                                 //Roo.log(this);
63731                                 Pman.Dialog.CourseCalendar.show({
63732                                     id : id,
63733                                     when_d : d,
63734                                     when_t : t,
63735                                     productitem_active : id ? 1 : 0
63736                                 }, function() {
63737                                     _this.grid.ds.load({});
63738                                 });
63739                            
63740                            });
63741                            
63742                            _this.panel.fireEvent('resize', [ '', '' ]);
63743                         }
63744                     },
63745                     loadResponse : function(o, success, response){
63746                             // this is overridden on before load..
63747                             
63748                             Roo.log("our code?");       
63749                             //Roo.log(success);
63750                             //Roo.log(response)
63751                             delete this.activeRequest;
63752                             if(!success){
63753                                 this.fireEvent("loadexception", this, o, response);
63754                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63755                                 return;
63756                             }
63757                             var result;
63758                             try {
63759                                 result = o.reader.read(response);
63760                             }catch(e){
63761                                 Roo.log("load exception?");
63762                                 this.fireEvent("loadexception", this, o, response, e);
63763                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63764                                 return;
63765                             }
63766                             Roo.log("ready...");        
63767                             // loop through result.records;
63768                             // and set this.tdate[date] = [] << array of records..
63769                             _this.tdata  = {};
63770                             Roo.each(result.records, function(r){
63771                                 //Roo.log(r.data);
63772                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
63773                                     _this.tdata[r.data.when_dt.format('j')] = [];
63774                                 }
63775                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
63776                             });
63777                             
63778                             //Roo.log(_this.tdata);
63779                             
63780                             result.records = [];
63781                             result.totalRecords = 6;
63782                     
63783                             // let's generate some duumy records for the rows.
63784                             //var st = _this.dateField.getValue();
63785                             
63786                             // work out monday..
63787                             //st = st.add(Date.DAY, -1 * st.format('w'));
63788                             
63789                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63790                             
63791                             var firstOfMonth = date.getFirstDayOfMonth();
63792                             var days = date.getDaysInMonth();
63793                             var d = 1;
63794                             var firstAdded = false;
63795                             for (var i = 0; i < result.totalRecords ; i++) {
63796                                 //var d= st.add(Date.DAY, i);
63797                                 var row = {};
63798                                 var added = 0;
63799                                 for(var w = 0 ; w < 7 ; w++){
63800                                     if(!firstAdded && firstOfMonth != w){
63801                                         continue;
63802                                     }
63803                                     if(d > days){
63804                                         continue;
63805                                     }
63806                                     firstAdded = true;
63807                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
63808                                     row['weekday'+w] = String.format(
63809                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
63810                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
63811                                                     d,
63812                                                     date.format('Y-m-')+dd
63813                                                 );
63814                                     added++;
63815                                     if(typeof(_this.tdata[d]) != 'undefined'){
63816                                         Roo.each(_this.tdata[d], function(r){
63817                                             var is_sub = '';
63818                                             var deactive = '';
63819                                             var id = r.id;
63820                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
63821                                             if(r.parent_id*1>0){
63822                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
63823                                                 id = r.parent_id;
63824                                             }
63825                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
63826                                                 deactive = 'de-act-link';
63827                                             }
63828                                             
63829                                             row['weekday'+w] += String.format(
63830                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
63831                                                     id, //0
63832                                                     r.product_id_name, //1
63833                                                     r.when_dt.format('h:ia'), //2
63834                                                     is_sub, //3
63835                                                     deactive, //4
63836                                                     desc // 5
63837                                             );
63838                                         });
63839                                     }
63840                                     d++;
63841                                 }
63842                                 
63843                                 // only do this if something added..
63844                                 if(added > 0){ 
63845                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
63846                                 }
63847                                 
63848                                 
63849                                 // push it twice. (second one with an hour..
63850                                 
63851                             }
63852                             //Roo.log(result);
63853                             this.fireEvent("load", this, o, o.request.arg);
63854                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
63855                         },
63856                     sortInfo : {field: 'when_dt', direction : 'ASC' },
63857                     proxy : {
63858                         xtype: 'HttpProxy',
63859                         xns: Roo.data,
63860                         method : 'GET',
63861                         url : baseURL + '/Roo/Shop_course.php'
63862                     },
63863                     reader : {
63864                         xtype: 'JsonReader',
63865                         xns: Roo.data,
63866                         id : 'id',
63867                         fields : [
63868                             {
63869                                 'name': 'id',
63870                                 'type': 'int'
63871                             },
63872                             {
63873                                 'name': 'when_dt',
63874                                 'type': 'string'
63875                             },
63876                             {
63877                                 'name': 'end_dt',
63878                                 'type': 'string'
63879                             },
63880                             {
63881                                 'name': 'parent_id',
63882                                 'type': 'int'
63883                             },
63884                             {
63885                                 'name': 'product_id',
63886                                 'type': 'int'
63887                             },
63888                             {
63889                                 'name': 'productitem_id',
63890                                 'type': 'int'
63891                             },
63892                             {
63893                                 'name': 'guid',
63894                                 'type': 'int'
63895                             }
63896                         ]
63897                     }
63898                 },
63899                 toolbar : {
63900                     xtype: 'Toolbar',
63901                     xns: Roo,
63902                     items : [
63903                         {
63904                             xtype: 'Button',
63905                             xns: Roo.Toolbar,
63906                             listeners : {
63907                                 click : function (_self, e)
63908                                 {
63909                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63910                                     sd.setMonth(sd.getMonth()-1);
63911                                     _this.monthField.setValue(sd.format('Y-m-d'));
63912                                     _this.grid.ds.load({});
63913                                 }
63914                             },
63915                             text : "Back"
63916                         },
63917                         {
63918                             xtype: 'Separator',
63919                             xns: Roo.Toolbar
63920                         },
63921                         {
63922                             xtype: 'MonthField',
63923                             xns: Roo.form,
63924                             listeners : {
63925                                 render : function (_self)
63926                                 {
63927                                     _this.monthField = _self;
63928                                    // _this.monthField.set  today
63929                                 },
63930                                 select : function (combo, date)
63931                                 {
63932                                     _this.grid.ds.load({});
63933                                 }
63934                             },
63935                             value : (function() { return new Date(); })()
63936                         },
63937                         {
63938                             xtype: 'Separator',
63939                             xns: Roo.Toolbar
63940                         },
63941                         {
63942                             xtype: 'TextItem',
63943                             xns: Roo.Toolbar,
63944                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
63945                         },
63946                         {
63947                             xtype: 'Fill',
63948                             xns: Roo.Toolbar
63949                         },
63950                         {
63951                             xtype: 'Button',
63952                             xns: Roo.Toolbar,
63953                             listeners : {
63954                                 click : function (_self, e)
63955                                 {
63956                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63957                                     sd.setMonth(sd.getMonth()+1);
63958                                     _this.monthField.setValue(sd.format('Y-m-d'));
63959                                     _this.grid.ds.load({});
63960                                 }
63961                             },
63962                             text : "Next"
63963                         }
63964                     ]
63965                 },
63966                  
63967             }
63968         };
63969         
63970         *//*
63971  * Based on:
63972  * Ext JS Library 1.1.1
63973  * Copyright(c) 2006-2007, Ext JS, LLC.
63974  *
63975  * Originally Released Under LGPL - original licence link has changed is not relivant.
63976  *
63977  * Fork - LGPL
63978  * <script type="text/javascript">
63979  */
63980  
63981 /**
63982  * @class Roo.LoadMask
63983  * A simple utility class for generically masking elements while loading data.  If the element being masked has
63984  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
63985  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
63986  * element's UpdateManager load indicator and will be destroyed after the initial load.
63987  * @constructor
63988  * Create a new LoadMask
63989  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
63990  * @param {Object} config The config object
63991  */
63992 Roo.LoadMask = function(el, config){
63993     this.el = Roo.get(el);
63994     Roo.apply(this, config);
63995     if(this.store){
63996         this.store.on('beforeload', this.onBeforeLoad, this);
63997         this.store.on('load', this.onLoad, this);
63998         this.store.on('loadexception', this.onLoadException, this);
63999         this.removeMask = false;
64000     }else{
64001         var um = this.el.getUpdateManager();
64002         um.showLoadIndicator = false; // disable the default indicator
64003         um.on('beforeupdate', this.onBeforeLoad, this);
64004         um.on('update', this.onLoad, this);
64005         um.on('failure', this.onLoad, this);
64006         this.removeMask = true;
64007     }
64008 };
64009
64010 Roo.LoadMask.prototype = {
64011     /**
64012      * @cfg {Boolean} removeMask
64013      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64014      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64015      */
64016     removeMask : false,
64017     /**
64018      * @cfg {String} msg
64019      * The text to display in a centered loading message box (defaults to 'Loading...')
64020      */
64021     msg : 'Loading...',
64022     /**
64023      * @cfg {String} msgCls
64024      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64025      */
64026     msgCls : 'x-mask-loading',
64027
64028     /**
64029      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64030      * @type Boolean
64031      */
64032     disabled: false,
64033
64034     /**
64035      * Disables the mask to prevent it from being displayed
64036      */
64037     disable : function(){
64038        this.disabled = true;
64039     },
64040
64041     /**
64042      * Enables the mask so that it can be displayed
64043      */
64044     enable : function(){
64045         this.disabled = false;
64046     },
64047     
64048     onLoadException : function()
64049     {
64050         Roo.log(arguments);
64051         
64052         if (typeof(arguments[3]) != 'undefined') {
64053             Roo.MessageBox.alert("Error loading",arguments[3]);
64054         } 
64055         /*
64056         try {
64057             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64058                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64059             }   
64060         } catch(e) {
64061             
64062         }
64063         */
64064     
64065         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64066     },
64067     // private
64068     onLoad : function()
64069     {
64070         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64071     },
64072
64073     // private
64074     onBeforeLoad : function(){
64075         if(!this.disabled){
64076             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64077         }
64078     },
64079
64080     // private
64081     destroy : function(){
64082         if(this.store){
64083             this.store.un('beforeload', this.onBeforeLoad, this);
64084             this.store.un('load', this.onLoad, this);
64085             this.store.un('loadexception', this.onLoadException, this);
64086         }else{
64087             var um = this.el.getUpdateManager();
64088             um.un('beforeupdate', this.onBeforeLoad, this);
64089             um.un('update', this.onLoad, this);
64090             um.un('failure', this.onLoad, this);
64091         }
64092     }
64093 };/*
64094  * Based on:
64095  * Ext JS Library 1.1.1
64096  * Copyright(c) 2006-2007, Ext JS, LLC.
64097  *
64098  * Originally Released Under LGPL - original licence link has changed is not relivant.
64099  *
64100  * Fork - LGPL
64101  * <script type="text/javascript">
64102  */
64103
64104
64105 /**
64106  * @class Roo.XTemplate
64107  * @extends Roo.Template
64108  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64109 <pre><code>
64110 var t = new Roo.XTemplate(
64111         '&lt;select name="{name}"&gt;',
64112                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64113         '&lt;/select&gt;'
64114 );
64115  
64116 // then append, applying the master template values
64117  </code></pre>
64118  *
64119  * Supported features:
64120  *
64121  *  Tags:
64122
64123 <pre><code>
64124       {a_variable} - output encoded.
64125       {a_variable.format:("Y-m-d")} - call a method on the variable
64126       {a_variable:raw} - unencoded output
64127       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64128       {a_variable:this.method_on_template(...)} - call a method on the template object.
64129  
64130 </code></pre>
64131  *  The tpl tag:
64132 <pre><code>
64133         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64134         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64135         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64136         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64137   
64138         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64139         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64140 </code></pre>
64141  *      
64142  */
64143 Roo.XTemplate = function()
64144 {
64145     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64146     if (this.html) {
64147         this.compile();
64148     }
64149 };
64150
64151
64152 Roo.extend(Roo.XTemplate, Roo.Template, {
64153
64154     /**
64155      * The various sub templates
64156      */
64157     tpls : false,
64158     /**
64159      *
64160      * basic tag replacing syntax
64161      * WORD:WORD()
64162      *
64163      * // you can fake an object call by doing this
64164      *  x.t:(test,tesT) 
64165      * 
64166      */
64167     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64168
64169     /**
64170      * compile the template
64171      *
64172      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64173      *
64174      */
64175     compile: function()
64176     {
64177         var s = this.html;
64178      
64179         s = ['<tpl>', s, '</tpl>'].join('');
64180     
64181         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64182             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64183             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64184             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64185             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64186             m,
64187             id     = 0,
64188             tpls   = [];
64189     
64190         while(true == !!(m = s.match(re))){
64191             var forMatch   = m[0].match(nameRe),
64192                 ifMatch   = m[0].match(ifRe),
64193                 execMatch   = m[0].match(execRe),
64194                 namedMatch   = m[0].match(namedRe),
64195                 
64196                 exp  = null, 
64197                 fn   = null,
64198                 exec = null,
64199                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64200                 
64201             if (ifMatch) {
64202                 // if - puts fn into test..
64203                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64204                 if(exp){
64205                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64206                 }
64207             }
64208             
64209             if (execMatch) {
64210                 // exec - calls a function... returns empty if true is  returned.
64211                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64212                 if(exp){
64213                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64214                 }
64215             }
64216             
64217             
64218             if (name) {
64219                 // for = 
64220                 switch(name){
64221                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64222                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64223                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64224                 }
64225             }
64226             var uid = namedMatch ? namedMatch[1] : id;
64227             
64228             
64229             tpls.push({
64230                 id:     namedMatch ? namedMatch[1] : id,
64231                 target: name,
64232                 exec:   exec,
64233                 test:   fn,
64234                 body:   m[1] || ''
64235             });
64236             if (namedMatch) {
64237                 s = s.replace(m[0], '');
64238             } else { 
64239                 s = s.replace(m[0], '{xtpl'+ id + '}');
64240             }
64241             ++id;
64242         }
64243         this.tpls = [];
64244         for(var i = tpls.length-1; i >= 0; --i){
64245             this.compileTpl(tpls[i]);
64246             this.tpls[tpls[i].id] = tpls[i];
64247         }
64248         this.master = tpls[tpls.length-1];
64249         return this;
64250     },
64251     /**
64252      * same as applyTemplate, except it's done to one of the subTemplates
64253      * when using named templates, you can do:
64254      *
64255      * var str = pl.applySubTemplate('your-name', values);
64256      *
64257      * 
64258      * @param {Number} id of the template
64259      * @param {Object} values to apply to template
64260      * @param {Object} parent (normaly the instance of this object)
64261      */
64262     applySubTemplate : function(id, values, parent)
64263     {
64264         
64265         
64266         var t = this.tpls[id];
64267         
64268         
64269         try { 
64270             if(t.test && !t.test.call(this, values, parent)){
64271                 return '';
64272             }
64273         } catch(e) {
64274             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64275             Roo.log(e.toString());
64276             Roo.log(t.test);
64277             return ''
64278         }
64279         try { 
64280             
64281             if(t.exec && t.exec.call(this, values, parent)){
64282                 return '';
64283             }
64284         } catch(e) {
64285             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64286             Roo.log(e.toString());
64287             Roo.log(t.exec);
64288             return ''
64289         }
64290         try {
64291             var vs = t.target ? t.target.call(this, values, parent) : values;
64292             parent = t.target ? values : parent;
64293             if(t.target && vs instanceof Array){
64294                 var buf = [];
64295                 for(var i = 0, len = vs.length; i < len; i++){
64296                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64297                 }
64298                 return buf.join('');
64299             }
64300             return t.compiled.call(this, vs, parent);
64301         } catch (e) {
64302             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64303             Roo.log(e.toString());
64304             Roo.log(t.compiled);
64305             return '';
64306         }
64307     },
64308
64309     compileTpl : function(tpl)
64310     {
64311         var fm = Roo.util.Format;
64312         var useF = this.disableFormats !== true;
64313         var sep = Roo.isGecko ? "+" : ",";
64314         var undef = function(str) {
64315             Roo.log("Property not found :"  + str);
64316             return '';
64317         };
64318         
64319         var fn = function(m, name, format, args)
64320         {
64321             //Roo.log(arguments);
64322             args = args ? args.replace(/\\'/g,"'") : args;
64323             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64324             if (typeof(format) == 'undefined') {
64325                 format= 'htmlEncode';
64326             }
64327             if (format == 'raw' ) {
64328                 format = false;
64329             }
64330             
64331             if(name.substr(0, 4) == 'xtpl'){
64332                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64333             }
64334             
64335             // build an array of options to determine if value is undefined..
64336             
64337             // basically get 'xxxx.yyyy' then do
64338             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64339             //    (function () { Roo.log("Property not found"); return ''; })() :
64340             //    ......
64341             
64342             var udef_ar = [];
64343             var lookfor = '';
64344             Roo.each(name.split('.'), function(st) {
64345                 lookfor += (lookfor.length ? '.': '') + st;
64346                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64347             });
64348             
64349             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64350             
64351             
64352             if(format && useF){
64353                 
64354                 args = args ? ',' + args : "";
64355                  
64356                 if(format.substr(0, 5) != "this."){
64357                     format = "fm." + format + '(';
64358                 }else{
64359                     format = 'this.call("'+ format.substr(5) + '", ';
64360                     args = ", values";
64361                 }
64362                 
64363                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64364             }
64365              
64366             if (args.length) {
64367                 // called with xxyx.yuu:(test,test)
64368                 // change to ()
64369                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64370             }
64371             // raw.. - :raw modifier..
64372             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64373             
64374         };
64375         var body;
64376         // branched to use + in gecko and [].join() in others
64377         if(Roo.isGecko){
64378             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64379                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64380                     "';};};";
64381         }else{
64382             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64383             body.push(tpl.body.replace(/(\r\n|\n)/g,
64384                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64385             body.push("'].join('');};};");
64386             body = body.join('');
64387         }
64388         
64389         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64390        
64391         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64392         eval(body);
64393         
64394         return this;
64395     },
64396
64397     applyTemplate : function(values){
64398         return this.master.compiled.call(this, values, {});
64399         //var s = this.subs;
64400     },
64401
64402     apply : function(){
64403         return this.applyTemplate.apply(this, arguments);
64404     }
64405
64406  });
64407
64408 Roo.XTemplate.from = function(el){
64409     el = Roo.getDom(el);
64410     return new Roo.XTemplate(el.value || el.innerHTML);
64411 };